Index: frontend/node_modules/eslint-plugin-react/LICENSE
===================================================================
--- frontend/node_modules/eslint-plugin-react/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Yannick Croissant
+
+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/README.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,423 @@
+# `eslint-plugin-react` <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
+
+===================
+
+[![github actions][actions-image]][actions-url]
+[![Maintenance Status][status-image]][status-url]
+[![NPM version][npm-image]][npm-url]
+[![Tidelift][tidelift-image]][tidelift-url]
+
+React specific linting rules for `eslint`
+
+## Installation
+
+```sh
+npm install eslint eslint-plugin-react --save-dev
+```
+
+It is also possible to install ESLint globally rather than locally (using `npm install -g eslint`). However, this is not recommended, and any plugins or shareable configs that you use must be installed locally in either case.
+
+## Configuration (legacy: `.eslintrc*`) <a id="configuration"></a>
+
+Use [our preset](#recommended) to get reasonable defaults:
+
+```json
+  "extends": [
+    "eslint:recommended",
+    "plugin:react/recommended"
+  ]
+```
+
+If you are using the [new JSX transform from React 17](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#removing-unused-react-imports), extend [`react/jsx-runtime`](https://github.com/jsx-eslint/eslint-plugin-react/blob/c8917b0885094b5e4cc2a6f613f7fb6f16fe932e/index.js#L163-L176) in your eslint config (add `"plugin:react/jsx-runtime"` to `"extends"`) to disable the relevant rules.
+
+You should also specify settings that will be shared across all the plugin rules. ([More about eslint shared settings](https://eslint.org/docs/latest/use/configure/configuration-files#configuring-shared-settings))
+
+```json5
+{
+  "settings": {
+    "react": {
+      "createClass": "createReactClass", // Regex for Component Factory to use,
+                                         // default to "createReactClass"
+      "pragma": "React",  // Pragma to use, default to "React"
+      "fragment": "Fragment",  // Fragment to use (may be a property of <pragma>), default to "Fragment"
+      "version": "detect", // React version. "detect" automatically picks the version you have installed.
+                           // You can also use `16.0`, `16.3`, etc, if you want to override the detected value.
+                           // Defaults to the "defaultVersion" setting and warns if missing, and to "detect" in the future
+      "defaultVersion": "", // Default React version to use when the version you have installed cannot be detected.
+                            // If not provided, defaults to the latest React version.
+      "flowVersion": "0.53" // Flow version
+    },
+    "propWrapperFunctions": [
+        // The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped.
+        "forbidExtraProps",
+        {"property": "freeze", "object": "Object"},
+        {"property": "myFavoriteWrapper"},
+        // for rules that check exact prop wrappers
+        {"property": "forbidExtraProps", "exact": true}
+    ],
+    "componentWrapperFunctions": [
+        // The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped.
+        "observer", // `property`
+        {"property": "styled"}, // `object` is optional
+        {"property": "observer", "object": "Mobx"},
+        {"property": "observer", "object": "<pragma>"} // sets `object` to whatever value `settings.react.pragma` is set to
+    ],
+    "formComponents": [
+      // Components used as alternatives to <form> for forms, eg. <Form endpoint={ url } />
+      "CustomForm",
+      {"name": "SimpleForm", "formAttribute": "endpoint"},
+      {"name": "Form", "formAttribute": ["registerEndpoint", "loginEndpoint"]}, // allows specifying multiple properties if necessary
+    ],
+    "linkComponents": [
+      // Components used as alternatives to <a> for linking, eg. <Link to={ url } />
+      "Hyperlink",
+      {"name": "MyLink", "linkAttribute": "to"},
+      {"name": "Link", "linkAttribute": ["to", "href"]}, // allows specifying multiple properties if necessary
+    ]
+  }
+}
+```
+
+If you do not use a preset you will need to specify individual rules and add extra configuration.
+
+Add "react" to the plugins section.
+
+```json
+{
+  "plugins": [
+    "react"
+  ]
+}
+```
+
+Enable JSX support.
+
+With `eslint` 2+
+
+```json
+{
+  "parserOptions": {
+    "ecmaFeatures": {
+      "jsx": true
+    }
+  }
+}
+```
+
+Enable the rules that you would like to use.
+
+```json
+  "rules": {
+    "react/jsx-uses-react": "error",
+    "react/jsx-uses-vars": "error",
+  }
+```
+
+### Shareable configs
+
+#### Recommended
+
+This plugin exports a `recommended` configuration that enforces React good practices.
+
+To enable this configuration use the `extends` property in your `.eslintrc` config file:
+
+```json
+{
+  "extends": ["eslint:recommended", "plugin:react/recommended"]
+}
+```
+
+See [`eslint` documentation](https://eslint.org/docs/user-guide/configuring/configuration-files#extending-configuration-files) for more information about extending configuration files.
+
+#### All
+
+This plugin also exports an `all` configuration that includes every available rule.
+This pairs well with the `eslint:all` rule.
+
+```json
+{
+  "plugins": [
+    "react"
+  ],
+  "extends": ["eslint:all", "plugin:react/all"]
+}
+```
+
+**Note**: These configurations will import `eslint-plugin-react` and enable JSX in [parser options](https://eslint.org/docs/user-guide/configuring/language-options#specifying-parser-options).
+
+## Configuration (new: `eslint.config.js`)
+
+From [`v8.21.0`](https://github.com/eslint/eslint/releases/tag/v8.21.0), eslint announced a new config system.
+In the new system, `.eslintrc*` is no longer used. `eslint.config.js` would be the default config file name.
+In eslint `v8`, the legacy system (`.eslintrc*`) would still be supported, while in eslint `v9`, only the new system would be supported.
+
+And from [`v8.23.0`](https://github.com/eslint/eslint/releases/tag/v8.23.0), eslint CLI starts to look up `eslint.config.js`.
+**So, if your eslint is `>=8.23.0`, you're 100% ready to use the new config system.**
+
+You might want to check out the official blog posts,
+
+- <https://eslint.org/blog/2022/08/new-config-system-part-1/>
+- <https://eslint.org/blog/2022/08/new-config-system-part-2/>
+- <https://eslint.org/blog/2022/08/new-config-system-part-3/>
+
+and the [official docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new).
+
+### Plugin
+
+The default export of `eslint-plugin-react` is a plugin object.
+
+```js
+const react = require('eslint-plugin-react');
+const globals = require('globals');
+
+module.exports = [
+  …
+  {
+    files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
+    plugins: {
+      react,
+    },
+    languageOptions: {
+      parserOptions: {
+        ecmaFeatures: {
+          jsx: true,
+        },
+      },
+      globals: {
+        ...globals.browser,
+      },
+    },
+    rules: {
+      // ... any rules you want
+      'react/jsx-uses-react': 'error',
+      'react/jsx-uses-vars': 'error',
+     },
+    // ... others are omitted for brevity
+  },
+  …
+];
+```
+
+### Configuring shared settings
+
+Refer to the [official docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuring-shared-settings).
+
+The schema of the `settings.react` object would be identical to that of what's already described above in the legacy config section.
+
+<!-- markdownlint-disable-next-line no-duplicate-heading -->
+### Flat Configs
+
+This plugin exports 3 flat configs:
+
+- `flat.all`
+- `flat.recommended`
+- `flat['jsx-runtime']`
+
+The flat configs are available via the root plugin import. They will configure the plugin under the `react/` namespace and enable JSX in [`languageOptions.parserOptions`](https://eslint.org/docs/latest/use/configure/language-options#specifying-parser-options).
+
+```js
+const reactPlugin = require('eslint-plugin-react');
+
+module.exports = [
+  …
+  reactPlugin.configs.flat.recommended, // This is not a plugin object, but a shareable config object
+  reactPlugin.configs.flat['jsx-runtime'], // Add this if you are using React 17+
+  …
+];
+```
+
+You can of course add/override some properties.
+
+**Note**: Our shareable configs does not preconfigure `files` or [`languageOptions.globals`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects).
+For most of the cases, you probably want to configure some properties by yourself.
+
+```js
+const reactPlugin = require('eslint-plugin-react');
+const globals = require('globals');
+
+module.exports = [
+  …
+  {
+    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
+    ...reactPlugin.configs.flat.recommended,
+    languageOptions: {
+      ...reactPlugin.configs.flat.recommended.languageOptions,
+      globals: {
+        ...globals.serviceworker,
+        ...globals.browser,
+      },
+    },
+  },
+  …
+];
+```
+
+The above example is same as the example below, as the new config system is based on chaining.
+
+```js
+const reactPlugin = require('eslint-plugin-react');
+const globals = require('globals');
+
+module.exports = [
+  …
+  {
+    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
+    ...reactPlugin.configs.flat.recommended,
+  },
+  {
+    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
+    languageOptions: {
+      globals: {
+        ...globals.serviceworker,
+        ...globals.browser,
+      },
+    },
+  },
+  …
+];
+```
+
+## List of supported rules
+
+<!-- begin auto-generated rules list -->
+
+💼 [Configurations](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs) enabled in.\
+🚫 [Configurations](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs) disabled in.\
+🏃 Set in the `jsx-runtime` [configuration](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs).\
+☑️ Set in the `recommended` [configuration](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs).\
+🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
+💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).\
+❌ Deprecated.
+
+| Name                                                                                         | Description                                                                                                                                  | 💼 | 🚫 | 🔧 | 💡 | ❌  |
+| :------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | :- | :- | :- | :- | :- |
+| [boolean-prop-naming](docs/rules/boolean-prop-naming.md)                                     | Enforces consistent naming for boolean props                                                                                                 |    |    |    |    |    |
+| [button-has-type](docs/rules/button-has-type.md)                                             | Disallow usage of `button` elements without an explicit `type` attribute                                                                     |    |    |    |    |    |
+| [checked-requires-onchange-or-readonly](docs/rules/checked-requires-onchange-or-readonly.md) | Enforce using `onChange` or `readonly` attribute when `checked` is used                                                                      |    |    |    |    |    |
+| [default-props-match-prop-types](docs/rules/default-props-match-prop-types.md)               | Enforce all defaultProps have a corresponding non-required PropType                                                                          |    |    |    |    |    |
+| [destructuring-assignment](docs/rules/destructuring-assignment.md)                           | Enforce consistent usage of destructuring assignment of props, state, and context                                                            |    |    | 🔧 |    |    |
+| [display-name](docs/rules/display-name.md)                                                   | Disallow missing displayName in a React component definition                                                                                 | ☑️ |    |    |    |    |
+| [forbid-component-props](docs/rules/forbid-component-props.md)                               | Disallow certain props on components                                                                                                         |    |    |    |    |    |
+| [forbid-dom-props](docs/rules/forbid-dom-props.md)                                           | Disallow certain props on DOM Nodes                                                                                                          |    |    |    |    |    |
+| [forbid-elements](docs/rules/forbid-elements.md)                                             | Disallow certain elements                                                                                                                    |    |    |    |    |    |
+| [forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md)                         | Disallow using another component's propTypes                                                                                                 |    |    |    |    |    |
+| [forbid-prop-types](docs/rules/forbid-prop-types.md)                                         | Disallow certain propTypes                                                                                                                   |    |    |    |    |    |
+| [forward-ref-uses-ref](docs/rules/forward-ref-uses-ref.md)                                   | Require all forwardRef components include a ref parameter                                                                                    |    |    |    | 💡 |    |
+| [function-component-definition](docs/rules/function-component-definition.md)                 | Enforce a specific function type for function components                                                                                     |    |    | 🔧 |    |    |
+| [hook-use-state](docs/rules/hook-use-state.md)                                               | Ensure destructuring and symmetric naming of useState hook value and setter variables                                                        |    |    |    | 💡 |    |
+| [iframe-missing-sandbox](docs/rules/iframe-missing-sandbox.md)                               | Enforce sandbox attribute on iframe elements                                                                                                 |    |    |    |    |    |
+| [jsx-boolean-value](docs/rules/jsx-boolean-value.md)                                         | Enforce boolean attributes notation in JSX                                                                                                   |    |    | 🔧 |    |    |
+| [jsx-child-element-spacing](docs/rules/jsx-child-element-spacing.md)                         | Enforce or disallow spaces inside of curly braces in JSX attributes and expressions                                                          |    |    |    |    |    |
+| [jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md)                   | Enforce closing bracket location in JSX                                                                                                      |    |    | 🔧 |    |    |
+| [jsx-closing-tag-location](docs/rules/jsx-closing-tag-location.md)                           | Enforce closing tag location for multiline JSX                                                                                               |    |    | 🔧 |    |    |
+| [jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md)                           | Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes |    |    | 🔧 |    |    |
+| [jsx-curly-newline](docs/rules/jsx-curly-newline.md)                                         | Enforce consistent linebreaks in curly braces in JSX attributes and expressions                                                              |    |    | 🔧 |    |    |
+| [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md)                                         | Enforce or disallow spaces inside of curly braces in JSX attributes and expressions                                                          |    |    | 🔧 |    |    |
+| [jsx-equals-spacing](docs/rules/jsx-equals-spacing.md)                                       | Enforce or disallow spaces around equal signs in JSX attributes                                                                              |    |    | 🔧 |    |    |
+| [jsx-filename-extension](docs/rules/jsx-filename-extension.md)                               | Disallow file extensions that may contain JSX                                                                                                |    |    |    |    |    |
+| [jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md)                             | Enforce proper position of the first property in JSX                                                                                         |    |    | 🔧 |    |    |
+| [jsx-fragments](docs/rules/jsx-fragments.md)                                                 | Enforce shorthand or standard form for React fragments                                                                                       |    |    | 🔧 |    |    |
+| [jsx-handler-names](docs/rules/jsx-handler-names.md)                                         | Enforce event handler naming conventions in JSX                                                                                              |    |    |    |    |    |
+| [jsx-indent](docs/rules/jsx-indent.md)                                                       | Enforce JSX indentation                                                                                                                      |    |    | 🔧 |    |    |
+| [jsx-indent-props](docs/rules/jsx-indent-props.md)                                           | Enforce props indentation in JSX                                                                                                             |    |    | 🔧 |    |    |
+| [jsx-key](docs/rules/jsx-key.md)                                                             | Disallow missing `key` props in iterators/collection literals                                                                                | ☑️ |    |    |    |    |
+| [jsx-max-depth](docs/rules/jsx-max-depth.md)                                                 | Enforce JSX maximum depth                                                                                                                    |    |    |    |    |    |
+| [jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md)                               | Enforce maximum of props on a single line in JSX                                                                                             |    |    | 🔧 |    |    |
+| [jsx-newline](docs/rules/jsx-newline.md)                                                     | Require or prevent a new line after jsx elements and expressions.                                                                            |    |    | 🔧 |    |    |
+| [jsx-no-bind](docs/rules/jsx-no-bind.md)                                                     | Disallow `.bind()` or arrow functions in JSX props                                                                                           |    |    |    |    |    |
+| [jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md)                           | Disallow comments from being inserted as text nodes                                                                                          | ☑️ |    |    |    |    |
+| [jsx-no-constructed-context-values](docs/rules/jsx-no-constructed-context-values.md)         | Disallows JSX context provider values from taking values that will cause needless rerenders                                                  |    |    |    |    |    |
+| [jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md)                               | Disallow duplicate properties in JSX                                                                                                         | ☑️ |    |    |    |    |
+| [jsx-no-leaked-render](docs/rules/jsx-no-leaked-render.md)                                   | Disallow problematic leaked values from being rendered                                                                                       |    |    | 🔧 |    |    |
+| [jsx-no-literals](docs/rules/jsx-no-literals.md)                                             | Disallow usage of string literals in JSX                                                                                                     |    |    |    |    |    |
+| [jsx-no-script-url](docs/rules/jsx-no-script-url.md)                                         | Disallow usage of `javascript:` URLs                                                                                                         |    |    |    |    |    |
+| [jsx-no-target-blank](docs/rules/jsx-no-target-blank.md)                                     | Disallow `target="_blank"` attribute without `rel="noreferrer"`                                                                              | ☑️ |    | 🔧 |    |    |
+| [jsx-no-undef](docs/rules/jsx-no-undef.md)                                                   | Disallow undeclared variables in JSX                                                                                                         | ☑️ |    |    |    |    |
+| [jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md)                             | Disallow unnecessary fragments                                                                                                               |    |    | 🔧 |    |    |
+| [jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md)                     | Require one JSX element per line                                                                                                             |    |    | 🔧 |    |    |
+| [jsx-pascal-case](docs/rules/jsx-pascal-case.md)                                             | Enforce PascalCase for user-defined JSX components                                                                                           |    |    |    |    |    |
+| [jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md)                         | Disallow multiple spaces between inline JSX props                                                                                            |    |    | 🔧 |    |    |
+| [jsx-props-no-spread-multi](docs/rules/jsx-props-no-spread-multi.md)                         | Disallow JSX prop spreading the same identifier multiple times                                                                               |    |    |    |    |    |
+| [jsx-props-no-spreading](docs/rules/jsx-props-no-spreading.md)                               | Disallow JSX prop spreading                                                                                                                  |    |    |    |    |    |
+| [jsx-sort-default-props](docs/rules/jsx-sort-default-props.md)                               | Enforce defaultProps declarations alphabetical sorting                                                                                       |    |    |    |    | ❌  |
+| [jsx-sort-props](docs/rules/jsx-sort-props.md)                                               | Enforce props alphabetical sorting                                                                                                           |    |    | 🔧 |    |    |
+| [jsx-space-before-closing](docs/rules/jsx-space-before-closing.md)                           | Enforce spacing before closing bracket in JSX                                                                                                |    |    | 🔧 |    | ❌  |
+| [jsx-tag-spacing](docs/rules/jsx-tag-spacing.md)                                             | Enforce whitespace in and around the JSX opening and closing brackets                                                                        |    |    | 🔧 |    |    |
+| [jsx-uses-react](docs/rules/jsx-uses-react.md)                                               | Disallow React to be incorrectly marked as unused                                                                                            | ☑️ | 🏃 |    |    |    |
+| [jsx-uses-vars](docs/rules/jsx-uses-vars.md)                                                 | Disallow variables used in JSX to be incorrectly marked as unused                                                                            | ☑️ |    |    |    |    |
+| [jsx-wrap-multilines](docs/rules/jsx-wrap-multilines.md)                                     | Disallow missing parentheses around multiline JSX                                                                                            |    |    | 🔧 |    |    |
+| [no-access-state-in-setstate](docs/rules/no-access-state-in-setstate.md)                     | Disallow when this.state is accessed within setState                                                                                         |    |    |    |    |    |
+| [no-adjacent-inline-elements](docs/rules/no-adjacent-inline-elements.md)                     | Disallow adjacent inline elements not separated by whitespace.                                                                               |    |    |    |    |    |
+| [no-array-index-key](docs/rules/no-array-index-key.md)                                       | Disallow usage of Array index in keys                                                                                                        |    |    |    |    |    |
+| [no-arrow-function-lifecycle](docs/rules/no-arrow-function-lifecycle.md)                     | Lifecycle methods should be methods on the prototype, not class fields                                                                       |    |    | 🔧 |    |    |
+| [no-children-prop](docs/rules/no-children-prop.md)                                           | Disallow passing of children as props                                                                                                        | ☑️ |    |    |    |    |
+| [no-danger](docs/rules/no-danger.md)                                                         | Disallow usage of dangerous JSX properties                                                                                                   |    |    |    |    |    |
+| [no-danger-with-children](docs/rules/no-danger-with-children.md)                             | Disallow when a DOM element is using both children and dangerouslySetInnerHTML                                                               | ☑️ |    |    |    |    |
+| [no-deprecated](docs/rules/no-deprecated.md)                                                 | Disallow usage of deprecated methods                                                                                                         | ☑️ |    |    |    |    |
+| [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md)                               | Disallow usage of setState in componentDidMount                                                                                              |    |    |    |    |    |
+| [no-did-update-set-state](docs/rules/no-did-update-set-state.md)                             | Disallow usage of setState in componentDidUpdate                                                                                             |    |    |    |    |    |
+| [no-direct-mutation-state](docs/rules/no-direct-mutation-state.md)                           | Disallow direct mutation of this.state                                                                                                       | ☑️ |    |    |    |    |
+| [no-find-dom-node](docs/rules/no-find-dom-node.md)                                           | Disallow usage of findDOMNode                                                                                                                | ☑️ |    |    |    |    |
+| [no-invalid-html-attribute](docs/rules/no-invalid-html-attribute.md)                         | Disallow usage of invalid attributes                                                                                                         |    |    |    | 💡 |    |
+| [no-is-mounted](docs/rules/no-is-mounted.md)                                                 | Disallow usage of isMounted                                                                                                                  | ☑️ |    |    |    |    |
+| [no-multi-comp](docs/rules/no-multi-comp.md)                                                 | Disallow multiple component definition per file                                                                                              |    |    |    |    |    |
+| [no-namespace](docs/rules/no-namespace.md)                                                   | Enforce that namespaces are not used in React elements                                                                                       |    |    |    |    |    |
+| [no-object-type-as-default-prop](docs/rules/no-object-type-as-default-prop.md)               | Disallow usage of referential-type variables as default param in functional component                                                        |    |    |    |    |    |
+| [no-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md)   | Disallow usage of shouldComponentUpdate when extending React.PureComponent                                                                   |    |    |    |    |    |
+| [no-render-return-value](docs/rules/no-render-return-value.md)                               | Disallow usage of the return value of ReactDOM.render                                                                                        | ☑️ |    |    |    |    |
+| [no-set-state](docs/rules/no-set-state.md)                                                   | Disallow usage of setState                                                                                                                   |    |    |    |    |    |
+| [no-string-refs](docs/rules/no-string-refs.md)                                               | Disallow using string references                                                                                                             | ☑️ |    |    |    |    |
+| [no-this-in-sfc](docs/rules/no-this-in-sfc.md)                                               | Disallow `this` from being used in stateless functional components                                                                           |    |    |    |    |    |
+| [no-typos](docs/rules/no-typos.md)                                                           | Disallow common typos                                                                                                                        |    |    |    |    |    |
+| [no-unescaped-entities](docs/rules/no-unescaped-entities.md)                                 | Disallow unescaped HTML entities from appearing in markup                                                                                    | ☑️ |    |    | 💡 |    |
+| [no-unknown-property](docs/rules/no-unknown-property.md)                                     | Disallow usage of unknown DOM property                                                                                                       | ☑️ |    | 🔧 |    |    |
+| [no-unsafe](docs/rules/no-unsafe.md)                                                         | Disallow usage of unsafe lifecycle methods                                                                                                   |    | ☑️ |    |    |    |
+| [no-unstable-nested-components](docs/rules/no-unstable-nested-components.md)                 | Disallow creating unstable components inside components                                                                                      |    |    |    |    |    |
+| [no-unused-class-component-methods](docs/rules/no-unused-class-component-methods.md)         | Disallow declaring unused methods of component class                                                                                         |    |    |    |    |    |
+| [no-unused-prop-types](docs/rules/no-unused-prop-types.md)                                   | Disallow definitions of unused propTypes                                                                                                     |    |    |    |    |    |
+| [no-unused-state](docs/rules/no-unused-state.md)                                             | Disallow definitions of unused state                                                                                                         |    |    |    |    |    |
+| [no-will-update-set-state](docs/rules/no-will-update-set-state.md)                           | Disallow usage of setState in componentWillUpdate                                                                                            |    |    |    |    |    |
+| [prefer-es6-class](docs/rules/prefer-es6-class.md)                                           | Enforce ES5 or ES6 class for React Components                                                                                                |    |    |    |    |    |
+| [prefer-exact-props](docs/rules/prefer-exact-props.md)                                       | Prefer exact proptype definitions                                                                                                            |    |    |    |    |    |
+| [prefer-read-only-props](docs/rules/prefer-read-only-props.md)                               | Enforce that props are read-only                                                                                                             |    |    | 🔧 |    |    |
+| [prefer-stateless-function](docs/rules/prefer-stateless-function.md)                         | Enforce stateless components to be written as a pure function                                                                                |    |    |    |    |    |
+| [prop-types](docs/rules/prop-types.md)                                                       | Disallow missing props validation in a React component definition                                                                            | ☑️ |    |    |    |    |
+| [react-in-jsx-scope](docs/rules/react-in-jsx-scope.md)                                       | Disallow missing React when using JSX                                                                                                        | ☑️ | 🏃 |    |    |    |
+| [require-default-props](docs/rules/require-default-props.md)                                 | Enforce a defaultProps definition for every prop that is not a required prop                                                                 |    |    |    |    |    |
+| [require-optimization](docs/rules/require-optimization.md)                                   | Enforce React components to have a shouldComponentUpdate method                                                                              |    |    |    |    |    |
+| [require-render-return](docs/rules/require-render-return.md)                                 | Enforce ES5 or ES6 class for returning value in render function                                                                              | ☑️ |    |    |    |    |
+| [self-closing-comp](docs/rules/self-closing-comp.md)                                         | Disallow extra closing tags for components without children                                                                                  |    |    | 🔧 |    |    |
+| [sort-comp](docs/rules/sort-comp.md)                                                         | Enforce component methods order                                                                                                              |    |    |    |    |    |
+| [sort-default-props](docs/rules/sort-default-props.md)                                       | Enforce defaultProps declarations alphabetical sorting                                                                                       |    |    |    |    |    |
+| [sort-prop-types](docs/rules/sort-prop-types.md)                                             | Enforce propTypes declarations alphabetical sorting                                                                                          |    |    | 🔧 |    |    |
+| [state-in-constructor](docs/rules/state-in-constructor.md)                                   | Enforce class component state initialization style                                                                                           |    |    |    |    |    |
+| [static-property-placement](docs/rules/static-property-placement.md)                         | Enforces where React component static properties should be positioned.                                                                       |    |    |    |    |    |
+| [style-prop-object](docs/rules/style-prop-object.md)                                         | Enforce style prop value is an object                                                                                                        |    |    |    |    |    |
+| [void-dom-elements-no-children](docs/rules/void-dom-elements-no-children.md)                 | Disallow void DOM elements (e.g. `<img />`, `<br />`) from receiving children                                                                |    |    |    |    |    |
+
+<!-- end auto-generated rules list -->
+
+## Other useful plugins
+
+- Rules of Hooks: [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks)
+- JSX accessibility: [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)
+- React Native: [eslint-plugin-react-native](https://github.com/Intellicode/eslint-plugin-react-native)
+
+## License
+
+`eslint-plugin-react` is licensed under the [MIT License](https://opensource.org/licenses/mit-license.php).
+
+[npm-url]: https://npmjs.org/package/eslint-plugin-react
+[npm-image]: https://img.shields.io/npm/v/eslint-plugin-react.svg
+
+[status-url]: https://github.com/jsx-eslint/eslint-plugin-react/pulse
+[status-image]: https://img.shields.io/github/last-commit/jsx-eslint/eslint-plugin-react.svg
+
+[tidelift-url]: https://tidelift.com/subscription/pkg/npm-eslint-plugin-react?utm_source=npm-eslint-plugin-react&utm_medium=referral&utm_campaign=readme
+[tidelift-image]: https://tidelift.com/badges/package/npm/eslint-plugin-react?style=flat
+
+[package-url]: https://npmjs.org/package/eslint-plugin-react
+[npm-version-svg]: https://versionbadg.es/jsx-eslint/eslint-plugin-react.svg
+
+[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/jsx-eslint/eslint-plugin-react
+[actions-url]: https://github.com/jsx-eslint/eslint-plugin-react/actions
Index: frontend/node_modules/eslint-plugin-react/configs/all.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/configs/all.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/configs/all.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+'use strict';
+
+const plugin = require('..');
+
+const legacyConfig = plugin.configs.all;
+
+module.exports = {
+  plugins: { react: plugin },
+  rules: legacyConfig.rules,
+  languageOptions: { parserOptions: legacyConfig.parserOptions },
+};
+
+Object.defineProperty(module.exports, 'languageOptions', { enumerable: false });
Index: frontend/node_modules/eslint-plugin-react/configs/jsx-runtime.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/configs/jsx-runtime.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/configs/jsx-runtime.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+'use strict';
+
+const plugin = require('..');
+
+const legacyConfig = plugin.configs['jsx-runtime'];
+
+module.exports = {
+  plugins: { react: plugin },
+  rules: legacyConfig.rules,
+  languageOptions: { parserOptions: legacyConfig.parserOptions },
+};
+
+Object.defineProperty(module.exports, 'languageOptions', { enumerable: false });
Index: frontend/node_modules/eslint-plugin-react/configs/recommended.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/configs/recommended.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/configs/recommended.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+'use strict';
+
+const plugin = require('..');
+
+const legacyConfig = plugin.configs.recommended;
+
+module.exports = {
+  plugins: { react: plugin },
+  rules: legacyConfig.rules,
+  languageOptions: { parserOptions: legacyConfig.parserOptions },
+};
+
+Object.defineProperty(module.exports, 'languageOptions', { enumerable: false });
Index: frontend/node_modules/eslint-plugin-react/index.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,187 @@
+export = plugin;
+/** @typedef {{ plugins: { react: typeof plugin }, rules: import('eslint').Linter.RulesRecord, languageOptions: { parserOptions: import('eslint').Linter.ParserOptions } }} ReactFlatConfig */
+/** @type {{ deprecatedRules: typeof deprecatedRules, rules: typeof allRules, configs: typeof configs & { flat: Record<string, ReactFlatConfig> }}} */
+declare const plugin: {
+    deprecatedRules: typeof deprecatedRules;
+    rules: typeof allRules;
+    configs: typeof configs & {
+        flat: Record<string, ReactFlatConfig>;
+    };
+};
+declare namespace plugin {
+    export { ReactFlatConfig };
+}
+/** @type {Partial<typeof allRules>} */
+declare const deprecatedRules: Partial<typeof allRules>;
+declare const allRules: {
+    'boolean-prop-naming': import("eslint").Rule.RuleModule;
+    'button-has-type': import("eslint").Rule.RuleModule;
+    'checked-requires-onchange-or-readonly': import("eslint").Rule.RuleModule;
+    'default-props-match-prop-types': import("eslint").Rule.RuleModule;
+    'destructuring-assignment': import("eslint").Rule.RuleModule;
+    'display-name': import("eslint").Rule.RuleModule;
+    'forbid-component-props': import("eslint").Rule.RuleModule;
+    'forbid-dom-props': import("eslint").Rule.RuleModule;
+    'forbid-elements': import("eslint").Rule.RuleModule;
+    'forbid-foreign-prop-types': import("eslint").Rule.RuleModule;
+    'forbid-prop-types': import("eslint").Rule.RuleModule;
+    'forward-ref-uses-ref': import("eslint").Rule.RuleModule;
+    'function-component-definition': import("eslint").Rule.RuleModule;
+    'hook-use-state': import("eslint").Rule.RuleModule;
+    'iframe-missing-sandbox': import("eslint").Rule.RuleModule;
+    'jsx-boolean-value': import("eslint").Rule.RuleModule;
+    'jsx-child-element-spacing': import("eslint").Rule.RuleModule;
+    'jsx-closing-bracket-location': import("eslint").Rule.RuleModule;
+    'jsx-closing-tag-location': import("eslint").Rule.RuleModule;
+    'jsx-curly-spacing': import("eslint").Rule.RuleModule;
+    'jsx-curly-newline': import("eslint").Rule.RuleModule;
+    'jsx-equals-spacing': import("eslint").Rule.RuleModule;
+    'jsx-filename-extension': import("eslint").Rule.RuleModule;
+    'jsx-first-prop-new-line': import("eslint").Rule.RuleModule;
+    'jsx-handler-names': import("eslint").Rule.RuleModule;
+    'jsx-indent': import("eslint").Rule.RuleModule;
+    'jsx-indent-props': import("eslint").Rule.RuleModule;
+    'jsx-key': import("eslint").Rule.RuleModule;
+    'jsx-max-depth': import("eslint").Rule.RuleModule;
+    'jsx-max-props-per-line': import("eslint").Rule.RuleModule;
+    'jsx-newline': import("eslint").Rule.RuleModule;
+    'jsx-no-bind': import("eslint").Rule.RuleModule;
+    'jsx-no-comment-textnodes': import("eslint").Rule.RuleModule;
+    'jsx-no-constructed-context-values': import("eslint").Rule.RuleModule;
+    'jsx-no-duplicate-props': import("eslint").Rule.RuleModule;
+    'jsx-no-leaked-render': import("eslint").Rule.RuleModule;
+    'jsx-no-literals': import("eslint").Rule.RuleModule;
+    'jsx-no-script-url': import("eslint").Rule.RuleModule;
+    'jsx-no-target-blank': import("eslint").Rule.RuleModule;
+    'jsx-no-useless-fragment': import("eslint").Rule.RuleModule;
+    'jsx-one-expression-per-line': import("eslint").Rule.RuleModule;
+    'jsx-no-undef': import("eslint").Rule.RuleModule;
+    'jsx-curly-brace-presence': import("eslint").Rule.RuleModule;
+    'jsx-pascal-case': import("eslint").Rule.RuleModule;
+    'jsx-fragments': import("eslint").Rule.RuleModule;
+    'jsx-props-no-multi-spaces': import("eslint").Rule.RuleModule;
+    'jsx-props-no-spreading': import("eslint").Rule.RuleModule;
+    'jsx-props-no-spread-multi': import("eslint").Rule.RuleModule;
+    'jsx-sort-default-props': import("eslint").Rule.RuleModule;
+    'jsx-sort-props': import("eslint").Rule.RuleModule;
+    'jsx-space-before-closing': import("eslint").Rule.RuleModule;
+    'jsx-tag-spacing': import("eslint").Rule.RuleModule;
+    'jsx-uses-react': import("eslint").Rule.RuleModule;
+    'jsx-uses-vars': import("eslint").Rule.RuleModule;
+    'jsx-wrap-multilines': import("eslint").Rule.RuleModule;
+    'no-invalid-html-attribute': import("eslint").Rule.RuleModule;
+    'no-access-state-in-setstate': import("eslint").Rule.RuleModule;
+    'no-adjacent-inline-elements': import("eslint").Rule.RuleModule;
+    'no-array-index-key': import("eslint").Rule.RuleModule;
+    'no-arrow-function-lifecycle': import("eslint").Rule.RuleModule;
+    'no-children-prop': import("eslint").Rule.RuleModule;
+    'no-danger': import("eslint").Rule.RuleModule;
+    'no-danger-with-children': import("eslint").Rule.RuleModule;
+    'no-deprecated': import("eslint").Rule.RuleModule;
+    'no-did-mount-set-state': import("eslint").Rule.RuleModule;
+    'no-did-update-set-state': import("eslint").Rule.RuleModule;
+    'no-direct-mutation-state': import("eslint").Rule.RuleModule;
+    'no-find-dom-node': import("eslint").Rule.RuleModule;
+    'no-is-mounted': import("eslint").Rule.RuleModule;
+    'no-multi-comp': import("eslint").Rule.RuleModule;
+    'no-namespace': import("eslint").Rule.RuleModule;
+    'no-set-state': import("eslint").Rule.RuleModule;
+    'no-string-refs': import("eslint").Rule.RuleModule;
+    'no-redundant-should-component-update': import("eslint").Rule.RuleModule;
+    'no-render-return-value': import("eslint").Rule.RuleModule;
+    'no-this-in-sfc': import("eslint").Rule.RuleModule;
+    'no-typos': import("eslint").Rule.RuleModule;
+    'no-unescaped-entities': import("eslint").Rule.RuleModule;
+    'no-unknown-property': import("eslint").Rule.RuleModule;
+    'no-unsafe': import("eslint").Rule.RuleModule;
+    'no-unstable-nested-components': import("eslint").Rule.RuleModule;
+    'no-unused-class-component-methods': import("eslint").Rule.RuleModule;
+    'no-unused-prop-types': import("eslint").Rule.RuleModule;
+    'no-unused-state': import("eslint").Rule.RuleModule;
+    'no-object-type-as-default-prop': import("eslint").Rule.RuleModule;
+    'no-will-update-set-state': import("eslint").Rule.RuleModule;
+    'prefer-es6-class': import("eslint").Rule.RuleModule;
+    'prefer-exact-props': import("eslint").Rule.RuleModule;
+    'prefer-read-only-props': import("eslint").Rule.RuleModule;
+    'prefer-stateless-function': import("eslint").Rule.RuleModule;
+    'prop-types': import("eslint").Rule.RuleModule;
+    'react-in-jsx-scope': import("eslint").Rule.RuleModule;
+    'require-default-props': import("eslint").Rule.RuleModule;
+    'require-optimization': import("eslint").Rule.RuleModule;
+    'require-render-return': import("eslint").Rule.RuleModule;
+    'self-closing-comp': import("eslint").Rule.RuleModule;
+    'sort-comp': import("eslint").Rule.RuleModule;
+    'sort-default-props': import("eslint").Rule.RuleModule;
+    'sort-prop-types': import("eslint").Rule.RuleModule;
+    'state-in-constructor': import("eslint").Rule.RuleModule;
+    'static-property-placement': import("eslint").Rule.RuleModule;
+    'style-prop-object': import("eslint").Rule.RuleModule;
+    'void-dom-elements-no-children': import("eslint").Rule.RuleModule;
+};
+declare const configs: {
+    recommended: {
+        plugins: ["react"];
+        parserOptions: {
+            ecmaFeatures: {
+                jsx: boolean;
+            };
+        };
+        rules: {
+            'react/display-name': 2;
+            'react/jsx-key': 2;
+            'react/jsx-no-comment-textnodes': 2;
+            'react/jsx-no-duplicate-props': 2;
+            'react/jsx-no-target-blank': 2;
+            'react/jsx-no-undef': 2;
+            'react/jsx-uses-react': 2;
+            'react/jsx-uses-vars': 2;
+            'react/no-children-prop': 2;
+            'react/no-danger-with-children': 2;
+            'react/no-deprecated': 2;
+            'react/no-direct-mutation-state': 2;
+            'react/no-find-dom-node': 2;
+            'react/no-is-mounted': 2;
+            'react/no-render-return-value': 2;
+            'react/no-string-refs': 2;
+            'react/no-unescaped-entities': 2;
+            'react/no-unknown-property': 2;
+            'react/no-unsafe': 0;
+            'react/prop-types': 2;
+            'react/react-in-jsx-scope': 2;
+            'react/require-render-return': 2;
+        };
+    };
+    all: {
+        plugins: ["react"];
+        parserOptions: {
+            ecmaFeatures: {
+                jsx: boolean;
+            };
+        };
+        rules: Record<"boolean-prop-naming" | "button-has-type" | "checked-requires-onchange-or-readonly" | "default-props-match-prop-types" | "destructuring-assignment" | "display-name" | "forbid-component-props" | "forbid-dom-props" | "forbid-elements" | "forbid-foreign-prop-types" | "forbid-prop-types" | "prop-types" | "forward-ref-uses-ref" | "function-component-definition" | "hook-use-state" | "iframe-missing-sandbox" | "jsx-boolean-value" | "jsx-child-element-spacing" | "jsx-closing-bracket-location" | "jsx-closing-tag-location" | "jsx-curly-spacing" | "jsx-curly-newline" | "jsx-equals-spacing" | "jsx-filename-extension" | "jsx-first-prop-new-line" | "jsx-handler-names" | "jsx-indent" | "jsx-indent-props" | "jsx-key" | "jsx-max-depth" | "jsx-max-props-per-line" | "jsx-newline" | "jsx-no-bind" | "jsx-no-comment-textnodes" | "jsx-no-constructed-context-values" | "jsx-no-duplicate-props" | "jsx-no-leaked-render" | "jsx-no-literals" | "jsx-no-script-url" | "jsx-no-target-blank" | "jsx-no-useless-fragment" | "jsx-one-expression-per-line" | "jsx-no-undef" | "jsx-curly-brace-presence" | "jsx-pascal-case" | "jsx-fragments" | "jsx-props-no-multi-spaces" | "jsx-props-no-spreading" | "jsx-props-no-spread-multi" | "sort-default-props" | "jsx-sort-default-props" | "jsx-sort-props" | "jsx-tag-spacing" | "jsx-space-before-closing" | "jsx-uses-react" | "jsx-uses-vars" | "jsx-wrap-multilines" | "no-invalid-html-attribute" | "no-access-state-in-setstate" | "no-adjacent-inline-elements" | "no-array-index-key" | "no-arrow-function-lifecycle" | "no-children-prop" | "no-danger" | "no-danger-with-children" | "no-deprecated" | "no-direct-mutation-state" | "no-find-dom-node" | "no-is-mounted" | "no-multi-comp" | "no-namespace" | "no-set-state" | "no-string-refs" | "no-redundant-should-component-update" | "no-render-return-value" | "no-this-in-sfc" | "no-typos" | "no-unescaped-entities" | "no-unknown-property" | "no-unsafe" | "no-unstable-nested-components" | "no-unused-class-component-methods" | "no-unused-prop-types" | "no-unused-state" | "no-object-type-as-default-prop" | "prefer-es6-class" | "prefer-exact-props" | "prefer-read-only-props" | "prefer-stateless-function" | "react-in-jsx-scope" | "require-default-props" | "require-optimization" | "require-render-return" | "self-closing-comp" | "sort-comp" | "sort-prop-types" | "state-in-constructor" | "static-property-placement" | "style-prop-object" | "void-dom-elements-no-children" | "no-did-mount-set-state" | "no-did-update-set-state" | "no-will-update-set-state", 2 | "error">;
+    };
+    'jsx-runtime': {
+        plugins: ["react"];
+        parserOptions: {
+            ecmaFeatures: {
+                jsx: boolean;
+            };
+            jsxPragma: any;
+        };
+        rules: {
+            'react/react-in-jsx-scope': 0;
+            'react/jsx-uses-react': 0;
+        };
+    };
+    flat: Record<string, ReactFlatConfig>;
+};
+type ReactFlatConfig = {
+    plugins: {
+        react: typeof plugin;
+    };
+    rules: import('eslint').Linter.RulesRecord;
+    languageOptions: {
+        parserOptions: import('eslint').Linter.ParserOptions;
+    };
+};
+//# sourceMappingURL=index.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/index.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";AAiGA,8LAA8L;AAE9L,sJAAsJ;AACtJ,sBADW;IAAE,eAAe,EAAE,OAAO,eAAe,CAAC;IAAC,KAAK,EAAE,OAAO,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,OAAO,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;KAAE,CAAA;CAAC,CAKhJ;;;;AAhFF,uCAAuC;AACvC,+BADW,OAAO,CAAC,OAAO,QAAQ,CAAC,CAC2C;AApB9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAwC;AAgCxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAuDmB,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC;EAGhD;;aAEuB;QAAE,KAAK,EAAE,OAAO,MAAM,CAAA;KAAE;WAAS,OAAO,QAAQ,EAAE,MAAM,CAAC,WAAW;qBAAmB;QAAE,aAAa,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,aAAa,CAAA;KAAE"}
Index: frontend/node_modules/eslint-plugin-react/index.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,125 @@
+'use strict';
+
+const fromEntries = require('object.fromentries');
+const entries = require('object.entries');
+
+const allRules = require('./lib/rules');
+
+function filterRules(rules, predicate) {
+  return fromEntries(entries(rules).filter((entry) => predicate(entry[1])));
+}
+
+/**
+ * @param {object} rules - rules object mapping rule name to rule module
+ * @returns {Record<string, SEVERITY_ERROR | 'error'>}
+ */
+function configureAsError(rules) {
+  return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2]));
+}
+
+/** @type {Partial<typeof allRules>} */
+const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated);
+/** @type {Record<keyof typeof activeRules, 2 | 'error'>} */
+const activeRulesConfig = configureAsError(activeRules);
+
+/** @type {Partial<typeof allRules>} */
+const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated);
+
+/** @type {['react']} */
+// for legacy config system
+const plugins = [
+  'react',
+];
+
+// TODO: with TS 4.5+, inline this
+const SEVERITY_ERROR = /** @type {2} */ (2);
+const SEVERITY_OFF = /** @type {0} */ (0);
+
+const configs = {
+  recommended: {
+    plugins,
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
+      },
+    },
+    rules: {
+      'react/display-name': SEVERITY_ERROR,
+      'react/jsx-key': SEVERITY_ERROR,
+      'react/jsx-no-comment-textnodes': SEVERITY_ERROR,
+      'react/jsx-no-duplicate-props': SEVERITY_ERROR,
+      'react/jsx-no-target-blank': SEVERITY_ERROR,
+      'react/jsx-no-undef': SEVERITY_ERROR,
+      'react/jsx-uses-react': SEVERITY_ERROR,
+      'react/jsx-uses-vars': SEVERITY_ERROR,
+      'react/no-children-prop': SEVERITY_ERROR,
+      'react/no-danger-with-children': SEVERITY_ERROR,
+      'react/no-deprecated': SEVERITY_ERROR,
+      'react/no-direct-mutation-state': SEVERITY_ERROR,
+      'react/no-find-dom-node': SEVERITY_ERROR,
+      'react/no-is-mounted': SEVERITY_ERROR,
+      'react/no-render-return-value': SEVERITY_ERROR,
+      'react/no-string-refs': SEVERITY_ERROR,
+      'react/no-unescaped-entities': SEVERITY_ERROR,
+      'react/no-unknown-property': SEVERITY_ERROR,
+      'react/no-unsafe': SEVERITY_OFF,
+      'react/prop-types': SEVERITY_ERROR,
+      'react/react-in-jsx-scope': SEVERITY_ERROR,
+      'react/require-render-return': SEVERITY_ERROR,
+    },
+  },
+  all: {
+    plugins,
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
+      },
+    },
+    rules: activeRulesConfig,
+  },
+  'jsx-runtime': {
+    plugins,
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
+      },
+      jsxPragma: null, // for @typescript/eslint-parser
+    },
+    rules: {
+      'react/react-in-jsx-scope': SEVERITY_OFF,
+      'react/jsx-uses-react': SEVERITY_OFF,
+    },
+  },
+  flat: /** @type {Record<string, ReactFlatConfig>} */ ({
+    __proto__: null,
+  }),
+};
+
+/** @typedef {{ plugins: { react: typeof plugin }, rules: import('eslint').Linter.RulesRecord, languageOptions: { parserOptions: import('eslint').Linter.ParserOptions } }} ReactFlatConfig */
+
+/** @type {{ deprecatedRules: typeof deprecatedRules, rules: typeof allRules, configs: typeof configs & { flat: Record<string, ReactFlatConfig> }}} */
+const plugin = {
+  deprecatedRules,
+  rules: allRules,
+  configs,
+};
+
+Object.assign(configs.flat, {
+  recommended: {
+    plugins: { react: plugin },
+    rules: configs.recommended.rules,
+    languageOptions: { parserOptions: configs.recommended.parserOptions },
+  },
+  all: {
+    plugins: { react: plugin },
+    rules: configs.all.rules,
+    languageOptions: { parserOptions: configs.all.parserOptions },
+  },
+  'jsx-runtime': {
+    plugins: { react: plugin },
+    rules: configs['jsx-runtime'].rules,
+    languageOptions: { parserOptions: configs['jsx-runtime'].parserOptions },
+  },
+});
+
+module.exports = plugin;
Index: frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=boolean-prop-naming.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"boolean-prop-naming.d.ts","sourceRoot":"","sources":["boolean-prop-naming.js"],"names":[],"mappings":"wBAyCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/boolean-prop-naming.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,426 @@
+/**
+ * @fileoverview Enforces consistent naming for boolean props
+ * @author Ev Haus
+ */
+
+'use strict';
+
+const flatMap = require('array.prototype.flatmap');
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const propsUtil = require('../util/props');
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const propWrapperUtil = require('../util/propWrapper');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+/**
+ * Checks if prop is nested
+ * @param {Object} prop Property object, single prop type declaration
+ * @returns {boolean}
+ */
+function nestedPropTypes(prop) {
+  return (
+    prop.type === 'Property'
+    && astUtil.isCallExpression(prop.value)
+  );
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  patternMismatch: 'Prop name `{{propName}}` doesn’t match rule `{{pattern}}`',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      category: 'Stylistic Issues',
+      description: 'Enforces consistent naming for boolean props',
+      recommended: false,
+      url: docsUrl('boolean-prop-naming'),
+    },
+
+    messages,
+
+    schema: [{
+      additionalProperties: false,
+      properties: {
+        propTypeNames: {
+          items: {
+            type: 'string',
+          },
+          minItems: 1,
+          type: 'array',
+          uniqueItems: true,
+        },
+        rule: {
+          default: '^(is|has)[A-Z]([A-Za-z0-9]?)+',
+          minLength: 1,
+          type: 'string',
+        },
+        message: {
+          minLength: 1,
+          type: 'string',
+        },
+        validateNested: {
+          default: false,
+          type: 'boolean',
+        },
+      },
+      type: 'object',
+    }],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const config = context.options[0] || {};
+    const rule = config.rule ? new RegExp(config.rule) : null;
+    const propTypeNames = config.propTypeNames || ['bool'];
+
+    // Remembers all Flowtype object definitions
+    const objectTypeAnnotations = new Map();
+
+    /**
+     * Returns the prop key to ensure we handle the following cases:
+     * propTypes: {
+     *   full: React.PropTypes.bool,
+     *   short: PropTypes.bool,
+     *   direct: bool,
+     *   required: PropTypes.bool.isRequired
+     * }
+     * @param {Object} node The node we're getting the name of
+     * @returns {string | null}
+     */
+    function getPropKey(node) {
+      // Check for `ExperimentalSpreadProperty` (eslint 3/4) and `SpreadElement` (eslint 5)
+      // so we can skip validation of those fields.
+      // Otherwise it will look for `node.value.property` which doesn't exist and breaks eslint.
+      if (node.type === 'ExperimentalSpreadProperty' || node.type === 'SpreadElement') {
+        return null;
+      }
+      if (node.value && node.value.property) {
+        const name = node.value.property.name;
+        if (name === 'isRequired') {
+          if (node.value.object && node.value.object.property) {
+            return node.value.object.property.name;
+          }
+          return null;
+        }
+        return name;
+      }
+      if (node.value && node.value.type === 'Identifier') {
+        return node.value.name;
+      }
+      return null;
+    }
+
+    /**
+     * Returns the name of the given node (prop)
+     * @param {Object} node The node we're getting the name of
+     * @returns {string}
+     */
+    function getPropName(node) {
+      // Due to this bug https://github.com/babel/babel-eslint/issues/307
+      // we can't get the name of the Flow object key name. So we have
+      // to hack around it for now.
+      if (node.type === 'ObjectTypeProperty') {
+        return getSourceCode(context).getFirstToken(node).value;
+      }
+
+      return node.key.name;
+    }
+
+    /**
+     * Checks if prop is declared in flow way
+     * @param {Object} prop Property object, single prop type declaration
+     * @returns {boolean}
+     */
+    function flowCheck(prop) {
+      return (
+        prop.type === 'ObjectTypeProperty'
+        && prop.value.type === 'BooleanTypeAnnotation'
+        && rule.test(getPropName(prop)) === false
+      );
+    }
+
+    /**
+     * Checks if prop is declared in regular way
+     * @param {Object} prop Property object, single prop type declaration
+     * @returns {boolean}
+     */
+    function regularCheck(prop) {
+      const propKey = getPropKey(prop);
+      return (
+        propKey
+        && propTypeNames.indexOf(propKey) >= 0
+        && rule.test(getPropName(prop)) === false
+      );
+    }
+
+    function tsCheck(prop) {
+      if (prop.type !== 'TSPropertySignature') return false;
+      const typeAnnotation = (prop.typeAnnotation || {}).typeAnnotation;
+      return (
+        typeAnnotation
+        && typeAnnotation.type === 'TSBooleanKeyword'
+        && rule.test(getPropName(prop)) === false
+      );
+    }
+
+    /**
+     * Runs recursive check on all proptypes
+     * @param {Array} proptypes A list of Property object (for each proptype defined)
+     * @param {Function} addInvalidProp callback to run for each error
+     */
+    function runCheck(proptypes, addInvalidProp) {
+      if (proptypes) {
+        proptypes.forEach((prop) => {
+          if (config.validateNested && nestedPropTypes(prop)) {
+            runCheck(prop.value.arguments[0].properties, addInvalidProp);
+            return;
+          }
+          if (flowCheck(prop) || regularCheck(prop) || tsCheck(prop)) {
+            addInvalidProp(prop);
+          }
+        });
+      }
+    }
+
+    /**
+     * Checks and mark props with invalid naming
+     * @param {Object} node The component node we're testing
+     * @param {Array} proptypes A list of Property object (for each proptype defined)
+     */
+    function validatePropNaming(node, proptypes) {
+      const component = components.get(node) || node;
+      const invalidProps = component.invalidProps || [];
+
+      runCheck(proptypes, (prop) => {
+        invalidProps.push(prop);
+      });
+
+      components.set(node, {
+        invalidProps,
+      });
+    }
+
+    /**
+     * Reports invalid prop naming
+     * @param {Object} component The component to process
+     */
+    function reportInvalidNaming(component) {
+      component.invalidProps.forEach((propNode) => {
+        const propName = getPropName(propNode);
+        report(context, config.message || messages.patternMismatch, !config.message && 'patternMismatch', {
+          node: propNode,
+          data: {
+            component: propName,
+            propName,
+            pattern: config.rule,
+          },
+        });
+      });
+    }
+
+    function checkPropWrapperArguments(node, args) {
+      if (!node || !Array.isArray(args)) {
+        return;
+      }
+      args.filter((arg) => arg.type === 'ObjectExpression').forEach((object) => validatePropNaming(node, object.properties));
+    }
+
+    function getComponentTypeAnnotation(component) {
+      // If this is a functional component that uses a global type, check it
+      if (
+        (component.node.type === 'FunctionDeclaration' || component.node.type === 'ArrowFunctionExpression')
+        && component.node.params
+        && component.node.params.length > 0
+        && component.node.params[0].typeAnnotation
+      ) {
+        return component.node.params[0].typeAnnotation.typeAnnotation;
+      }
+
+      if (
+        !component.node.parent
+        || component.node.parent.type !== 'VariableDeclarator'
+        || !component.node.parent.id
+        || component.node.parent.id.type !== 'Identifier'
+        || !component.node.parent.id.typeAnnotation
+        || !component.node.parent.id.typeAnnotation.typeAnnotation
+      ) {
+        return;
+      }
+
+      const annotationTypeArguments = propsUtil.getTypeArguments(
+        component.node.parent.id.typeAnnotation.typeAnnotation
+      );
+      if (
+        annotationTypeArguments && (
+          annotationTypeArguments.type === 'TSTypeParameterInstantiation'
+          || annotationTypeArguments.type === 'TypeParameterInstantiation'
+        )
+      ) {
+        return annotationTypeArguments.params.find(
+          (param) => param.type === 'TSTypeReference' || param.type === 'GenericTypeAnnotation'
+        );
+      }
+    }
+
+    function findAllTypeAnnotations(identifier, node) {
+      if (node.type === 'TSTypeLiteral' || node.type === 'ObjectTypeAnnotation' || node.type === 'TSInterfaceBody') {
+        const currentNode = [].concat(
+          objectTypeAnnotations.get(identifier.name) || [],
+          node
+        );
+        objectTypeAnnotations.set(identifier.name, currentNode);
+      } else if (
+        node.type === 'TSParenthesizedType'
+        && (
+          node.typeAnnotation.type === 'TSIntersectionType'
+          || node.typeAnnotation.type === 'TSUnionType'
+        )
+      ) {
+        node.typeAnnotation.types.forEach((type) => {
+          findAllTypeAnnotations(identifier, type);
+        });
+      } else if (
+        node.type === 'TSIntersectionType'
+        || node.type === 'TSUnionType'
+        || node.type === 'IntersectionTypeAnnotation'
+        || node.type === 'UnionTypeAnnotation'
+      ) {
+        node.types.forEach((type) => {
+          findAllTypeAnnotations(identifier, type);
+        });
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!rule || !propsUtil.isPropTypesDeclaration(node)) {
+          return;
+        }
+        if (
+          node.value
+          && astUtil.isCallExpression(node.value)
+          && propWrapperUtil.isPropWrapperFunction(
+            context,
+            getText(context, node.value.callee)
+          )
+        ) {
+          checkPropWrapperArguments(node, node.value.arguments);
+        }
+        if (node.value && node.value.properties) {
+          validatePropNaming(node, node.value.properties);
+        }
+        if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
+          validatePropNaming(node, node.typeAnnotation.typeAnnotation.properties);
+        }
+      },
+
+      MemberExpression(node) {
+        if (!rule || !propsUtil.isPropTypesDeclaration(node)) {
+          return;
+        }
+        const component = utils.getRelatedComponent(node);
+        if (!component || !node.parent.right) {
+          return;
+        }
+        const right = node.parent.right;
+        if (
+          astUtil.isCallExpression(right)
+          && propWrapperUtil.isPropWrapperFunction(
+            context,
+            getText(context, right.callee)
+          )
+        ) {
+          checkPropWrapperArguments(component.node, right.arguments);
+          return;
+        }
+        validatePropNaming(component.node, node.parent.right.properties);
+      },
+
+      ObjectExpression(node) {
+        if (!rule) {
+          return;
+        }
+
+        // Search for the proptypes declaration
+        node.properties.forEach((property) => {
+          if (!propsUtil.isPropTypesDeclaration(property)) {
+            return;
+          }
+          validatePropNaming(node, property.value.properties);
+        });
+      },
+
+      TypeAlias(node) {
+        findAllTypeAnnotations(node.id, node.right);
+      },
+
+      TSTypeAliasDeclaration(node) {
+        findAllTypeAnnotations(node.id, node.typeAnnotation);
+      },
+
+      TSInterfaceDeclaration(node) {
+        findAllTypeAnnotations(node.id, node.body);
+      },
+
+      // eslint-disable-next-line object-shorthand
+      'Program:exit'() {
+        if (!rule) {
+          return;
+        }
+
+        values(components.list()).forEach((component) => {
+          const annotation = getComponentTypeAnnotation(component);
+
+          if (annotation) {
+            let propType;
+            if (annotation.type === 'GenericTypeAnnotation') {
+              propType = objectTypeAnnotations.get(annotation.id.name);
+            } else if (annotation.type === 'ObjectTypeAnnotation' || annotation.type === 'TSTypeLiteral') {
+              propType = annotation;
+            } else if (annotation.type === 'TSTypeReference') {
+              propType = objectTypeAnnotations.get(annotation.typeName.name);
+            } else if (annotation.type === 'TSIntersectionType') {
+              propType = flatMap(annotation.types, (type) => (
+                type.type === 'TSTypeReference'
+                  ? objectTypeAnnotations.get(type.typeName.name)
+                  : type
+              ));
+            }
+
+            if (propType) {
+              [].concat(propType).filter(Boolean).forEach((prop) => {
+                validatePropNaming(
+                  component.node,
+                  prop.properties || prop.members || prop.body
+                );
+              });
+            }
+          }
+
+          if (component.invalidProps && component.invalidProps.length > 0) {
+            reportInvalidNaming(component);
+          }
+        });
+
+        // Reset cache
+        objectTypeAnnotations.clear();
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=button-has-type.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"button-has-type.d.ts","sourceRoot":"","sources":["button-has-type.js"],"names":[],"mappings":"wBA8BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/button-has-type.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,169 @@
+/**
+ * @fileoverview Forbid "button" element without an explicit "type" attribute
+ * @author Filipp Riabchun
+ */
+
+'use strict';
+
+const getProp = require('jsx-ast-utils/getProp');
+const getLiteralPropValue = require('jsx-ast-utils/getLiteralPropValue');
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const optionDefaults = {
+  button: true,
+  submit: true,
+  reset: true,
+};
+
+const messages = {
+  missingType: 'Missing an explicit type attribute for button',
+  complexType: 'The button type attribute must be specified by a static string or a trivial ternary expression',
+  invalidValue: '"{{value}}" is an invalid value for button type attribute',
+  forbiddenValue: '"{{value}}" is an invalid value for button type attribute',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of `button` elements without an explicit `type` attribute',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('button-has-type'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        button: {
+          default: optionDefaults.button,
+          type: 'boolean',
+        },
+        submit: {
+          default: optionDefaults.submit,
+          type: 'boolean',
+        },
+        reset: {
+          default: optionDefaults.reset,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = Object.assign({}, optionDefaults, context.options[0]);
+
+    function reportMissing(node) {
+      report(context, messages.missingType, 'missingType', {
+        node,
+      });
+    }
+
+    function reportComplex(node) {
+      report(context, messages.complexType, 'complexType', {
+        node,
+      });
+    }
+
+    function checkValue(node, value) {
+      if (!(value in configuration)) {
+        report(context, messages.invalidValue, 'invalidValue', {
+          node,
+          data: {
+            value,
+          },
+        });
+      } else if (!configuration[value]) {
+        report(context, messages.forbiddenValue, 'forbiddenValue', {
+          node,
+          data: {
+            value,
+          },
+        });
+      }
+    }
+
+    function checkExpression(node, expression) {
+      switch (expression.type) {
+        case 'Literal':
+          checkValue(node, expression.value);
+          return;
+        case 'TemplateLiteral':
+          if (expression.expressions.length === 0) {
+            checkValue(node, expression.quasis[0].value.raw);
+          } else {
+            reportComplex(expression);
+          }
+          return;
+        case 'ConditionalExpression':
+          checkExpression(node, expression.consequent);
+          checkExpression(node, expression.alternate);
+          return;
+        default:
+          reportComplex(expression);
+      }
+    }
+
+    return {
+      JSXElement(node) {
+        if (node.openingElement.name.name !== 'button') {
+          return;
+        }
+
+        const typeProp = getProp(node.openingElement.attributes, 'type');
+
+        if (!typeProp) {
+          reportMissing(node);
+          return;
+        }
+
+        if (typeProp.value && typeProp.value.type === 'JSXExpressionContainer') {
+          checkExpression(node, typeProp.value.expression);
+          return;
+        }
+
+        const propValue = getLiteralPropValue(typeProp);
+        checkValue(node, propValue);
+      },
+      CallExpression(node) {
+        if (!isCreateElement(context, node) || node.arguments.length < 1) {
+          return;
+        }
+
+        if (node.arguments[0].type !== 'Literal' || node.arguments[0].value !== 'button') {
+          return;
+        }
+
+        if (!node.arguments[1] || node.arguments[1].type !== 'ObjectExpression') {
+          reportMissing(node);
+          return;
+        }
+
+        const props = node.arguments[1].properties;
+        const typeProp = props.find((prop) => (
+          'key' in prop
+          && prop.key
+          && 'name' in prop.key
+          && prop.key.name === 'type'
+        ));
+
+        if (!typeProp) {
+          reportMissing(node);
+          return;
+        }
+
+        checkExpression(node, 'value' in typeProp ? typeProp.value : undefined);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=checked-requires-onchange-or-readonly.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"checked-requires-onchange-or-readonly.d.ts","sourceRoot":"","sources":["checked-requires-onchange-or-readonly.js"],"names":[],"mappings":"wBA2CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/checked-requires-onchange-or-readonly.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,142 @@
+/**
+ * @fileoverview Enforce the use of the 'onChange' or 'readonly' attribute when 'checked' is used'
+ * @author Jaesoekjjang
+ */
+
+'use strict';
+
+const ASTUtils = require('jsx-ast-utils');
+const flatMap = require('array.prototype.flatmap');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+const docsUrl = require('../util/docsUrl');
+
+const messages = {
+  missingProperty: '`checked` should be used with either `onChange` or `readOnly`.',
+  exclusiveCheckedAttribute: 'Use either `checked` or `defaultChecked`, but not both.',
+};
+
+const targetPropSet = new Set(['checked', 'onChange', 'readOnly', 'defaultChecked']);
+
+const defaultOptions = {
+  ignoreMissingProperties: false,
+  ignoreExclusiveCheckedAttribute: false,
+};
+
+/**
+ * @param {object[]} properties
+ * @param {string} keyName
+ * @returns {Set<string>}
+ */
+function extractTargetProps(properties, keyName) {
+  return new Set(
+    flatMap(
+      properties,
+      (prop) => (
+        prop[keyName] && targetPropSet.has(prop[keyName].name)
+          ? [prop[keyName].name]
+          : []
+      )
+    )
+  );
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce using `onChange` or `readonly` attribute when `checked` is used',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('checked-requires-onchange-or-readonly'),
+    },
+    messages,
+    schema: [{
+      additionalProperties: false,
+      properties: {
+        ignoreMissingProperties: {
+          type: 'boolean',
+        },
+        ignoreExclusiveCheckedAttribute: {
+          type: 'boolean',
+        },
+      },
+    }],
+  },
+  create(context) {
+    const options = Object.assign({}, defaultOptions, context.options[0]);
+
+    function reportMissingProperty(node) {
+      report(
+        context,
+        messages.missingProperty,
+        'missingProperty',
+        { node }
+      );
+    }
+
+    function reportExclusiveCheckedAttribute(node) {
+      report(
+        context,
+        messages.exclusiveCheckedAttribute,
+        'exclusiveCheckedAttribute',
+        { node }
+      );
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @param {Set<string>} propSet
+     * @returns {void}
+     */
+    const checkAttributesAndReport = (node, propSet) => {
+      if (!propSet.has('checked')) {
+        return;
+      }
+
+      if (!options.ignoreExclusiveCheckedAttribute && propSet.has('defaultChecked')) {
+        reportExclusiveCheckedAttribute(node);
+      }
+
+      if (
+        !options.ignoreMissingProperties
+        && !(propSet.has('onChange') || propSet.has('readOnly'))
+      ) {
+        reportMissingProperty(node);
+      }
+    };
+
+    return {
+      JSXOpeningElement(node) {
+        if (ASTUtils.elementType(node) !== 'input') {
+          return;
+        }
+
+        const propSet = extractTargetProps(node.attributes, 'name');
+        checkAttributesAndReport(node, propSet);
+      },
+      CallExpression(node) {
+        if (!isCreateElement(context, node)) {
+          return;
+        }
+
+        const firstArg = node.arguments[0];
+        const secondArg = node.arguments[1];
+        if (
+          !firstArg
+          || firstArg.type !== 'Literal'
+          || firstArg.value !== 'input'
+        ) {
+          return;
+        }
+
+        if (!secondArg || secondArg.type !== 'ObjectExpression') {
+          return;
+        }
+
+        const propSet = extractTargetProps(secondArg.properties, 'key');
+        checkAttributesAndReport(node, propSet);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=default-props-match-prop-types.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"default-props-match-prop-types.d.ts","sourceRoot":"","sources":["default-props-match-prop-types.js"],"names":[],"mappings":"wBAuBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/default-props-match-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,109 @@
+/**
+ * @fileOverview Enforce all defaultProps are defined in propTypes
+ * @author Vitor Balocco
+ * @author Roy Sutton
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  requiredHasDefault: 'defaultProp "{{name}}" defined for isRequired propType.',
+  defaultHasNoType: 'defaultProp "{{name}}" has no corresponding propTypes declaration.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce all defaultProps have a corresponding non-required PropType',
+      category: 'Best Practices',
+      url: docsUrl('default-props-match-prop-types'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowRequiredDefaults: {
+          default: false,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components) => {
+    const configuration = context.options[0] || {};
+    const allowRequiredDefaults = configuration.allowRequiredDefaults || false;
+
+    /**
+     * Reports all defaultProps passed in that don't have an appropriate propTypes counterpart.
+     * @param  {Object[]} propTypes    Array of propTypes to check.
+     * @param  {Object}   defaultProps Object of defaultProps to check. Keys are the props names.
+     * @return {void}
+     */
+    function reportInvalidDefaultProps(propTypes, defaultProps) {
+      // If this defaultProps is "unresolved" or the propTypes is undefined, then we should ignore
+      // this component and not report any errors for it, to avoid false-positives with e.g.
+      // external defaultProps/propTypes declarations or spread operators.
+      if (defaultProps === 'unresolved' || !propTypes || Object.keys(propTypes).length === 0) {
+        return;
+      }
+
+      Object.keys(defaultProps).forEach((defaultPropName) => {
+        const defaultProp = defaultProps[defaultPropName];
+        const prop = propTypes[defaultPropName];
+
+        if (prop && (allowRequiredDefaults || !prop.isRequired)) {
+          return;
+        }
+
+        if (prop) {
+          report(context, messages.requiredHasDefault, 'requiredHasDefault', {
+            node: defaultProp.node,
+            data: {
+              name: defaultPropName,
+            },
+          });
+        } else {
+          report(context, messages.defaultHasNoType, 'defaultHasNoType', {
+            node: defaultProp.node,
+            data: {
+              name: defaultPropName,
+            },
+          });
+        }
+      });
+    }
+
+    // --------------------------------------------------------------------------
+    // Public API
+    // --------------------------------------------------------------------------
+
+    return {
+      'Program:exit'() {
+        // If no defaultProps could be found, we don't report anything.
+        values(components.list())
+          .filter((component) => component.defaultProps)
+          .forEach((component) => {
+            reportInvalidDefaultProps(
+              component.declaredPropTypes,
+              component.defaultProps || {}
+            );
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=destructuring-assignment.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"destructuring-assignment.d.ts","sourceRoot":"","sources":["destructuring-assignment.js"],"names":[],"mappings":"wBA2DW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/destructuring-assignment.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,319 @@
+/**
+ * @fileoverview Enforce consistent usage of destructuring assignment of props, state, and context.
+ */
+
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const eslintUtil = require('../util/eslint');
+const isAssignmentLHS = require('../util/ast').isAssignmentLHS;
+const report = require('../util/report');
+
+const getScope = eslintUtil.getScope;
+const getText = eslintUtil.getText;
+
+const DEFAULT_OPTION = 'always';
+
+function createSFCParams() {
+  const queue = [];
+
+  return {
+    push(params) {
+      queue.unshift(params);
+    },
+    pop() {
+      queue.shift();
+    },
+    propsName() {
+      const found = queue.find((params) => {
+        const props = params[0];
+        return props && !props.destructuring && props.name;
+      });
+      return found && found[0] && found[0].name;
+    },
+    contextName() {
+      const found = queue.find((params) => {
+        const context = params[1];
+        return context && !context.destructuring && context.name;
+      });
+      return found && found[1] && found[1].name;
+    },
+  };
+}
+
+function evalParams(params) {
+  return params.map((param) => ({
+    destructuring: param.type === 'ObjectPattern',
+    name: param.type === 'Identifier' && param.name,
+  }));
+}
+
+const messages = {
+  noDestructPropsInSFCArg: 'Must never use destructuring props assignment in SFC argument',
+  noDestructContextInSFCArg: 'Must never use destructuring context assignment in SFC argument',
+  noDestructAssignment: 'Must never use destructuring {{type}} assignment',
+  useDestructAssignment: 'Must use destructuring {{type}} assignment',
+  destructureInSignature: 'Must destructure props in the function signature.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce consistent usage of destructuring assignment of props, state, and context',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('destructuring-assignment'),
+    },
+    fixable: 'code',
+    messages,
+
+    schema: [{
+      type: 'string',
+      enum: [
+        'always',
+        'never',
+      ],
+    }, {
+      type: 'object',
+      properties: {
+        ignoreClassFields: {
+          type: 'boolean',
+        },
+        destructureInSignature: {
+          type: 'string',
+          enum: [
+            'always',
+            'ignore',
+          ],
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const configuration = context.options[0] || DEFAULT_OPTION;
+    const ignoreClassFields = (context.options[1] && (context.options[1].ignoreClassFields === true)) || false;
+    const destructureInSignature = (context.options[1] && context.options[1].destructureInSignature) || 'ignore';
+    const sfcParams = createSFCParams();
+
+    /**
+     * @param {ASTNode} node We expect either an ArrowFunctionExpression,
+     *   FunctionDeclaration, or FunctionExpression
+     */
+    function handleStatelessComponent(node) {
+      const params = evalParams(node.params);
+
+      const SFCComponent = components.get(getScope(context, node).block);
+      if (!SFCComponent) {
+        return;
+      }
+      sfcParams.push(params);
+
+      if (params[0] && params[0].destructuring && components.get(node) && configuration === 'never') {
+        report(context, messages.noDestructPropsInSFCArg, 'noDestructPropsInSFCArg', {
+          node,
+        });
+      } else if (params[1] && params[1].destructuring && components.get(node) && configuration === 'never') {
+        report(context, messages.noDestructContextInSFCArg, 'noDestructContextInSFCArg', {
+          node,
+        });
+      }
+    }
+
+    function handleStatelessComponentExit(node) {
+      const SFCComponent = components.get(getScope(context, node).block);
+      if (SFCComponent) {
+        sfcParams.pop();
+      }
+    }
+
+    function handleSFCUsage(node) {
+      const propsName = sfcParams.propsName();
+      const contextName = sfcParams.contextName();
+      // props.aProp || context.aProp
+      const isPropUsed = (
+        (propsName && node.object.name === propsName)
+          || (contextName && node.object.name === contextName)
+      )
+        && !isAssignmentLHS(node);
+      if (isPropUsed && configuration === 'always' && !node.optional) {
+        report(context, messages.useDestructAssignment, 'useDestructAssignment', {
+          node,
+          data: {
+            type: node.object.name,
+          },
+        });
+      }
+    }
+
+    function isInClassProperty(node) {
+      let curNode = node.parent;
+      while (curNode) {
+        if (curNode.type === 'ClassProperty' || curNode.type === 'PropertyDefinition') {
+          return true;
+        }
+        curNode = curNode.parent;
+      }
+      return false;
+    }
+
+    function handleClassUsage(node) {
+      // this.props.Aprop || this.context.aProp || this.state.aState
+      const isPropUsed = (
+        node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression'
+        && (node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state')
+        && !isAssignmentLHS(node)
+      );
+
+      if (
+        isPropUsed && configuration === 'always'
+        && !(ignoreClassFields && isInClassProperty(node))
+      ) {
+        report(context, messages.useDestructAssignment, 'useDestructAssignment', {
+          node,
+          data: {
+            type: node.object.property.name,
+          },
+        });
+      }
+    }
+
+    // valid-jsdoc cannot read function types
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * Find a parent that satisfy the given predicate
+     * @param {ASTNode} node
+     * @param {(node: ASTNode) => boolean} predicate
+     * @returns {ASTNode | undefined}
+     */
+    function findParent(node, predicate) {
+      let n = node;
+      while (n) {
+        if (predicate(n)) {
+          return n;
+        }
+        n = n.parent;
+      }
+      return undefined;
+    }
+
+    return {
+
+      FunctionDeclaration: handleStatelessComponent,
+
+      ArrowFunctionExpression: handleStatelessComponent,
+
+      FunctionExpression: handleStatelessComponent,
+
+      'FunctionDeclaration:exit': handleStatelessComponentExit,
+
+      'ArrowFunctionExpression:exit': handleStatelessComponentExit,
+
+      'FunctionExpression:exit': handleStatelessComponentExit,
+
+      MemberExpression(node) {
+        const SFCComponent = utils.getParentStatelessComponent(node);
+        if (SFCComponent) {
+          handleSFCUsage(node);
+        }
+
+        const classComponent = utils.getParentComponent(node);
+        if (classComponent) {
+          handleClassUsage(node);
+        }
+      },
+
+      TSQualifiedName(node) {
+        if (configuration !== 'always') {
+          return;
+        }
+        // handle `typeof props.a.b`
+        if (node.left.type === 'Identifier'
+          && node.left.name === sfcParams.propsName()
+          && findParent(node, (n) => n.type === 'TSTypeQuery')
+          && utils.getParentStatelessComponent(node)
+        ) {
+          report(context, messages.useDestructAssignment, 'useDestructAssignment', {
+            node,
+            data: {
+              type: 'props',
+            },
+          });
+        }
+      },
+
+      VariableDeclarator(node) {
+        const classComponent = utils.getParentComponent(node);
+        const SFCComponent = components.get(getScope(context, node).block);
+
+        const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
+        // let {foo} = props;
+        const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
+        // let {foo} = this.props;
+        const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (
+          node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state'
+        );
+
+        if (SFCComponent && destructuringSFC && configuration === 'never') {
+          report(context, messages.noDestructAssignment, 'noDestructAssignment', {
+            node,
+            data: {
+              type: node.init.name,
+            },
+          });
+        }
+
+        if (
+          classComponent && destructuringClass && configuration === 'never'
+          && !(ignoreClassFields && (node.parent.type === 'ClassProperty' || node.parent.type === 'PropertyDefinition'))
+        ) {
+          report(context, messages.noDestructAssignment, 'noDestructAssignment', {
+            node,
+            data: {
+              type: node.init.property.name,
+            },
+          });
+        }
+
+        if (
+          SFCComponent
+          && destructuringSFC
+          && configuration === 'always'
+          && destructureInSignature === 'always'
+          && node.init.name === 'props'
+        ) {
+          const scopeSetProps = getScope(context, node).set.get('props');
+          const propsRefs = scopeSetProps && scopeSetProps.references;
+          if (!propsRefs) {
+            return;
+          }
+
+          // Skip if props is used elsewhere
+          if (propsRefs.length > 1) {
+            return;
+          }
+          report(context, messages.destructureInSignature, 'destructureInSignature', {
+            node,
+            fix(fixer) {
+              const param = SFCComponent.node.params[0];
+              if (!param) {
+                return;
+              }
+              const replaceRange = [
+                param.range[0],
+                param.typeAnnotation ? param.typeAnnotation.range[0] : param.range[1],
+              ];
+              return [
+                fixer.replaceTextRange(replaceRange, getText(context, node.id)),
+                fixer.remove(node.parent),
+              ];
+            },
+          });
+        }
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/display-name.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/display-name.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/display-name.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=display-name.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/display-name.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/display-name.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/display-name.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"display-name.d.ts","sourceRoot":"","sources":["display-name.js"],"names":[],"mappings":"wBA6BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/display-name.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/display-name.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/display-name.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,285 @@
+/**
+ * @fileoverview Prevent missing displayName in a React component definition
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const values = require('object.values');
+const filter = require('es-iterator-helpers/Iterator.prototype.filter');
+const forEach = require('es-iterator-helpers/Iterator.prototype.forEach');
+
+const Components = require('../util/Components');
+const isCreateContext = require('../util/isCreateContext');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const testReactVersion = require('../util/version').testReactVersion;
+const propsUtil = require('../util/props');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noDisplayName: 'Component definition is missing display name',
+  noContextDisplayName: 'Context definition is missing display name',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow missing displayName in a React component definition',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('display-name'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignoreTranspilerName: {
+          type: 'boolean',
+        },
+        checkContextObjects: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const config = context.options[0] || {};
+    const ignoreTranspilerName = config.ignoreTranspilerName || false;
+    const checkContextObjects = (config.checkContextObjects || false) && testReactVersion(context, '>= 16.3.0');
+
+    const contextObjects = new Map();
+
+    /**
+     * Mark a prop type as declared
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markDisplayNameAsDeclared(node) {
+      components.set(node, {
+        hasDisplayName: true,
+      });
+    }
+
+    /**
+     * Checks if React.forwardRef is nested inside React.memo
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if React.forwardRef is nested inside React.memo, false if not.
+     */
+    function isNestedMemo(node) {
+      return astUtil.isCallExpression(node)
+        && node.arguments
+        && astUtil.isCallExpression(node.arguments[0])
+        && utils.isPragmaComponentWrapper(node);
+    }
+
+    /**
+     * Reports missing display name for a given component
+     * @param {Object} component The component to process
+     */
+    function reportMissingDisplayName(component) {
+      if (
+        testReactVersion(context, '^0.14.10 || ^15.7.0 || >= 16.12.0')
+        && isNestedMemo(component.node)
+      ) {
+        return;
+      }
+
+      report(context, messages.noDisplayName, 'noDisplayName', {
+        node: component.node,
+      });
+    }
+
+    /**
+     * Reports missing display name for a given context object
+     * @param {Object} contextObj The context object to process
+     */
+    function reportMissingContextDisplayName(contextObj) {
+      report(context, messages.noContextDisplayName, 'noContextDisplayName', {
+        node: contextObj.node,
+      });
+    }
+
+    /**
+     * Checks if the component have a name set by the transpiler
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if component has a name, false if not.
+     */
+    function hasTranspilerName(node) {
+      const namedObjectAssignment = (
+        node.type === 'ObjectExpression'
+        && node.parent
+        && node.parent.parent
+        && node.parent.parent.type === 'AssignmentExpression'
+        && (
+          !node.parent.parent.left.object
+          || node.parent.parent.left.object.name !== 'module'
+          || node.parent.parent.left.property.name !== 'exports'
+        )
+      );
+      const namedObjectDeclaration = (
+        node.type === 'ObjectExpression'
+        && node.parent
+        && node.parent.parent
+        && node.parent.parent.type === 'VariableDeclarator'
+      );
+      const namedClass = (
+        (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')
+        && node.id
+        && !!node.id.name
+      );
+
+      const namedFunctionDeclaration = (
+        (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression')
+        && node.id
+        && !!node.id.name
+      );
+
+      const namedFunctionExpression = (
+        astUtil.isFunctionLikeExpression(node)
+        && node.parent
+        && (node.parent.type === 'VariableDeclarator' || node.parent.type === 'Property' || node.parent.method === true)
+        && (!node.parent.parent || !componentUtil.isES5Component(node.parent.parent, context))
+      );
+
+      if (
+        namedObjectAssignment || namedObjectDeclaration
+        || namedClass
+        || namedFunctionDeclaration || namedFunctionExpression
+      ) {
+        return true;
+      }
+      return false;
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      ExpressionStatement(node) {
+        if (checkContextObjects && isCreateContext(node)) {
+          contextObjects.set(node.expression.left.name, { node, hasDisplayName: false });
+        }
+      },
+      VariableDeclarator(node) {
+        if (checkContextObjects && isCreateContext(node)) {
+          contextObjects.set(node.id.name, { node, hasDisplayName: false });
+        }
+      },
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!propsUtil.isDisplayNameDeclaration(node)) {
+          return;
+        }
+        markDisplayNameAsDeclared(node);
+      },
+
+      MemberExpression(node) {
+        if (!propsUtil.isDisplayNameDeclaration(node.property)) {
+          return;
+        }
+        if (
+          checkContextObjects
+          && node.object
+          && node.object.name
+          && contextObjects.has(node.object.name)
+        ) {
+          contextObjects.get(node.object.name).hasDisplayName = true;
+        }
+        const component = utils.getRelatedComponent(node);
+        if (!component) {
+          return;
+        }
+        markDisplayNameAsDeclared(astUtil.unwrapTSAsExpression(component.node));
+      },
+
+      'FunctionExpression, FunctionDeclaration, ArrowFunctionExpression'(node) {
+        if (ignoreTranspilerName || !hasTranspilerName(node)) {
+          return;
+        }
+        if (components.get(node)) {
+          markDisplayNameAsDeclared(node);
+        }
+      },
+
+      MethodDefinition(node) {
+        if (!propsUtil.isDisplayNameDeclaration(node.key)) {
+          return;
+        }
+        markDisplayNameAsDeclared(node);
+      },
+
+      'ClassExpression, ClassDeclaration'(node) {
+        if (ignoreTranspilerName || !hasTranspilerName(node)) {
+          return;
+        }
+        markDisplayNameAsDeclared(node);
+      },
+
+      ObjectExpression(node) {
+        if (!componentUtil.isES5Component(node, context)) {
+          return;
+        }
+        if (ignoreTranspilerName || !hasTranspilerName(node)) {
+          // Search for the displayName declaration
+          node.properties.forEach((property) => {
+            if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) {
+              return;
+            }
+            markDisplayNameAsDeclared(node);
+          });
+          return;
+        }
+        markDisplayNameAsDeclared(node);
+      },
+
+      CallExpression(node) {
+        if (!utils.isPragmaComponentWrapper(node)) {
+          return;
+        }
+
+        if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
+          // Skip over React.forwardRef declarations that are embedded within
+          // a React.memo i.e. React.memo(React.forwardRef(/* ... */))
+          // This means that we raise a single error for the call to React.memo
+          // instead of one for React.memo and one for React.forwardRef
+          const isWrappedInAnotherPragma = utils.getPragmaComponentWrapper(node);
+          if (
+            !isWrappedInAnotherPragma
+            && (ignoreTranspilerName || !hasTranspilerName(node.arguments[0]))
+          ) {
+            return;
+          }
+
+          if (components.get(node)) {
+            markDisplayNameAsDeclared(node);
+          }
+        }
+      },
+
+      'Program:exit'() {
+        const list = components.list();
+        // Report missing display name for all components
+        values(list).filter((component) => !component.hasDisplayName).forEach((component) => {
+          reportMissingDisplayName(component);
+        });
+        if (checkContextObjects) {
+          // Report missing display name for all context objects
+          forEach(
+            filter(contextObjects.values(), (v) => !v.hasDisplayName),
+            (contextObj) => reportMissingContextDisplayName(contextObj)
+          );
+        }
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=forbid-component-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forbid-component-props.d.ts","sourceRoot":"","sources":["forbid-component-props.js"],"names":[],"mappings":"wBAyBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-component-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,239 @@
+/**
+ * @fileoverview Forbid certain props on components
+ * @author Joe Lencioni
+ */
+
+'use strict';
+
+const minimatch = require('minimatch');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = ['className', 'style'];
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  propIsForbidden: 'Prop "{{prop}}" is forbidden on Components',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow certain props on components',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('forbid-component-props'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbid: {
+          type: 'array',
+          items: {
+            anyOf: [
+              { type: 'string' },
+              {
+                type: 'object',
+                properties: {
+                  propName: { type: 'string' },
+                  allowedFor: {
+                    type: 'array',
+                    uniqueItems: true,
+                    items: { type: 'string' },
+                  },
+                  allowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    items: { type: 'string' },
+                  },
+                  message: { type: 'string' },
+                },
+                additionalProperties: false,
+              },
+              {
+                type: 'object',
+                properties: {
+                  propName: { type: 'string' },
+                  disallowedFor: {
+                    type: 'array',
+                    uniqueItems: true,
+                    minItems: 1,
+                    items: { type: 'string' },
+                  },
+                  disallowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    minItems: 1,
+                    items: { type: 'string' },
+                  },
+                  message: { type: 'string' },
+                },
+                anyOf: [
+                  { required: ['disallowedFor'] },
+                  { required: ['disallowedForPatterns'] },
+                ],
+                additionalProperties: false,
+              },
+              {
+                type: 'object',
+                properties: {
+                  propNamePattern: { type: 'string' },
+                  allowedFor: {
+                    type: 'array',
+                    uniqueItems: true,
+                    items: { type: 'string' },
+                  },
+                  allowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    items: { type: 'string' },
+                  },
+                  message: { type: 'string' },
+                },
+                additionalProperties: false,
+              },
+              {
+                type: 'object',
+                properties: {
+                  propNamePattern: { type: 'string' },
+                  disallowedFor: {
+                    type: 'array',
+                    uniqueItems: true,
+                    minItems: 1,
+                    items: { type: 'string' },
+                  },
+                  disallowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    minItems: 1,
+                    items: { type: 'string' },
+                  },
+                  message: { type: 'string' },
+                },
+                anyOf: [
+                  { required: ['disallowedFor'] },
+                  { required: ['disallowedForPatterns'] },
+                ],
+                additionalProperties: false,
+              },
+            ],
+          },
+        },
+      },
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => {
+      const propName = typeof value === 'string' ? value : value.propName;
+      const propPattern = value.propNamePattern;
+      const prop = propName || propPattern;
+      const options = {
+        allowList: [].concat(value.allowedFor || []),
+        allowPatternList: [].concat(value.allowedForPatterns || []),
+        disallowList: [].concat(value.disallowedFor || []),
+        disallowPatternList: [].concat(value.disallowedForPatterns || []),
+        message: typeof value === 'string' ? null : value.message,
+        isPattern: !!value.propNamePattern,
+      };
+      return [prop, options];
+    }));
+
+    function getPropOptions(prop) {
+      // Get config options having pattern
+      const propNamePatternArray = Array.from(forbid.entries()).filter((propEntry) => propEntry[1].isPattern);
+      // Match current prop with pattern options, return if matched
+      const propNamePattern = propNamePatternArray.find((propPatternVal) => minimatch(prop, propPatternVal[0]));
+      // Get options for matched propNamePattern
+      const propNamePatternOptions = propNamePattern && propNamePattern[1];
+
+      const options = forbid.get(prop) || propNamePatternOptions;
+      return options;
+    }
+
+    function isForbidden(prop, tagName) {
+      const options = getPropOptions(prop);
+      if (!options) {
+        return false;
+      }
+
+      function checkIsTagForbiddenByAllowOptions() {
+        if (options.allowList.indexOf(tagName) !== -1) {
+          return false;
+        }
+
+        if (options.allowPatternList.length === 0) {
+          return true;
+        }
+
+        return options.allowPatternList.every(
+          (pattern) => !minimatch(tagName, pattern)
+        );
+      }
+
+      function checkIsTagForbiddenByDisallowOptions() {
+        if (options.disallowList.indexOf(tagName) !== -1) {
+          return true;
+        }
+
+        if (options.disallowPatternList.length === 0) {
+          return false;
+        }
+
+        return options.disallowPatternList.some(
+          (pattern) => minimatch(tagName, pattern)
+        );
+      }
+
+      const hasDisallowOptions = options.disallowList.length > 0 || options.disallowPatternList.length > 0;
+
+      // disallowList should have a least one item (schema configuration)
+      const isTagForbidden = hasDisallowOptions
+        ? checkIsTagForbiddenByDisallowOptions()
+        : checkIsTagForbiddenByAllowOptions();
+
+      // if the tagName is undefined (`<this.something>`), we assume it's a forbidden element
+      return typeof tagName === 'undefined' || isTagForbidden;
+    }
+
+    return {
+      JSXAttribute(node) {
+        const parentName = node.parent.name;
+        // Extract a component name when using a "namespace", e.g. `<AntdLayout.Content />`.
+        const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`;
+        const componentName = parentName.name || parentName.property.name;
+        if (componentName && typeof componentName[0] === 'string' && componentName[0] !== componentName[0].toUpperCase()) {
+          // This is a DOM node, not a Component, so exit.
+          return;
+        }
+
+        const prop = node.name.name;
+
+        if (!isForbidden(prop, tag)) {
+          return;
+        }
+
+        const customMessage = getPropOptions(prop).message;
+
+        report(context, customMessage || messages.propIsForbidden, !customMessage && 'propIsForbidden', {
+          node,
+          data: {
+            prop,
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=forbid-dom-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forbid-dom-props.d.ts","sourceRoot":"","sources":["forbid-dom-props.js"],"names":[],"mappings":"wBAuCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-dom-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,122 @@
+/**
+ * @fileoverview Forbid certain props on DOM Nodes
+ * @author David Vázquez
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = [];
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/**
+ * @param {Map<string, object>} forbidMap // { disallowList: null | string[], message: null | string }
+ * @param {string} prop
+ * @param {string} tagName
+ * @returns {boolean}
+ */
+function isForbidden(forbidMap, prop, tagName) {
+  const options = forbidMap.get(prop);
+  return options && (
+    typeof tagName === 'undefined'
+    || !options.disallowList
+    || options.disallowList.indexOf(tagName) !== -1
+  );
+}
+
+const messages = {
+  propIsForbidden: 'Prop "{{prop}}" is forbidden on DOM Nodes',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow certain props on DOM Nodes',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('forbid-dom-props'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbid: {
+          type: 'array',
+          items: {
+            anyOf: [{
+              type: 'string',
+            }, {
+              type: 'object',
+              properties: {
+                propName: {
+                  type: 'string',
+                },
+                disallowedFor: {
+                  type: 'array',
+                  uniqueItems: true,
+                  items: {
+                    type: 'string',
+                  },
+                },
+                message: {
+                  type: 'string',
+                },
+              },
+            }],
+            minLength: 1,
+          },
+          uniqueItems: true,
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => {
+      const propName = typeof value === 'string' ? value : value.propName;
+      return [propName, {
+        disallowList: typeof value === 'string' ? null : (value.disallowedFor || null),
+        message: typeof value === 'string' ? null : value.message,
+      }];
+    }));
+
+    return {
+      JSXAttribute(node) {
+        const tag = node.parent.name.name;
+        if (!(tag && typeof tag === 'string' && tag[0] !== tag[0].toUpperCase())) {
+          // This is a Component, not a DOM node, so exit.
+          return;
+        }
+
+        const prop = node.name.name;
+
+        if (!isForbidden(forbid, prop, tag)) {
+          return;
+        }
+
+        const customMessage = forbid.get(prop).message;
+
+        report(context, customMessage || messages.propIsForbidden, !customMessage && 'propIsForbidden', {
+          node,
+          data: {
+            prop,
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=forbid-elements.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forbid-elements.d.ts","sourceRoot":"","sources":["forbid-elements.js"],"names":[],"mappings":"wBAsBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-elements.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,119 @@
+/**
+ * @fileoverview Forbid certain elements
+ * @author Kenneth Chung
+ */
+
+'use strict';
+
+const has = require('hasown');
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  forbiddenElement: '<{{element}}> is forbidden',
+  forbiddenElement_message: '<{{element}}> is forbidden, {{message}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow certain elements',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('forbid-elements'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbid: {
+          type: 'array',
+          items: {
+            anyOf: [
+              { type: 'string' },
+              {
+                type: 'object',
+                properties: {
+                  element: { type: 'string' },
+                  message: { type: 'string' },
+                },
+                required: ['element'],
+                additionalProperties: false,
+              },
+            ],
+          },
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const forbidConfiguration = configuration.forbid || [];
+
+    /** @type {Record<string, { element: string, message?: string }>} */
+    const indexedForbidConfigs = {};
+
+    forbidConfiguration.forEach((item) => {
+      if (typeof item === 'string') {
+        indexedForbidConfigs[item] = { element: item };
+      } else {
+        indexedForbidConfigs[item.element] = item;
+      }
+    });
+
+    function reportIfForbidden(element, node) {
+      if (has(indexedForbidConfigs, element)) {
+        const message = indexedForbidConfigs[element].message;
+
+        report(
+          context,
+          message ? messages.forbiddenElement_message : messages.forbiddenElement,
+          message ? 'forbiddenElement_message' : 'forbiddenElement',
+          {
+            node,
+            data: {
+              element,
+              message,
+            },
+          }
+        );
+      }
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        reportIfForbidden(getText(context, node.name), node.name);
+      },
+
+      CallExpression(node) {
+        if (!isCreateElement(context, node)) {
+          return;
+        }
+
+        const argument = node.arguments[0];
+        if (!argument) {
+          return;
+        }
+
+        if (argument.type === 'Identifier' && /^[A-Z_]/.test(argument.name)) {
+          reportIfForbidden(argument.name, argument);
+        } else if (argument.type === 'Literal' && /^[a-z][^.]*$/.test(String(argument.value))) {
+          reportIfForbidden(argument.value, argument);
+        } else if (argument.type === 'MemberExpression') {
+          reportIfForbidden(getText(context, argument), argument);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=forbid-foreign-prop-types.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forbid-foreign-prop-types.d.ts","sourceRoot":"","sources":["forbid-foreign-prop-types.js"],"names":[],"mappings":"wBAeW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-foreign-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,141 @@
+/**
+ * @fileoverview Forbid using another component's propTypes
+ * @author Ian Christian Myers
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const ast = require('../util/ast');
+const report = require('../util/report');
+
+const messages = {
+  forbiddenPropType: 'Using propTypes from another component is not safe because they may be removed in production builds',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow using another component\'s propTypes',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('forbid-foreign-prop-types'),
+    },
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          allowInPropTypes: {
+            type: 'boolean',
+          },
+        },
+        additionalProperties: false,
+      },
+    ],
+  },
+
+  create(context) {
+    const config = context.options[0] || {};
+    const allowInPropTypes = config.allowInPropTypes || false;
+
+    // --------------------------------------------------------------------------
+    // Helpers
+    // --------------------------------------------------------------------------
+
+    function findParentAssignmentExpression(node) {
+      let parent = node.parent;
+
+      while (parent && parent.type !== 'Program') {
+        if (parent.type === 'AssignmentExpression') {
+          return parent;
+        }
+        parent = parent.parent;
+      }
+      return null;
+    }
+
+    function findParentClassProperty(node) {
+      let parent = node.parent;
+
+      while (parent && parent.type !== 'Program') {
+        if (parent.type === 'ClassProperty' || parent.type === 'PropertyDefinition') {
+          return parent;
+        }
+        parent = parent.parent;
+      }
+      return null;
+    }
+
+    function isAllowedAssignment(node) {
+      if (!allowInPropTypes) {
+        return false;
+      }
+
+      const assignmentExpression = findParentAssignmentExpression(node);
+
+      if (
+        assignmentExpression
+        && assignmentExpression.left
+        && assignmentExpression.left.property
+        && assignmentExpression.left.property.name === 'propTypes'
+      ) {
+        return true;
+      }
+
+      const classProperty = findParentClassProperty(node);
+
+      if (
+        classProperty
+        && classProperty.key
+        && classProperty.key.name === 'propTypes'
+      ) {
+        return true;
+      }
+      return false;
+    }
+
+    return {
+      MemberExpression(node) {
+        if (
+          (node.property
+          && (
+            !node.computed
+            && node.property.type === 'Identifier'
+            && node.property.name === 'propTypes'
+            && !ast.isAssignmentLHS(node)
+            && !isAllowedAssignment(node)
+          )) || (
+            // @ts-expect-error: The JSXText type is not present in the estree type definitions
+            (node.property.type === 'Literal' || node.property.type === 'JSXText')
+            && 'value' in node.property
+            && node.property.value === 'propTypes'
+            && !ast.isAssignmentLHS(node)
+            && !isAllowedAssignment(node)
+          )
+        ) {
+          report(context, messages.forbiddenPropType, 'forbiddenPropType', {
+            node: node.property,
+          });
+        }
+      },
+
+      ObjectPattern(node) {
+        const propTypesNode = node.properties.find((property) => (
+          property.type === 'Property'
+          && 'name' in property.key
+          && property.key.name === 'propTypes'
+        ));
+
+        if (propTypesNode) {
+          report(context, messages.forbiddenPropType, 'forbiddenPropType', {
+            node: propTypesNode,
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=forbid-prop-types.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forbid-prop-types.d.ts","sourceRoot":"","sources":["forbid-prop-types.js"],"names":[],"mappings":"wBA4BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,298 @@
+/**
+ * @fileoverview Forbid certain propTypes
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const propsUtil = require('../util/props');
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const propWrapperUtil = require('../util/propWrapper');
+const report = require('../util/report');
+const getText = require('../util/eslint').getText;
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = ['any', 'array', 'object'];
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  forbiddenPropType: 'Prop type "{{target}}" is forbidden',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow certain propTypes',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('forbid-prop-types'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbid: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        checkContextTypes: {
+          type: 'boolean',
+        },
+        checkChildContextTypes: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: true,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const checkContextTypes = configuration.checkContextTypes || false;
+    const checkChildContextTypes = configuration.checkChildContextTypes || false;
+    let propTypesPackageName = null;
+    let reactPackageName = null;
+    let isForeignPropTypesPackage = false;
+
+    function isPropTypesPackage(node) {
+      return (
+        node.type === 'Identifier'
+        && (
+          node.name === null
+          || node.name === propTypesPackageName
+          || !isForeignPropTypesPackage
+        )
+      ) || (
+        node.type === 'MemberExpression'
+        && (
+          node.object.name === null
+          || node.object.name === reactPackageName
+          || !isForeignPropTypesPackage
+        )
+      );
+    }
+
+    function isForbidden(type) {
+      const forbid = configuration.forbid || DEFAULTS;
+      return forbid.indexOf(type) >= 0;
+    }
+
+    function reportIfForbidden(type, declaration, target) {
+      if (isForbidden(type)) {
+        report(context, messages.forbiddenPropType, 'forbiddenPropType', {
+          node: declaration,
+          data: {
+            target,
+          },
+        });
+      }
+    }
+
+    function shouldCheckContextTypes(node) {
+      if (checkContextTypes && propsUtil.isContextTypesDeclaration(node)) {
+        return true;
+      }
+      return false;
+    }
+
+    function shouldCheckChildContextTypes(node) {
+      if (checkChildContextTypes && propsUtil.isChildContextTypesDeclaration(node)) {
+        return true;
+      }
+      return false;
+    }
+
+    /**
+     * Checks if propTypes declarations are forbidden
+     * @param {Array} declarations The array of AST nodes being checked.
+     * @returns {void}
+     */
+    function checkProperties(declarations) {
+      if (declarations) {
+        declarations.forEach((declaration) => {
+          if (declaration.type !== 'Property') {
+            return;
+          }
+          let target;
+          let value = declaration.value;
+          if (
+            value.type === 'MemberExpression'
+            && value.property
+            && value.property.name
+            && value.property.name === 'isRequired'
+          ) {
+            value = value.object;
+          }
+          if (astUtil.isCallExpression(value)) {
+            if (!isPropTypesPackage(value.callee)) {
+              return;
+            }
+            value.arguments.forEach((arg) => {
+              const name = arg.type === 'MemberExpression' ? arg.property.name : arg.name;
+              reportIfForbidden(name, declaration, name);
+            });
+            value = value.callee;
+          }
+          if (!isPropTypesPackage(value)) {
+            return;
+          }
+          if (value.property) {
+            target = value.property.name;
+          } else if (value.type === 'Identifier') {
+            target = value.name;
+          }
+          reportIfForbidden(target, declaration, target);
+        });
+      }
+    }
+
+    function checkNode(node) {
+      if (!node) {
+        return;
+      }
+
+      if (node.type === 'ObjectExpression') {
+        checkProperties(node.properties);
+      } else if (node.type === 'Identifier') {
+        const propTypesObject = variableUtil.findVariableByName(context, node, node.name);
+        if (propTypesObject && propTypesObject.properties) {
+          checkProperties(propTypesObject.properties);
+        }
+      } else if (astUtil.isCallExpression(node)) {
+        const innerNode = node.arguments && node.arguments[0];
+        if (
+          propWrapperUtil.isPropWrapperFunction(context, getText(context, node.callee))
+            && innerNode
+        ) {
+          checkNode(innerNode);
+        }
+      }
+    }
+
+    return {
+      ImportDeclaration(node) {
+        if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
+          if (node.specifiers.length > 0) {
+            propTypesPackageName = node.specifiers[0].local.name;
+          }
+        } else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react"
+          if (node.specifiers.length > 0) {
+            reactPackageName = node.specifiers[0].local.name; // guard against accidental anonymous `import "react"`
+          }
+          if (node.specifiers.length >= 1) {
+            const propTypesSpecifier = node.specifiers.find((specifier) => (
+              'imported' in specifier
+              && specifier.imported
+              && 'name' in specifier.imported
+              && specifier.imported.name === 'PropTypes'
+            ));
+            if (propTypesSpecifier) {
+              propTypesPackageName = propTypesSpecifier.local.name;
+            }
+          }
+        } else { // package is not imported from "react" or "prop-types"
+          // eslint-disable-next-line no-lonely-if
+          if (node.specifiers.some((x) => x.local.name === 'PropTypes')) { // assert: node.specifiers.length > 1
+            isForeignPropTypesPackage = true;
+          }
+        }
+      },
+
+      'ClassProperty, PropertyDefinition'(node) {
+        if (
+          !propsUtil.isPropTypesDeclaration(node)
+          && !isPropTypesPackage(node)
+          && !shouldCheckContextTypes(node)
+          && !shouldCheckChildContextTypes(node)
+        ) {
+          return;
+        }
+        checkNode(node.value);
+      },
+
+      MemberExpression(node) {
+        if (
+          !propsUtil.isPropTypesDeclaration(node)
+          && !isPropTypesPackage(node)
+          && !shouldCheckContextTypes(node)
+          && !shouldCheckChildContextTypes(node)
+        ) {
+          return;
+        }
+
+        checkNode('right' in node.parent && node.parent.right);
+      },
+
+      CallExpression(node) {
+        if (
+          node.callee.type === 'MemberExpression'
+          && node.callee.object
+          && !isPropTypesPackage(node.callee.object)
+          && !propsUtil.isPropTypesDeclaration(node.callee)
+        ) {
+          return;
+        }
+
+        if (
+          node.arguments.length > 0
+          && (
+            ('name' in node.callee && node.callee.name === 'shape')
+            || astUtil.getPropertyName(node.callee) === 'shape'
+          )
+        ) {
+          checkProperties('properties' in node.arguments[0] && node.arguments[0].properties);
+        }
+      },
+
+      MethodDefinition(node) {
+        if (
+          !propsUtil.isPropTypesDeclaration(node)
+          && !isPropTypesPackage(node)
+          && !shouldCheckContextTypes(node)
+          && !shouldCheckChildContextTypes(node)
+        ) {
+          return;
+        }
+
+        const returnStatement = astUtil.findReturnStatement(node);
+
+        if (returnStatement && returnStatement.argument) {
+          checkNode(returnStatement.argument);
+        }
+      },
+
+      ObjectExpression(node) {
+        node.properties.forEach((property) => {
+          if (!('key' in property) || !property.key) {
+            return;
+          }
+
+          if (
+            !propsUtil.isPropTypesDeclaration(property)
+            && !isPropTypesPackage(property)
+            && !shouldCheckContextTypes(property)
+            && !shouldCheckChildContextTypes(property)
+          ) {
+            return;
+          }
+          if (property.value.type === 'ObjectExpression') {
+            checkProperties(property.value.properties);
+          }
+        });
+      },
+
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=forward-ref-uses-ref.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"forward-ref-uses-ref.d.ts","sourceRoot":"","sources":["forward-ref-uses-ref.js"],"names":[],"mappings":"wBA2CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/forward-ref-uses-ref.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,100 @@
+/**
+ * @fileoverview Require all forwardRef components include a ref parameter
+ */
+
+'use strict';
+
+const isParenthesized = require('../util/ast').isParenthesized;
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getMessageData = require('../util/message');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/**
+ * @param {ASTNode} node
+ * @returns {boolean} If the node represents the identifier `forwardRef`.
+ */
+function isForwardRefIdentifier(node) {
+  return node.type === 'Identifier' && node.name === 'forwardRef';
+}
+
+/**
+ * @param {ASTNode} node
+ * @returns {boolean} If the node represents a function call `forwardRef()` or `React.forwardRef()`.
+ */
+function isForwardRefCall(node) {
+  return (
+    node.type === 'CallExpression'
+    && (
+      isForwardRefIdentifier(node.callee)
+      || (node.callee.type === 'MemberExpression' && isForwardRefIdentifier(node.callee.property))
+    )
+  );
+}
+
+const messages = {
+  missingRefParameter: 'forwardRef is used with this component but no ref parameter is set',
+  addRefParameter: 'Add a ref parameter',
+  removeForwardRef: 'Remove forwardRef wrapper',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Require all forwardRef components include a ref parameter',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('forward-ref-uses-ref'),
+    },
+    messages,
+    schema: [],
+    type: 'suggestion',
+    hasSuggestions: true,
+  },
+
+  create(context) {
+    const sourceCode = context.getSourceCode();
+
+    return {
+      'FunctionExpression, ArrowFunctionExpression'(node) {
+        if (!isForwardRefCall(node.parent)) {
+          return;
+        }
+
+        if (node.params.length === 1) {
+          report(context, messages.missingRefParameter, 'missingRefParameter', {
+            node,
+            suggest: [
+              Object.assign(
+                getMessageData('addRefParameter', messages.addRefParameter),
+                {
+                  fix(fixer) {
+                    const param = node.params[0];
+                    // If using shorthand arrow function syntax, add parentheses around the new parameter pair
+                    const shouldAddParentheses = node.type === 'ArrowFunctionExpression' && !isParenthesized(context, param);
+                    return [].concat(
+                      shouldAddParentheses ? fixer.insertTextBefore(param, '(') : [],
+                      fixer.insertTextAfter(param, `, ref${shouldAddParentheses ? ')' : ''}`)
+                    );
+                  },
+                }
+              ),
+              Object.assign(
+                getMessageData('removeForwardRef', messages.removeForwardRef),
+                {
+                  fix(fixer) {
+                    return fixer.replaceText(node.parent, sourceCode.getText(node));
+                  },
+                }
+              ),
+            ],
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=function-component-definition.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"function-component-definition.d.ts","sourceRoot":"","sources":["function-component-definition.js"],"names":[],"mappings":"wBAqHW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/function-component-definition.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,285 @@
+/**
+ * @fileoverview Standardize the way function component get defined
+ * @author Stefan Wullems
+ */
+
+'use strict';
+
+const arrayIncludes = require('array-includes');
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const reportC = require('../util/report');
+const getText = require('../util/eslint').getText;
+const propsUtil = require('../util/props');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function buildFunction(template, parts) {
+  return Object.keys(parts).reduce(
+    (acc, key) => acc.replace(`{${key}}`, () => parts[key] || ''),
+    template
+  );
+}
+
+const NAMED_FUNCTION_TEMPLATES = {
+  'function-declaration': 'function {name}{typeParams}({params}){returnType} {body}',
+  'arrow-function': '{varType} {name}{typeAnnotation} = {typeParams}({params}){returnType} => {body}',
+  'function-expression': '{varType} {name}{typeAnnotation} = function{typeParams}({params}){returnType} {body}',
+};
+
+const UNNAMED_FUNCTION_TEMPLATES = {
+  'function-expression': 'function{typeParams}({params}){returnType} {body}',
+  'arrow-function': '{typeParams}({params}){returnType} => {body}',
+};
+
+function hasOneUnconstrainedTypeParam(node) {
+  const nodeTypeArguments = propsUtil.getTypeArguments(node);
+
+  return nodeTypeArguments
+    && nodeTypeArguments.params
+    && nodeTypeArguments.params.length === 1
+    && !nodeTypeArguments.params[0].constraint;
+}
+
+function hasName(node) {
+  return (
+    node.type === 'FunctionDeclaration'
+    || node.parent.type === 'VariableDeclarator'
+  );
+}
+
+function getNodeText(prop, source) {
+  if (!prop) return null;
+  return source.slice(prop.range[0], prop.range[1]);
+}
+
+function getName(node) {
+  if (node.type === 'FunctionDeclaration') {
+    return node.id.name;
+  }
+
+  if (
+    node.type === 'ArrowFunctionExpression'
+    || node.type === 'FunctionExpression'
+  ) {
+    return hasName(node) && node.parent.id.name;
+  }
+}
+
+function getParams(node, source) {
+  if (node.params.length === 0) return null;
+  return source.slice(
+    node.params[0].range[0],
+    node.params[node.params.length - 1].range[1]
+  );
+}
+
+function getBody(node, source) {
+  const range = node.body.range;
+
+  if (node.body.type !== 'BlockStatement') {
+    return ['{', `  return ${source.slice(range[0], range[1])}`, '}'].join('\n');
+  }
+
+  return source.slice(range[0], range[1]);
+}
+
+function getTypeAnnotation(node, source) {
+  if (!hasName(node) || node.type === 'FunctionDeclaration') return;
+
+  if (
+    node.type === 'ArrowFunctionExpression'
+    || node.type === 'FunctionExpression'
+  ) {
+    return getNodeText(node.parent.id.typeAnnotation, source);
+  }
+}
+
+function isUnfixableBecauseOfExport(node) {
+  return (
+    node.type === 'FunctionDeclaration'
+    && node.parent
+    && node.parent.type === 'ExportDefaultDeclaration'
+  );
+}
+
+function isFunctionExpressionWithName(node) {
+  return node.type === 'FunctionExpression' && node.id && node.id.name;
+}
+
+const messages = {
+  'function-declaration': 'Function component is not a function declaration',
+  'function-expression': 'Function component is not a function expression',
+  'arrow-function': 'Function component is not an arrow function',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce a specific function type for function components',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('function-component-definition'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          namedComponents: {
+            anyOf: [
+              {
+                enum: [
+                  'function-declaration',
+                  'arrow-function',
+                  'function-expression',
+                ],
+              },
+              {
+                type: 'array',
+                items: {
+                  type: 'string',
+                  enum: [
+                    'function-declaration',
+                    'arrow-function',
+                    'function-expression',
+                  ],
+                },
+              },
+            ],
+          },
+          unnamedComponents: {
+            anyOf: [
+              { enum: ['arrow-function', 'function-expression'] },
+              {
+                type: 'array',
+                items: {
+                  type: 'string',
+                  enum: ['arrow-function', 'function-expression'],
+                },
+              },
+            ],
+          },
+        },
+      },
+    ],
+  },
+
+  create: Components.detect((context, components) => {
+    const configuration = context.options[0] || {};
+    let fileVarType = 'var';
+
+    const namedConfig = [].concat(
+      configuration.namedComponents || 'function-declaration'
+    );
+    const unnamedConfig = [].concat(
+      configuration.unnamedComponents || 'function-expression'
+    );
+
+    function getFixer(node, options) {
+      const source = getText(context);
+
+      const typeAnnotation = getTypeAnnotation(node, source);
+
+      if (options.type === 'function-declaration' && typeAnnotation) {
+        return;
+      }
+      if (options.type === 'arrow-function' && hasOneUnconstrainedTypeParam(node)) {
+        return;
+      }
+      if (isUnfixableBecauseOfExport(node)) return;
+      if (isFunctionExpressionWithName(node)) return;
+      let varType = fileVarType;
+      if (
+        (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression')
+        && node.parent.type === 'VariableDeclarator'
+      ) {
+        varType = node.parent.parent.kind;
+      }
+
+      const nodeTypeArguments = propsUtil.getTypeArguments(node);
+      return (fixer) => fixer.replaceTextRange(
+        options.range,
+        buildFunction(options.template, {
+          typeAnnotation,
+          typeParams: getNodeText(nodeTypeArguments, source),
+          params: getParams(node, source),
+          returnType: getNodeText(node.returnType, source),
+          body: getBody(node, source),
+          name: getName(node),
+          varType,
+        })
+      );
+    }
+
+    function report(node, options) {
+      reportC(context, messages[options.messageId], options.messageId, {
+        node,
+        fix: getFixer(node, options.fixerOptions),
+      });
+    }
+
+    function validate(node, functionType) {
+      if (!components.get(node)) return;
+
+      if (node.parent && node.parent.type === 'Property') return;
+
+      if (hasName(node) && !arrayIncludes(namedConfig, functionType)) {
+        report(node, {
+          messageId: namedConfig[0],
+          fixerOptions: {
+            type: namedConfig[0],
+            template: NAMED_FUNCTION_TEMPLATES[namedConfig[0]],
+            range:
+              node.type === 'FunctionDeclaration'
+                ? node.range
+                : node.parent.parent.range,
+          },
+        });
+      }
+      if (!hasName(node) && !arrayIncludes(unnamedConfig, functionType)) {
+        report(node, {
+          messageId: unnamedConfig[0],
+          fixerOptions: {
+            type: unnamedConfig[0],
+            template: UNNAMED_FUNCTION_TEMPLATES[unnamedConfig[0]],
+            range: node.range,
+          },
+        });
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+    const validatePairs = [];
+    let hasES6OrJsx = false;
+    return {
+      FunctionDeclaration(node) {
+        validatePairs.push([node, 'function-declaration']);
+      },
+      ArrowFunctionExpression(node) {
+        validatePairs.push([node, 'arrow-function']);
+      },
+      FunctionExpression(node) {
+        validatePairs.push([node, 'function-expression']);
+      },
+      VariableDeclaration(node) {
+        hasES6OrJsx = hasES6OrJsx || node.kind === 'const' || node.kind === 'let';
+      },
+      'Program:exit'() {
+        if (hasES6OrJsx) fileVarType = 'const';
+        validatePairs.forEach((pair) => validate(pair[0], pair[1]));
+      },
+      'ImportDeclaration, ExportNamedDeclaration, ExportDefaultDeclaration, ExportAllDeclaration, ExportSpecifier, ExportDefaultSpecifier, JSXElement, TSExportAssignment, TSImportEqualsDeclaration'() {
+        hasES6OrJsx = true;
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=hook-use-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"hook-use-state.d.ts","sourceRoot":"","sources":["hook-use-state.js"],"names":[],"mappings":"wBA4BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/hook-use-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,205 @@
+/**
+ * @fileoverview Ensure symmetric naming of useState hook value and setter variables
+ * @author Duncan Beevers
+ */
+
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getMessageData = require('../util/message');
+const getText = require('../util/eslint').getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function isNodeDestructuring(node) {
+  return node && (node.type === 'ArrayPattern' || node.type === 'ObjectPattern');
+}
+
+const messages = {
+  useStateErrorMessage: 'useState call is not destructured into value + setter pair',
+  useStateErrorMessageOrAddOption: 'useState call is not destructured into value + setter pair (you can allow destructuring by enabling "allowDestructuredState" option)',
+  suggestPair: 'Destructure useState call into value + setter pair',
+  suggestMemo: 'Replace useState call with useMemo',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Ensure destructuring and symmetric naming of useState hook value and setter variables',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('hook-use-state'),
+    },
+    messages,
+    schema: [{
+      type: 'object',
+      properties: {
+        allowDestructuredState: {
+          default: false,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+    type: 'suggestion',
+    hasSuggestions: true,
+  },
+
+  create: Components.detect((context, components, util) => {
+    const configuration = context.options[0] || {};
+    const allowDestructuredState = configuration.allowDestructuredState || false;
+
+    return {
+      CallExpression(node) {
+        const isImmediateReturn = node.parent
+          && node.parent.type === 'ReturnStatement';
+
+        if (isImmediateReturn || !util.isReactHookCall(node, ['useState'])) {
+          return;
+        }
+
+        const isDestructuringDeclarator = node.parent
+          && node.parent.type === 'VariableDeclarator'
+          && node.parent.id.type === 'ArrayPattern';
+
+        if (!isDestructuringDeclarator) {
+          report(
+            context,
+            messages.useStateErrorMessage,
+            'useStateErrorMessage',
+            {
+              node,
+              suggest: false,
+            }
+          );
+          return;
+        }
+
+        const variableNodes = node.parent.id.elements;
+        const valueVariable = variableNodes[0];
+        const setterVariable = variableNodes[1];
+        const isOnlyValueDestructuring = isNodeDestructuring(valueVariable) && !isNodeDestructuring(setterVariable);
+
+        if (allowDestructuredState && isOnlyValueDestructuring) {
+          return;
+        }
+
+        const valueVariableName = valueVariable
+          ? valueVariable.name
+          : undefined;
+
+        const setterVariableName = setterVariable
+          ? setterVariable.name
+          : undefined;
+
+        const caseCandidateMatch = valueVariableName ? valueVariableName.match(/(^[a-z]+)(.*)/) : undefined;
+        const upperCaseCandidatePrefix = caseCandidateMatch ? caseCandidateMatch[1] : undefined;
+        const caseCandidateSuffix = caseCandidateMatch ? caseCandidateMatch[2] : undefined;
+        const expectedSetterVariableNames = upperCaseCandidatePrefix ? [
+          `set${upperCaseCandidatePrefix.charAt(0).toUpperCase()}${upperCaseCandidatePrefix.slice(1)}${caseCandidateSuffix}`,
+          `set${upperCaseCandidatePrefix.toUpperCase()}${caseCandidateSuffix}`,
+        ] : [];
+
+        const isSymmetricGetterSetterPair = valueVariable
+          && setterVariable
+          && expectedSetterVariableNames.indexOf(setterVariableName) !== -1
+          && variableNodes.length === 2;
+
+        if (!isSymmetricGetterSetterPair) {
+          const suggestions = [
+            Object.assign(
+              getMessageData('suggestPair', messages.suggestPair),
+              {
+                fix(fixer) {
+                  if (expectedSetterVariableNames.length > 0) {
+                    return fixer.replaceTextRange(
+                      node.parent.id.range,
+                      `[${valueVariableName}, ${expectedSetterVariableNames[0]}]`
+                    );
+                  }
+                },
+              }
+            ),
+          ];
+
+          const defaultReactImports = components.getDefaultReactImports();
+          const defaultReactImportSpecifier = defaultReactImports
+            ? defaultReactImports[0]
+            : undefined;
+
+          const defaultReactImportName = defaultReactImportSpecifier
+            ? defaultReactImportSpecifier.local.name
+            : undefined;
+
+          const namedReactImports = components.getNamedReactImports();
+          const useStateReactImportSpecifier = namedReactImports
+            ? namedReactImports.find((specifier) => specifier.imported.name === 'useState')
+            : undefined;
+
+          const isSingleGetter = valueVariable && variableNodes.length === 1;
+          const isUseStateCalledWithSingleArgument = node.arguments.length === 1;
+          if (isSingleGetter && isUseStateCalledWithSingleArgument) {
+            const useMemoReactImportSpecifier = namedReactImports
+              && namedReactImports.find((specifier) => specifier.imported.name === 'useMemo');
+
+            let useMemoCode;
+            if (useMemoReactImportSpecifier) {
+              useMemoCode = useMemoReactImportSpecifier.local.name;
+            } else if (defaultReactImportName) {
+              useMemoCode = `${defaultReactImportName}.useMemo`;
+            } else {
+              useMemoCode = 'useMemo';
+            }
+
+            suggestions.unshift(Object.assign(
+              getMessageData('suggestMemo', messages.suggestMemo),
+              {
+                fix: (fixer) => [
+                  // Add useMemo import, if necessary
+                  useStateReactImportSpecifier
+                    && (!useMemoReactImportSpecifier || defaultReactImportName)
+                    && fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'),
+                  // Convert single-value destructure to simple assignment
+                  fixer.replaceTextRange(node.parent.id.range, valueVariableName),
+                  // Convert useState call to useMemo + arrow function + dependency array
+                  fixer.replaceTextRange(
+                    node.range,
+                    `${useMemoCode}(() => ${getText(context, node.arguments[0])}, [])`
+                  ),
+                ].filter(Boolean),
+              }
+            ));
+          }
+
+          if (isOnlyValueDestructuring) {
+            report(
+              context,
+              messages.useStateErrorMessageOrAddOption,
+              'useStateErrorMessageOrAddOption',
+              {
+                node: node.parent.id,
+                suggest: false,
+              }
+            );
+            return;
+          }
+
+          report(
+            context,
+            messages.useStateErrorMessage,
+            'useStateErrorMessage',
+            {
+              node: node.parent.id,
+              suggest: suggestions,
+            }
+          );
+        }
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=iframe-missing-sandbox.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"iframe-missing-sandbox.d.ts","sourceRoot":"","sources":["iframe-missing-sandbox.js"],"names":[],"mappings":"wBA+GW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/iframe-missing-sandbox.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview TBD
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+const messages = {
+  attributeMissing: 'An iframe element is missing a sandbox attribute',
+  invalidValue: 'An iframe element defines a sandbox attribute with invalid value "{{ value }}"',
+  invalidCombination: 'An iframe element defines a sandbox attribute with both allow-scripts and allow-same-origin which is invalid',
+};
+
+const ALLOWED_VALUES = [
+  // From https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox
+  '',
+  'allow-downloads-without-user-activation',
+  'allow-downloads',
+  'allow-forms',
+  'allow-modals',
+  'allow-orientation-lock',
+  'allow-pointer-lock',
+  'allow-popups',
+  'allow-popups-to-escape-sandbox',
+  'allow-presentation',
+  'allow-same-origin',
+  'allow-scripts',
+  'allow-storage-access-by-user-activation',
+  'allow-top-navigation',
+  'allow-top-navigation-by-user-activation',
+];
+
+function validateSandboxAttribute(context, node, attribute) {
+  if (typeof attribute !== 'string') {
+    // Only string literals are supported for now
+    return;
+  }
+  const values = attribute.split(' ');
+  let allowScripts = false;
+  let allowSameOrigin = false;
+  values.forEach((attributeValue) => {
+    const trimmedAttributeValue = attributeValue.trim();
+    if (ALLOWED_VALUES.indexOf(trimmedAttributeValue) === -1) {
+      report(context, messages.invalidValue, 'invalidValue', {
+        node,
+        data: {
+          value: trimmedAttributeValue,
+        },
+      });
+    }
+    if (trimmedAttributeValue === 'allow-scripts') {
+      allowScripts = true;
+    }
+    if (trimmedAttributeValue === 'allow-same-origin') {
+      allowSameOrigin = true;
+    }
+  });
+  if (allowScripts && allowSameOrigin) {
+    report(context, messages.invalidCombination, 'invalidCombination', {
+      node,
+    });
+  }
+}
+
+function checkAttributes(context, node) {
+  let sandboxAttributeFound = false;
+  node.attributes.forEach((attribute) => {
+    if (attribute.type === 'JSXAttribute'
+        && attribute.name
+        && attribute.name.type === 'JSXIdentifier'
+        && attribute.name.name === 'sandbox'
+    ) {
+      sandboxAttributeFound = true;
+      if (
+        attribute.value
+        && attribute.value.type === 'Literal'
+        && attribute.value.value
+      ) {
+        validateSandboxAttribute(context, node, attribute.value.value);
+      }
+    }
+  });
+  if (!sandboxAttributeFound) {
+    report(context, messages.attributeMissing, 'attributeMissing', {
+      node,
+    });
+  }
+}
+
+function checkProps(context, node) {
+  let sandboxAttributeFound = false;
+  if (node.arguments.length > 1) {
+    const props = node.arguments[1];
+    const sandboxProp = props.properties && props.properties.find((x) => x.type === 'Property' && x.key.name === 'sandbox');
+    if (sandboxProp) {
+      sandboxAttributeFound = true;
+      if (sandboxProp.value && sandboxProp.value.type === 'Literal' && sandboxProp.value.value) {
+        validateSandboxAttribute(context, node, sandboxProp.value.value);
+      }
+    }
+  }
+  if (!sandboxAttributeFound) {
+    report(context, messages.attributeMissing, 'attributeMissing', {
+      node,
+    });
+  }
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce sandbox attribute on iframe elements',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('iframe-missing-sandbox'),
+    },
+
+    schema: [],
+
+    messages,
+  },
+
+  create(context) {
+    return {
+      'JSXOpeningElement[name.name="iframe"]'(node) {
+        checkAttributes(context, node);
+      },
+
+      CallExpression(node) {
+        if (isCreateElement(context, node) && node.arguments && node.arguments.length > 0) {
+          const tag = node.arguments[0];
+          if (tag.type === 'Literal' && tag.value === 'iframe') {
+            checkProps(context, node);
+          }
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/index.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,108 @@
+export = rules;
+/** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */
+declare const rules: {
+    'boolean-prop-naming': import("eslint").Rule.RuleModule;
+    'button-has-type': import("eslint").Rule.RuleModule;
+    'checked-requires-onchange-or-readonly': import("eslint").Rule.RuleModule;
+    'default-props-match-prop-types': import("eslint").Rule.RuleModule;
+    'destructuring-assignment': import("eslint").Rule.RuleModule;
+    'display-name': import("eslint").Rule.RuleModule;
+    'forbid-component-props': import("eslint").Rule.RuleModule;
+    'forbid-dom-props': import("eslint").Rule.RuleModule;
+    'forbid-elements': import("eslint").Rule.RuleModule;
+    'forbid-foreign-prop-types': import("eslint").Rule.RuleModule;
+    'forbid-prop-types': import("eslint").Rule.RuleModule;
+    'forward-ref-uses-ref': import("eslint").Rule.RuleModule;
+    'function-component-definition': import("eslint").Rule.RuleModule;
+    'hook-use-state': import("eslint").Rule.RuleModule;
+    'iframe-missing-sandbox': import("eslint").Rule.RuleModule;
+    'jsx-boolean-value': import("eslint").Rule.RuleModule;
+    'jsx-child-element-spacing': import("eslint").Rule.RuleModule;
+    'jsx-closing-bracket-location': import("eslint").Rule.RuleModule;
+    'jsx-closing-tag-location': import("eslint").Rule.RuleModule;
+    'jsx-curly-spacing': import("eslint").Rule.RuleModule;
+    'jsx-curly-newline': import("eslint").Rule.RuleModule;
+    'jsx-equals-spacing': import("eslint").Rule.RuleModule;
+    'jsx-filename-extension': import("eslint").Rule.RuleModule;
+    'jsx-first-prop-new-line': import("eslint").Rule.RuleModule;
+    'jsx-handler-names': import("eslint").Rule.RuleModule;
+    'jsx-indent': import("eslint").Rule.RuleModule;
+    'jsx-indent-props': import("eslint").Rule.RuleModule;
+    'jsx-key': import("eslint").Rule.RuleModule;
+    'jsx-max-depth': import("eslint").Rule.RuleModule;
+    'jsx-max-props-per-line': import("eslint").Rule.RuleModule;
+    'jsx-newline': import("eslint").Rule.RuleModule;
+    'jsx-no-bind': import("eslint").Rule.RuleModule;
+    'jsx-no-comment-textnodes': import("eslint").Rule.RuleModule;
+    'jsx-no-constructed-context-values': import("eslint").Rule.RuleModule;
+    'jsx-no-duplicate-props': import("eslint").Rule.RuleModule;
+    'jsx-no-leaked-render': import("eslint").Rule.RuleModule;
+    'jsx-no-literals': import("eslint").Rule.RuleModule;
+    'jsx-no-script-url': import("eslint").Rule.RuleModule;
+    'jsx-no-target-blank': import("eslint").Rule.RuleModule;
+    'jsx-no-useless-fragment': import("eslint").Rule.RuleModule;
+    'jsx-one-expression-per-line': import("eslint").Rule.RuleModule;
+    'jsx-no-undef': import("eslint").Rule.RuleModule;
+    'jsx-curly-brace-presence': import("eslint").Rule.RuleModule;
+    'jsx-pascal-case': import("eslint").Rule.RuleModule;
+    'jsx-fragments': import("eslint").Rule.RuleModule;
+    'jsx-props-no-multi-spaces': import("eslint").Rule.RuleModule;
+    'jsx-props-no-spreading': import("eslint").Rule.RuleModule;
+    'jsx-props-no-spread-multi': import("eslint").Rule.RuleModule;
+    'jsx-sort-default-props': import("eslint").Rule.RuleModule;
+    'jsx-sort-props': import("eslint").Rule.RuleModule;
+    'jsx-space-before-closing': import("eslint").Rule.RuleModule;
+    'jsx-tag-spacing': import("eslint").Rule.RuleModule;
+    'jsx-uses-react': import("eslint").Rule.RuleModule;
+    'jsx-uses-vars': import("eslint").Rule.RuleModule;
+    'jsx-wrap-multilines': import("eslint").Rule.RuleModule;
+    'no-invalid-html-attribute': import("eslint").Rule.RuleModule;
+    'no-access-state-in-setstate': import("eslint").Rule.RuleModule;
+    'no-adjacent-inline-elements': import("eslint").Rule.RuleModule;
+    'no-array-index-key': import("eslint").Rule.RuleModule;
+    'no-arrow-function-lifecycle': import("eslint").Rule.RuleModule;
+    'no-children-prop': import("eslint").Rule.RuleModule;
+    'no-danger': import("eslint").Rule.RuleModule;
+    'no-danger-with-children': import("eslint").Rule.RuleModule;
+    'no-deprecated': import("eslint").Rule.RuleModule;
+    'no-did-mount-set-state': import("eslint").Rule.RuleModule;
+    'no-did-update-set-state': import("eslint").Rule.RuleModule;
+    'no-direct-mutation-state': import("eslint").Rule.RuleModule;
+    'no-find-dom-node': import("eslint").Rule.RuleModule;
+    'no-is-mounted': import("eslint").Rule.RuleModule;
+    'no-multi-comp': import("eslint").Rule.RuleModule;
+    'no-namespace': import("eslint").Rule.RuleModule;
+    'no-set-state': import("eslint").Rule.RuleModule;
+    'no-string-refs': import("eslint").Rule.RuleModule;
+    'no-redundant-should-component-update': import("eslint").Rule.RuleModule;
+    'no-render-return-value': import("eslint").Rule.RuleModule;
+    'no-this-in-sfc': import("eslint").Rule.RuleModule;
+    'no-typos': import("eslint").Rule.RuleModule;
+    'no-unescaped-entities': import("eslint").Rule.RuleModule;
+    'no-unknown-property': import("eslint").Rule.RuleModule;
+    'no-unsafe': import("eslint").Rule.RuleModule;
+    'no-unstable-nested-components': import("eslint").Rule.RuleModule;
+    'no-unused-class-component-methods': import("eslint").Rule.RuleModule;
+    'no-unused-prop-types': import("eslint").Rule.RuleModule;
+    'no-unused-state': import("eslint").Rule.RuleModule;
+    'no-object-type-as-default-prop': import("eslint").Rule.RuleModule;
+    'no-will-update-set-state': import("eslint").Rule.RuleModule;
+    'prefer-es6-class': import("eslint").Rule.RuleModule;
+    'prefer-exact-props': import("eslint").Rule.RuleModule;
+    'prefer-read-only-props': import("eslint").Rule.RuleModule;
+    'prefer-stateless-function': import("eslint").Rule.RuleModule;
+    'prop-types': import("eslint").Rule.RuleModule;
+    'react-in-jsx-scope': import("eslint").Rule.RuleModule;
+    'require-default-props': import("eslint").Rule.RuleModule;
+    'require-optimization': import("eslint").Rule.RuleModule;
+    'require-render-return': import("eslint").Rule.RuleModule;
+    'self-closing-comp': import("eslint").Rule.RuleModule;
+    'sort-comp': import("eslint").Rule.RuleModule;
+    'sort-default-props': import("eslint").Rule.RuleModule;
+    'sort-prop-types': import("eslint").Rule.RuleModule;
+    'state-in-constructor': import("eslint").Rule.RuleModule;
+    'static-property-placement': import("eslint").Rule.RuleModule;
+    'style-prop-object': import("eslint").Rule.RuleModule;
+    'void-dom-elements-no-children': import("eslint").Rule.RuleModule;
+};
+//# sourceMappingURL=index.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/index.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/index.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";AAIA,oEAAoE;AACpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwGE"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/index.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,112 @@
+'use strict';
+
+/* eslint global-require: 0 */
+
+/** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */
+const rules = {
+  'boolean-prop-naming': require('./boolean-prop-naming'),
+  'button-has-type': require('./button-has-type'),
+  'checked-requires-onchange-or-readonly': require('./checked-requires-onchange-or-readonly'),
+  'default-props-match-prop-types': require('./default-props-match-prop-types'),
+  'destructuring-assignment': require('./destructuring-assignment'),
+  'display-name': require('./display-name'),
+  'forbid-component-props': require('./forbid-component-props'),
+  'forbid-dom-props': require('./forbid-dom-props'),
+  'forbid-elements': require('./forbid-elements'),
+  'forbid-foreign-prop-types': require('./forbid-foreign-prop-types'),
+  'forbid-prop-types': require('./forbid-prop-types'),
+  'forward-ref-uses-ref': require('./forward-ref-uses-ref'),
+  'function-component-definition': require('./function-component-definition'),
+  'hook-use-state': require('./hook-use-state'),
+  'iframe-missing-sandbox': require('./iframe-missing-sandbox'),
+  'jsx-boolean-value': require('./jsx-boolean-value'),
+  'jsx-child-element-spacing': require('./jsx-child-element-spacing'),
+  'jsx-closing-bracket-location': require('./jsx-closing-bracket-location'),
+  'jsx-closing-tag-location': require('./jsx-closing-tag-location'),
+  'jsx-curly-spacing': require('./jsx-curly-spacing'),
+  'jsx-curly-newline': require('./jsx-curly-newline'),
+  'jsx-equals-spacing': require('./jsx-equals-spacing'),
+  'jsx-filename-extension': require('./jsx-filename-extension'),
+  'jsx-first-prop-new-line': require('./jsx-first-prop-new-line'),
+  'jsx-handler-names': require('./jsx-handler-names'),
+  'jsx-indent': require('./jsx-indent'),
+  'jsx-indent-props': require('./jsx-indent-props'),
+  'jsx-key': require('./jsx-key'),
+  'jsx-max-depth': require('./jsx-max-depth'),
+  'jsx-max-props-per-line': require('./jsx-max-props-per-line'),
+  'jsx-newline': require('./jsx-newline'),
+  'jsx-no-bind': require('./jsx-no-bind'),
+  'jsx-no-comment-textnodes': require('./jsx-no-comment-textnodes'),
+  'jsx-no-constructed-context-values': require('./jsx-no-constructed-context-values'),
+  'jsx-no-duplicate-props': require('./jsx-no-duplicate-props'),
+  'jsx-no-leaked-render': require('./jsx-no-leaked-render'),
+  'jsx-no-literals': require('./jsx-no-literals'),
+  'jsx-no-script-url': require('./jsx-no-script-url'),
+  'jsx-no-target-blank': require('./jsx-no-target-blank'),
+  'jsx-no-useless-fragment': require('./jsx-no-useless-fragment'),
+  'jsx-one-expression-per-line': require('./jsx-one-expression-per-line'),
+  'jsx-no-undef': require('./jsx-no-undef'),
+  'jsx-curly-brace-presence': require('./jsx-curly-brace-presence'),
+  'jsx-pascal-case': require('./jsx-pascal-case'),
+  'jsx-fragments': require('./jsx-fragments'),
+  'jsx-props-no-multi-spaces': require('./jsx-props-no-multi-spaces'),
+  'jsx-props-no-spreading': require('./jsx-props-no-spreading'),
+  'jsx-props-no-spread-multi': require('./jsx-props-no-spread-multi'),
+  'jsx-sort-default-props': require('./jsx-sort-default-props'),
+  'jsx-sort-props': require('./jsx-sort-props'),
+  'jsx-space-before-closing': require('./jsx-space-before-closing'),
+  'jsx-tag-spacing': require('./jsx-tag-spacing'),
+  'jsx-uses-react': require('./jsx-uses-react'),
+  'jsx-uses-vars': require('./jsx-uses-vars'),
+  'jsx-wrap-multilines': require('./jsx-wrap-multilines'),
+  'no-invalid-html-attribute': require('./no-invalid-html-attribute'),
+  'no-access-state-in-setstate': require('./no-access-state-in-setstate'),
+  'no-adjacent-inline-elements': require('./no-adjacent-inline-elements'),
+  'no-array-index-key': require('./no-array-index-key'),
+  'no-arrow-function-lifecycle': require('./no-arrow-function-lifecycle'),
+  'no-children-prop': require('./no-children-prop'),
+  'no-danger': require('./no-danger'),
+  'no-danger-with-children': require('./no-danger-with-children'),
+  'no-deprecated': require('./no-deprecated'),
+  'no-did-mount-set-state': require('./no-did-mount-set-state'),
+  'no-did-update-set-state': require('./no-did-update-set-state'),
+  'no-direct-mutation-state': require('./no-direct-mutation-state'),
+  'no-find-dom-node': require('./no-find-dom-node'),
+  'no-is-mounted': require('./no-is-mounted'),
+  'no-multi-comp': require('./no-multi-comp'),
+  'no-namespace': require('./no-namespace'),
+  'no-set-state': require('./no-set-state'),
+  'no-string-refs': require('./no-string-refs'),
+  'no-redundant-should-component-update': require('./no-redundant-should-component-update'),
+  'no-render-return-value': require('./no-render-return-value'),
+  'no-this-in-sfc': require('./no-this-in-sfc'),
+  'no-typos': require('./no-typos'),
+  'no-unescaped-entities': require('./no-unescaped-entities'),
+  'no-unknown-property': require('./no-unknown-property'),
+  'no-unsafe': require('./no-unsafe'),
+  'no-unstable-nested-components': require('./no-unstable-nested-components'),
+  'no-unused-class-component-methods': require('./no-unused-class-component-methods'),
+  'no-unused-prop-types': require('./no-unused-prop-types'),
+  'no-unused-state': require('./no-unused-state'),
+  'no-object-type-as-default-prop': require('./no-object-type-as-default-prop'),
+  'no-will-update-set-state': require('./no-will-update-set-state'),
+  'prefer-es6-class': require('./prefer-es6-class'),
+  'prefer-exact-props': require('./prefer-exact-props'),
+  'prefer-read-only-props': require('./prefer-read-only-props'),
+  'prefer-stateless-function': require('./prefer-stateless-function'),
+  'prop-types': require('./prop-types'),
+  'react-in-jsx-scope': require('./react-in-jsx-scope'),
+  'require-default-props': require('./require-default-props'),
+  'require-optimization': require('./require-optimization'),
+  'require-render-return': require('./require-render-return'),
+  'self-closing-comp': require('./self-closing-comp'),
+  'sort-comp': require('./sort-comp'),
+  'sort-default-props': require('./sort-default-props'),
+  'sort-prop-types': require('./sort-prop-types'),
+  'state-in-constructor': require('./state-in-constructor'),
+  'static-property-placement': require('./static-property-placement'),
+  'style-prop-object': require('./style-prop-object'),
+  'void-dom-elements-no-children': require('./void-dom-elements-no-children'),
+};
+
+module.exports = rules;
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-boolean-value.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-boolean-value.d.ts","sourceRoot":"","sources":["jsx-boolean-value.js"],"names":[],"mappings":"wBAwDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,169 @@
+/**
+ * @fileoverview Enforce boolean attributes notation in JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const exceptionsSchema = {
+  type: 'array',
+  items: { type: 'string', minLength: 1 },
+  uniqueItems: true,
+};
+
+const ALWAYS = 'always';
+const NEVER = 'never';
+
+/**
+ * @param {string} configuration
+ * @param {Set<string>} exceptions
+ * @param {string} propName
+ * @returns {boolean} propName
+ */
+function isAlways(configuration, exceptions, propName) {
+  const isException = exceptions.has(propName);
+  if (configuration === ALWAYS) {
+    return !isException;
+  }
+  return isException;
+}
+/**
+ * @param {string} configuration
+ * @param {Set<string>} exceptions
+ * @param {string} propName
+ * @returns {boolean} propName
+ */
+function isNever(configuration, exceptions, propName) {
+  const isException = exceptions.has(propName);
+  if (configuration === NEVER) {
+    return !isException;
+  }
+  return isException;
+}
+
+const messages = {
+  omitBoolean: 'Value must be omitted for boolean attribute `{{propName}}`',
+  setBoolean: 'Value must be set for boolean attribute `{{propName}}`',
+  omitPropAndBoolean: 'Value must be omitted for `false` attribute: `{{propName}}`',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce boolean attributes notation in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-boolean-value'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: {
+      anyOf: [{
+        type: 'array',
+        items: [{ enum: [ALWAYS, NEVER] }],
+        additionalItems: false,
+      }, {
+        type: 'array',
+        items: [{
+          enum: [ALWAYS],
+        }, {
+          type: 'object',
+          additionalProperties: false,
+          properties: {
+            [NEVER]: exceptionsSchema,
+            assumeUndefinedIsFalse: {
+              type: 'boolean',
+            },
+          },
+        }],
+        additionalItems: false,
+      }, {
+        type: 'array',
+        items: [{
+          enum: [NEVER],
+        }, {
+          type: 'object',
+          additionalProperties: false,
+          properties: {
+            [ALWAYS]: exceptionsSchema,
+            assumeUndefinedIsFalse: {
+              type: 'boolean',
+            },
+          },
+        }],
+        additionalItems: false,
+      }],
+    },
+  },
+
+  create(context) {
+    const configuration = context.options[0] || NEVER;
+    const configObject = context.options[1] || {};
+    const exceptions = new Set((configuration === ALWAYS ? configObject[NEVER] : configObject[ALWAYS]) || []);
+
+    return {
+      JSXAttribute(node) {
+        const propName = node.name && node.name.name;
+        const value = node.value;
+
+        if (
+          isAlways(configuration, exceptions, propName)
+          && value === null
+        ) {
+          const messageId = 'setBoolean';
+          const data = { propName };
+          report(context, messages[messageId], messageId, {
+            node,
+            data,
+            fix(fixer) {
+              return fixer.insertTextAfter(node, '={true}');
+            },
+          });
+        }
+        if (
+          isNever(configuration, exceptions, propName)
+          && value
+          && value.type === 'JSXExpressionContainer'
+          && value.expression.value === true
+        ) {
+          const messageId = 'omitBoolean';
+          const data = { propName };
+          report(context, messages[messageId], messageId, {
+            node,
+            data,
+            fix(fixer) {
+              return fixer.removeRange([node.name.range[1], value.range[1]]);
+            },
+          });
+        }
+        if (
+          isNever(configuration, exceptions, propName)
+          && configObject.assumeUndefinedIsFalse
+          && value
+          && value.type === 'JSXExpressionContainer'
+          && value.expression.value === false
+        ) {
+          const messageId = 'omitPropAndBoolean';
+          const data = { propName };
+          report(context, messages[messageId], messageId, {
+            node,
+            data,
+            fix(fixer) {
+              return fixer.removeRange([node.name.range[0], value.range[1]]);
+            },
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-child-element-spacing.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-child-element-spacing.d.ts","sourceRoot":"","sources":["jsx-child-element-spacing.js"],"names":[],"mappings":"wBA8CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-child-element-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,117 @@
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// This list is taken from https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
+
+// Note: 'br' is not included because whitespace around br tags is inconsequential to the rendered output
+const INLINE_ELEMENTS = new Set([
+  'a',
+  'abbr',
+  'acronym',
+  'b',
+  'bdo',
+  'big',
+  'button',
+  'cite',
+  'code',
+  'dfn',
+  'em',
+  'i',
+  'img',
+  'input',
+  'kbd',
+  'label',
+  'map',
+  'object',
+  'q',
+  'samp',
+  'script',
+  'select',
+  'small',
+  'span',
+  'strong',
+  'sub',
+  'sup',
+  'textarea',
+  'tt',
+  'var',
+]);
+
+const messages = {
+  spacingAfterPrev: 'Ambiguous spacing after previous element {{element}}',
+  spacingBeforeNext: 'Ambiguous spacing before next element {{element}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce or disallow spaces inside of curly braces in JSX attributes and expressions',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-child-element-spacing'),
+    },
+    fixable: null,
+
+    messages,
+
+    schema: [],
+  },
+  create(context) {
+    const TEXT_FOLLOWING_ELEMENT_PATTERN = /^\s*\n\s*\S/;
+    const TEXT_PRECEDING_ELEMENT_PATTERN = /\S\s*\n\s*$/;
+
+    const elementName = (node) => (
+      node.openingElement
+      && node.openingElement.name
+      && node.openingElement.name.type === 'JSXIdentifier'
+      && node.openingElement.name.name
+    );
+
+    const isInlineElement = (node) => (
+      node.type === 'JSXElement'
+      && INLINE_ELEMENTS.has(elementName(node))
+    );
+
+    const handleJSX = (node) => {
+      let lastChild = null;
+      let child = null;
+      (node.children.concat([null])).forEach((nextChild) => {
+        if (
+          (lastChild || nextChild)
+          && (!lastChild || isInlineElement(lastChild))
+          && (child && (child.type === 'Literal' || child.type === 'JSXText'))
+          && (!nextChild || isInlineElement(nextChild))
+          && true
+        ) {
+          if (lastChild && child.value.match(TEXT_FOLLOWING_ELEMENT_PATTERN)) {
+            report(context, messages.spacingAfterPrev, 'spacingAfterPrev', {
+              node: lastChild,
+              loc: lastChild.loc.end,
+              data: {
+                element: elementName(lastChild),
+              },
+            });
+          } else if (nextChild && child.value.match(TEXT_PRECEDING_ELEMENT_PATTERN)) {
+            report(context, messages.spacingBeforeNext, 'spacingBeforeNext', {
+              node: nextChild,
+              loc: nextChild.loc.start,
+              data: {
+                element: elementName(nextChild),
+              },
+            });
+          }
+        }
+        lastChild = child;
+        child = nextChild;
+      });
+    };
+
+    return {
+      JSXElement: handleJSX,
+      JSXFragment: handleJSX,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-closing-bracket-location.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-closing-bracket-location.d.ts","sourceRoot":"","sources":["jsx-closing-bracket-location.js"],"names":[],"mappings":"wBAsBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,315 @@
+/**
+ * @fileoverview Validate closing bracket location in JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const has = require('hasown');
+const repeat = require('string.prototype.repeat');
+
+const docsUrl = require('../util/docsUrl');
+const getSourceCode = require('../util/eslint').getSourceCode;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  bracketLocation: 'The closing bracket must be {{location}}{{details}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce closing bracket location in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-closing-bracket-location'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      anyOf: [
+        {
+          enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned'],
+        },
+        {
+          type: 'object',
+          properties: {
+            location: {
+              enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned'],
+            },
+          },
+          additionalProperties: false,
+        }, {
+          type: 'object',
+          properties: {
+            nonEmpty: {
+              enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned', false],
+            },
+            selfClosing: {
+              enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned', false],
+            },
+          },
+          additionalProperties: false,
+        },
+      ],
+    }],
+  },
+
+  create(context) {
+    const MESSAGE_LOCATION = {
+      'after-props': 'placed after the last prop',
+      'after-tag': 'placed after the opening tag',
+      'props-aligned': 'aligned with the last prop',
+      'tag-aligned': 'aligned with the opening tag',
+      'line-aligned': 'aligned with the line containing the opening tag',
+    };
+    const DEFAULT_LOCATION = 'tag-aligned';
+
+    const config = context.options[0];
+    const options = {
+      nonEmpty: DEFAULT_LOCATION,
+      selfClosing: DEFAULT_LOCATION,
+    };
+
+    if (typeof config === 'string') {
+      // simple shorthand [1, 'something']
+      options.nonEmpty = config;
+      options.selfClosing = config;
+    } else if (typeof config === 'object') {
+      // [1, {location: 'something'}] (back-compat)
+      if (has(config, 'location')) {
+        options.nonEmpty = config.location;
+        options.selfClosing = config.location;
+      }
+      // [1, {nonEmpty: 'something'}]
+      if (has(config, 'nonEmpty')) {
+        options.nonEmpty = config.nonEmpty;
+      }
+      // [1, {selfClosing: 'something'}]
+      if (has(config, 'selfClosing')) {
+        options.selfClosing = config.selfClosing;
+      }
+    }
+
+    /**
+     * Get expected location for the closing bracket
+     * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+     * @return {string} Expected location for the closing bracket
+     */
+    function getExpectedLocation(tokens) {
+      let location;
+      // Is always after the opening tag if there is no props
+      if (typeof tokens.lastProp === 'undefined') {
+        location = 'after-tag';
+      // Is always after the last prop if this one is on the same line as the opening bracket
+      } else if (tokens.opening.line === tokens.lastProp.lastLine) {
+        location = 'after-props';
+      // Else use configuration dependent on selfClosing property
+      } else {
+        location = tokens.selfClosing ? options.selfClosing : options.nonEmpty;
+      }
+      return location;
+    }
+
+    /**
+     * Get the correct 0-indexed column for the closing bracket, given the
+     * expected location.
+     * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+     * @param {string} expectedLocation Expected location for the closing bracket
+     * @return {?Number} The correct column for the closing bracket, or null
+     */
+    function getCorrectColumn(tokens, expectedLocation) {
+      switch (expectedLocation) {
+        case 'props-aligned':
+          return tokens.lastProp.column;
+        case 'tag-aligned':
+          return tokens.opening.column;
+        case 'line-aligned':
+          return tokens.openingStartOfLine.column;
+        default:
+          return null;
+      }
+    }
+
+    /**
+     * Check if the closing bracket is correctly located
+     * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+     * @param {string} expectedLocation Expected location for the closing bracket
+     * @return {boolean} True if the closing bracket is correctly located, false if not
+     */
+    function hasCorrectLocation(tokens, expectedLocation) {
+      switch (expectedLocation) {
+        case 'after-tag':
+          return tokens.tag.line === tokens.closing.line;
+        case 'after-props':
+          return tokens.lastProp.lastLine === tokens.closing.line;
+        case 'props-aligned':
+        case 'tag-aligned':
+        case 'line-aligned': {
+          const correctColumn = getCorrectColumn(tokens, expectedLocation);
+          return correctColumn === tokens.closing.column;
+        }
+        default:
+          return true;
+      }
+    }
+
+    /**
+     * Get the characters used for indentation on the line to be matched
+     * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+     * @param {string} expectedLocation Expected location for the closing bracket
+     * @param {number} [correctColumn] Expected column for the closing bracket. Default to 0
+     * @return {string} The characters used for indentation
+     */
+    function getIndentation(tokens, expectedLocation, correctColumn) {
+      const newColumn = correctColumn || 0;
+      let indentation;
+      let spaces = '';
+      switch (expectedLocation) {
+        case 'props-aligned':
+          indentation = /^\s*/.exec(getSourceCode(context).lines[tokens.lastProp.firstLine - 1])[0];
+          break;
+        case 'tag-aligned':
+        case 'line-aligned':
+          indentation = /^\s*/.exec(getSourceCode(context).lines[tokens.opening.line - 1])[0];
+          break;
+        default:
+          indentation = '';
+      }
+      if (indentation.length + 1 < newColumn) {
+        // Non-whitespace characters were included in the column offset
+        spaces = repeat(' ', +correctColumn - indentation.length);
+      }
+      return indentation + spaces;
+    }
+
+    /**
+     * Get the locations of the opening bracket, closing bracket, last prop, and
+     * start of opening line.
+     * @param {ASTNode} node The node to check
+     * @return {Object} Locations of the opening bracket, closing bracket, last
+     * prop and start of opening line.
+     */
+    function getTokensLocations(node) {
+      const sourceCode = getSourceCode(context);
+      const opening = sourceCode.getFirstToken(node).loc.start;
+      const closing = sourceCode.getLastTokens(node, node.selfClosing ? 2 : 1)[0].loc.start;
+      const tag = sourceCode.getFirstToken(node.name).loc.start;
+      let lastProp;
+      if (node.attributes.length) {
+        lastProp = node.attributes[node.attributes.length - 1];
+        lastProp = {
+          column: sourceCode.getFirstToken(lastProp).loc.start.column,
+          firstLine: sourceCode.getFirstToken(lastProp).loc.start.line,
+          lastLine: sourceCode.getLastToken(lastProp).loc.end.line,
+        };
+      }
+      const openingLine = sourceCode.lines[opening.line - 1];
+      const closingLine = sourceCode.lines[closing.line - 1];
+      const isTab = {
+        openTab: /^\t/.test(openingLine),
+        closeTab: /^\t/.test(closingLine),
+      };
+      const openingStartOfLine = {
+        column: /^\s*/.exec(openingLine)[0].length,
+        line: opening.line,
+      };
+      return {
+        isTab,
+        tag,
+        opening,
+        closing,
+        lastProp,
+        selfClosing: node.selfClosing,
+        openingStartOfLine,
+      };
+    }
+
+    /**
+     * Get an unique ID for a given JSXOpeningElement
+     *
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {string} Unique ID (based on its range)
+     */
+    function getOpeningElementId(node) {
+      return node.range.join(':');
+    }
+
+    const lastAttributeNode = {};
+
+    return {
+      JSXAttribute(node) {
+        lastAttributeNode[getOpeningElementId(node.parent)] = node;
+      },
+
+      JSXSpreadAttribute(node) {
+        lastAttributeNode[getOpeningElementId(node.parent)] = node;
+      },
+
+      'JSXOpeningElement:exit'(node) {
+        const attributeNode = lastAttributeNode[getOpeningElementId(node)];
+        const cachedLastAttributeEndPos = attributeNode ? attributeNode.range[1] : null;
+
+        let expectedNextLine;
+        const tokens = getTokensLocations(node);
+        const expectedLocation = getExpectedLocation(tokens);
+        let usingSameIndentation = true;
+
+        if (expectedLocation === 'tag-aligned') {
+          usingSameIndentation = tokens.isTab.openTab === tokens.isTab.closeTab;
+        }
+
+        if (hasCorrectLocation(tokens, expectedLocation) && usingSameIndentation) {
+          return;
+        }
+
+        const data = {
+          location: MESSAGE_LOCATION[expectedLocation],
+          details: '',
+        };
+        const correctColumn = getCorrectColumn(tokens, expectedLocation);
+
+        if (correctColumn !== null) {
+          expectedNextLine = tokens.lastProp
+            && (tokens.lastProp.lastLine === tokens.closing.line);
+          data.details = ` (expected column ${correctColumn + 1}${expectedNextLine ? ' on the next line)' : ')'}`;
+        }
+
+        report(context, messages.bracketLocation, 'bracketLocation', {
+          node,
+          loc: tokens.closing,
+          data,
+          fix(fixer) {
+            const closingTag = tokens.selfClosing ? '/>' : '>';
+            switch (expectedLocation) {
+              case 'after-tag':
+                if (cachedLastAttributeEndPos) {
+                  return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]],
+                    (expectedNextLine ? '\n' : '') + closingTag);
+                }
+                return fixer.replaceTextRange([node.name.range[1], node.range[1]],
+                  (expectedNextLine ? '\n' : ' ') + closingTag);
+              case 'after-props':
+                return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]],
+                  (expectedNextLine ? '\n' : '') + closingTag);
+              case 'props-aligned':
+              case 'tag-aligned':
+              case 'line-aligned':
+                return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]],
+                  `\n${getIndentation(tokens, expectedLocation, correctColumn)}${closingTag}`);
+              default:
+                return true;
+            }
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-closing-tag-location.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-closing-tag-location.d.ts","sourceRoot":"","sources":["jsx-closing-tag-location.js"],"names":[],"mappings":"wBAgCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-closing-tag-location.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,142 @@
+/**
+ * @fileoverview Validate closing tag location in JSX
+ * @author Ross Solomon
+ */
+
+'use strict';
+
+const repeat = require('string.prototype.repeat');
+const has = require('hasown');
+
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const getSourceCode = require('../util/eslint').getSourceCode;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  onOwnLine: 'Closing tag of a multiline JSX expression must be on its own line.',
+  matchIndent: 'Expected closing tag to match indentation of opening.',
+  alignWithOpening: 'Expected closing tag to be aligned with the line containing the opening tag',
+};
+
+const defaultOption = 'tag-aligned';
+
+const optionMessageMap = {
+  'tag-aligned': 'matchIndent',
+  'line-aligned': 'alignWithOpening',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce closing tag location for multiline JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-closing-tag-location'),
+    },
+    fixable: 'whitespace',
+    messages,
+    schema: [{
+      anyOf: [
+        {
+          enum: ['tag-aligned', 'line-aligned'],
+        },
+        {
+          type: 'object',
+          properties: {
+            location: {
+              enum: ['tag-aligned', 'line-aligned'],
+            },
+          },
+          additionalProperties: false,
+        },
+      ],
+    }],
+  },
+
+  create(context) {
+    const config = context.options[0];
+    let option = defaultOption;
+
+    if (typeof config === 'string') {
+      option = config;
+    } else if (typeof config === 'object') {
+      if (has(config, 'location')) {
+        option = config.location;
+      }
+    }
+
+    function getIndentation(openingStartOfLine, opening) {
+      if (option === 'line-aligned') return openingStartOfLine.column;
+      if (option === 'tag-aligned') return opening.loc.start.column;
+    }
+
+    function handleClosingElement(node) {
+      if (!node.parent) {
+        return;
+      }
+      const sourceCode = getSourceCode(context);
+
+      const opening = node.parent.openingElement || node.parent.openingFragment;
+      const openingLoc = sourceCode.getFirstToken(opening).loc.start;
+      const openingLine = sourceCode.lines[openingLoc.line - 1];
+
+      const openingStartOfLine = {
+        column: /^\s*/.exec(openingLine)[0].length,
+        line: openingLoc.line,
+      };
+
+      if (opening.loc.start.line === node.loc.start.line) {
+        return;
+      }
+
+      if (
+        opening.loc.start.column === node.loc.start.column
+        && option === 'tag-aligned'
+      ) {
+        return;
+      }
+
+      if (
+        openingStartOfLine.column === node.loc.start.column
+        && option === 'line-aligned'
+      ) {
+        return;
+      }
+
+      const messageId = astUtil.isNodeFirstInLine(context, node)
+        ? optionMessageMap[option]
+        : 'onOwnLine';
+
+      report(context, messages[messageId], messageId, {
+        node,
+        loc: node.loc,
+        fix(fixer) {
+          const indent = repeat(
+            ' ',
+            getIndentation(openingStartOfLine, opening)
+          );
+
+          if (astUtil.isNodeFirstInLine(context, node)) {
+            return fixer.replaceTextRange(
+              [node.range[0] - node.loc.start.column, node.range[0]],
+              indent
+            );
+          }
+
+          return fixer.insertTextBefore(node, `\n${indent}`);
+        },
+      });
+    }
+
+    return {
+      JSXClosingElement: handleClosingElement,
+      JSXClosingFragment: handleClosingElement,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-curly-brace-presence.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-curly-brace-presence.d.ts","sourceRoot":"","sources":["jsx-curly-brace-presence.js"],"names":[],"mappings":"wBAgJW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-brace-presence.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,421 @@
+/**
+ * @fileoverview Enforce curly braces or disallow unnecessary curly brace in JSX
+ * @author Jacky Ho
+ * @author Simon Lydell
+ */
+
+'use strict';
+
+const arrayIncludes = require('array-includes');
+
+const docsUrl = require('../util/docsUrl');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const OPTION_ALWAYS = 'always';
+const OPTION_NEVER = 'never';
+const OPTION_IGNORE = 'ignore';
+
+const OPTION_VALUES = [
+  OPTION_ALWAYS,
+  OPTION_NEVER,
+  OPTION_IGNORE,
+];
+const DEFAULT_CONFIG = { props: OPTION_NEVER, children: OPTION_NEVER, propElementValues: OPTION_IGNORE };
+
+const HTML_ENTITY_REGEX = () => /&[A-Za-z\d#]+;/g;
+
+function containsLineTerminators(rawStringValue) {
+  return /[\n\r\u2028\u2029]/.test(rawStringValue);
+}
+
+function containsBackslash(rawStringValue) {
+  return arrayIncludes(rawStringValue, '\\');
+}
+
+function containsHTMLEntity(rawStringValue) {
+  return HTML_ENTITY_REGEX().test(rawStringValue);
+}
+
+function containsOnlyHtmlEntities(rawStringValue) {
+  return rawStringValue.replace(HTML_ENTITY_REGEX(), '').trim() === '';
+}
+
+function containsDisallowedJSXTextChars(rawStringValue) {
+  return /[{<>}]/.test(rawStringValue);
+}
+
+function containsQuoteCharacters(value) {
+  return /['"]/.test(value);
+}
+
+function containsMultilineComment(value) {
+  return /\/\*/.test(value);
+}
+
+function escapeDoubleQuotes(rawStringValue) {
+  return rawStringValue.replace(/\\"/g, '"').replace(/"/g, '\\"');
+}
+
+function escapeBackslashes(rawStringValue) {
+  return rawStringValue.replace(/\\/g, '\\\\');
+}
+
+function needToEscapeCharacterForJSX(raw, node) {
+  return (
+    containsBackslash(raw)
+    || containsHTMLEntity(raw)
+    || (node.parent.type !== 'JSXAttribute' && containsDisallowedJSXTextChars(raw))
+  );
+}
+
+function containsWhitespaceExpression(child) {
+  if (child.type === 'JSXExpressionContainer') {
+    const value = child.expression.value;
+    return value ? jsxUtil.isWhiteSpaces(value) : false;
+  }
+  return false;
+}
+
+function isLineBreak(text) {
+  return containsLineTerminators(text) && text.trim() === '';
+}
+
+function wrapNonHTMLEntities(text) {
+  const HTML_ENTITY = '<HTML_ENTITY>';
+  const withCurlyBraces = text.split(HTML_ENTITY_REGEX()).map((word) => (
+    word === '' ? '' : `{${JSON.stringify(word)}}`
+  )).join(HTML_ENTITY);
+
+  const htmlEntities = text.match(HTML_ENTITY_REGEX());
+  return htmlEntities.reduce((acc, htmlEntity) => (
+    acc.replace(HTML_ENTITY, htmlEntity)
+  ), withCurlyBraces);
+}
+
+function wrapWithCurlyBraces(rawText) {
+  if (!containsLineTerminators(rawText)) {
+    return `{${JSON.stringify(rawText)}}`;
+  }
+
+  return rawText.split('\n').map((line) => {
+    if (line.trim() === '') {
+      return line;
+    }
+    const firstCharIndex = line.search(/[^\s]/);
+    const leftWhitespace = line.slice(0, firstCharIndex);
+    const text = line.slice(firstCharIndex);
+
+    if (containsHTMLEntity(line)) {
+      return `${leftWhitespace}${wrapNonHTMLEntities(text)}`;
+    }
+    return `${leftWhitespace}{${JSON.stringify(text)}}`;
+  }).join('\n');
+}
+
+function isWhiteSpaceLiteral(node) {
+  return node.type && node.type === 'Literal' && node.value && jsxUtil.isWhiteSpaces(node.value);
+}
+
+function isStringWithTrailingWhiteSpaces(value) {
+  return /^\s|\s$/.test(value);
+}
+
+function isLiteralWithTrailingWhiteSpaces(node) {
+  return node.type && node.type === 'Literal' && node.value && isStringWithTrailingWhiteSpaces(node.value);
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  unnecessaryCurly: 'Curly braces are unnecessary here.',
+  missingCurly: 'Need to wrap this literal in a JSX expression.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-curly-brace-presence'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [
+      {
+        anyOf: [
+          {
+            type: 'object',
+            properties: {
+              props: { enum: OPTION_VALUES },
+              children: { enum: OPTION_VALUES },
+              propElementValues: { enum: OPTION_VALUES },
+            },
+            additionalProperties: false,
+          },
+          {
+            enum: OPTION_VALUES,
+          },
+        ],
+      },
+    ],
+  },
+
+  create(context) {
+    const ruleOptions = context.options[0];
+    const userConfig = typeof ruleOptions === 'string'
+      ? { props: ruleOptions, children: ruleOptions, propElementValues: OPTION_IGNORE }
+      : Object.assign({}, DEFAULT_CONFIG, ruleOptions);
+
+    /**
+     * Report and fix an unnecessary curly brace violation on a node
+     * @param {ASTNode} JSXExpressionNode - The AST node with an unnecessary JSX expression
+     */
+    function reportUnnecessaryCurly(JSXExpressionNode) {
+      report(context, messages.unnecessaryCurly, 'unnecessaryCurly', {
+        node: JSXExpressionNode,
+        fix(fixer) {
+          const expression = JSXExpressionNode.expression;
+
+          let textToReplace;
+          if (jsxUtil.isJSX(expression)) {
+            textToReplace = getText(context, expression);
+          } else {
+            const expressionType = expression && expression.type;
+            const parentType = JSXExpressionNode.parent.type;
+
+            if (parentType === 'JSXAttribute') {
+              if (expressionType !== 'TemplateLiteral' && /["]/.test(expression.raw.slice(1, -1))) {
+                textToReplace = expression.raw;
+              } else {
+                textToReplace = `"${expressionType === 'TemplateLiteral'
+                  ? expression.quasis[0].value.raw
+                  : expression.raw.slice(1, -1)
+                }"`;
+              }
+            } else if (jsxUtil.isJSX(expression)) {
+              textToReplace = getText(context, expression);
+            } else {
+              textToReplace = expressionType === 'TemplateLiteral'
+                ? expression.quasis[0].value.cooked : expression.value;
+            }
+          }
+
+          return fixer.replaceText(JSXExpressionNode, textToReplace);
+        },
+      });
+    }
+
+    function reportMissingCurly(literalNode) {
+      report(context, messages.missingCurly, 'missingCurly', {
+        node: literalNode,
+        fix(fixer) {
+          if (jsxUtil.isJSX(literalNode)) {
+            return fixer.replaceText(literalNode, `{${getText(context, literalNode)}}`);
+          }
+
+          // If a HTML entity name is found, bail out because it can be fixed
+          // by either using the real character or the unicode equivalent.
+          // If it contains any line terminator character, bail out as well.
+          if (
+            containsOnlyHtmlEntities(literalNode.raw)
+            || (literalNode.parent.type === 'JSXAttribute' && containsLineTerminators(literalNode.raw))
+            || isLineBreak(literalNode.raw)
+          ) {
+            return null;
+          }
+
+          const expression = literalNode.parent.type === 'JSXAttribute'
+            ? `{"${escapeDoubleQuotes(escapeBackslashes(
+              literalNode.raw.slice(1, -1)
+            ))}"}`
+            : wrapWithCurlyBraces(literalNode.raw);
+
+          return fixer.replaceText(literalNode, expression);
+        },
+      });
+    }
+
+    // Bail out if there is any character that needs to be escaped in JSX
+    // because escaping decreases readability and the original code may be more
+    // readable anyway or intentional for other specific reasons
+    function lintUnnecessaryCurly(JSXExpressionNode) {
+      const expression = JSXExpressionNode.expression;
+      const expressionType = expression.type;
+
+      const sourceCode = getSourceCode(context);
+      // Curly braces containing comments are necessary
+      if (sourceCode.getCommentsInside && sourceCode.getCommentsInside(JSXExpressionNode).length > 0) {
+        return;
+      }
+
+      if (
+        (expressionType === 'Literal' || expressionType === 'JSXText')
+          && typeof expression.value === 'string'
+          && (
+            (JSXExpressionNode.parent.type === 'JSXAttribute' && !isWhiteSpaceLiteral(expression))
+            || !isLiteralWithTrailingWhiteSpaces(expression)
+          )
+          && !containsMultilineComment(expression.value)
+          && !needToEscapeCharacterForJSX(expression.raw, JSXExpressionNode) && (
+          jsxUtil.isJSX(JSXExpressionNode.parent)
+          || (!containsQuoteCharacters(expression.value) || typeof expression.value === 'string')
+        )
+      ) {
+        reportUnnecessaryCurly(JSXExpressionNode);
+      } else if (
+        expressionType === 'TemplateLiteral'
+        && expression.expressions.length === 0
+        && expression.quasis[0].value.raw.indexOf('\n') === -1
+        && !isStringWithTrailingWhiteSpaces(expression.quasis[0].value.raw)
+        && !needToEscapeCharacterForJSX(expression.quasis[0].value.raw, JSXExpressionNode)
+        && !containsQuoteCharacters(expression.quasis[0].value.cooked)
+      ) {
+        reportUnnecessaryCurly(JSXExpressionNode);
+      } else if (jsxUtil.isJSX(expression)) {
+        reportUnnecessaryCurly(JSXExpressionNode);
+      }
+    }
+
+    function areRuleConditionsSatisfied(parent, config, ruleCondition) {
+      return (
+        parent.type === 'JSXAttribute'
+          && typeof config.props === 'string'
+          && config.props === ruleCondition
+      ) || (
+        jsxUtil.isJSX(parent)
+          && typeof config.children === 'string'
+          && config.children === ruleCondition
+      );
+    }
+
+    function getAdjacentSiblings(node, children) {
+      for (let i = 1; i < children.length - 1; i++) {
+        const child = children[i];
+        if (node === child) {
+          return [children[i - 1], children[i + 1]];
+        }
+      }
+      if (node === children[0] && children[1]) {
+        return [children[1]];
+      }
+      if (node === children[children.length - 1] && children[children.length - 2]) {
+        return [children[children.length - 2]];
+      }
+      return [];
+    }
+
+    function hasAdjacentJsxExpressionContainers(node, children) {
+      if (!children) {
+        return false;
+      }
+      const childrenExcludingWhitespaceLiteral = children.filter((child) => !isWhiteSpaceLiteral(child));
+      const adjSiblings = getAdjacentSiblings(node, childrenExcludingWhitespaceLiteral);
+
+      return adjSiblings.some((x) => x.type && x.type === 'JSXExpressionContainer');
+    }
+    function hasAdjacentJsx(node, children) {
+      if (!children) {
+        return false;
+      }
+      const childrenExcludingWhitespaceLiteral = children.filter((child) => !isWhiteSpaceLiteral(child));
+      const adjSiblings = getAdjacentSiblings(node, childrenExcludingWhitespaceLiteral);
+
+      return adjSiblings.some((x) => x.type && arrayIncludes(['JSXExpressionContainer', 'JSXElement'], x.type));
+    }
+    function shouldCheckForUnnecessaryCurly(node, config) {
+      const parent = node.parent;
+      // Bail out if the parent is a JSXAttribute & its contents aren't
+      // StringLiteral or TemplateLiteral since e.g
+      // <App prop1={<CustomEl />} prop2={<CustomEl>...</CustomEl>} />
+
+      if (
+        parent.type && parent.type === 'JSXAttribute'
+        && (node.expression && node.expression.type
+          && node.expression.type !== 'Literal'
+          && node.expression.type !== 'StringLiteral'
+          && node.expression.type !== 'TemplateLiteral')
+      ) {
+        return false;
+      }
+
+      // If there are adjacent `JsxExpressionContainer` then there is no need,
+      // to check for unnecessary curly braces.
+      if (jsxUtil.isJSX(parent) && hasAdjacentJsxExpressionContainers(node, parent.children)) {
+        return false;
+      }
+      if (containsWhitespaceExpression(node) && hasAdjacentJsx(node, parent.children)) {
+        return false;
+      }
+      if (
+        parent.children
+        && parent.children.length === 1
+        && containsWhitespaceExpression(node)
+      ) {
+        return false;
+      }
+
+      return areRuleConditionsSatisfied(parent, config, OPTION_NEVER);
+    }
+
+    function shouldCheckForMissingCurly(node, config) {
+      if (jsxUtil.isJSX(node)) {
+        return config.propElementValues !== OPTION_IGNORE;
+      }
+      if (
+        isLineBreak(node.raw)
+        || containsOnlyHtmlEntities(node.raw)
+      ) {
+        return false;
+      }
+      const parent = node.parent;
+      if (
+        parent.children
+        && parent.children.length === 1
+        && containsWhitespaceExpression(parent.children[0])
+      ) {
+        return false;
+      }
+
+      return areRuleConditionsSatisfied(parent, config, OPTION_ALWAYS);
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      'JSXAttribute > JSXExpressionContainer > JSXElement'(node) {
+        if (userConfig.propElementValues === OPTION_NEVER) {
+          reportUnnecessaryCurly(node.parent);
+        }
+      },
+
+      JSXExpressionContainer(node) {
+        if (shouldCheckForUnnecessaryCurly(node, userConfig)) {
+          lintUnnecessaryCurly(node);
+        }
+      },
+
+      'JSXAttribute > JSXElement, Literal, JSXText'(node) {
+        if (shouldCheckForMissingCurly(node, userConfig)) {
+          reportMissingCurly(node);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-curly-newline.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-curly-newline.d.ts","sourceRoot":"","sources":["jsx-curly-newline.js"],"names":[],"mappings":"wBA+CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-newline.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,187 @@
+/**
+ * @fileoverview enforce consistent line breaks inside jsx curly
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const eslintUtil = require('../util/eslint');
+const report = require('../util/report');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function getNormalizedOption(context) {
+  const rawOption = context.options[0] || 'consistent';
+
+  if (rawOption === 'consistent') {
+    return {
+      multiline: 'consistent',
+      singleline: 'consistent',
+    };
+  }
+
+  if (rawOption === 'never') {
+    return {
+      multiline: 'forbid',
+      singleline: 'forbid',
+    };
+  }
+
+  return {
+    multiline: rawOption.multiline || 'consistent',
+    singleline: rawOption.singleline || 'consistent',
+  };
+}
+
+const messages = {
+  expectedBefore: 'Expected newline before \'}\'.',
+  expectedAfter: 'Expected newline after \'{\'.',
+  unexpectedBefore: 'Unexpected newline before \'}\'.',
+  unexpectedAfter: 'Unexpected newline after \'{\'.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    type: 'layout',
+
+    docs: {
+      description: 'Enforce consistent linebreaks in curly braces in JSX attributes and expressions',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-curly-newline'),
+    },
+
+    fixable: 'whitespace',
+
+    schema: [
+      {
+        anyOf: [
+          {
+            enum: ['consistent', 'never'],
+          },
+          {
+            type: 'object',
+            properties: {
+              singleline: { enum: ['consistent', 'require', 'forbid'] },
+              multiline: { enum: ['consistent', 'require', 'forbid'] },
+            },
+            additionalProperties: false,
+          },
+        ],
+      },
+    ],
+
+    messages,
+  },
+
+  create(context) {
+    const sourceCode = getSourceCode(context);
+    const option = getNormalizedOption(context);
+
+    // ----------------------------------------------------------------------
+    // Helpers
+    // ----------------------------------------------------------------------
+
+    /**
+     * Determines whether two adjacent tokens are on the same line.
+     * @param {Object} left - The left token object.
+     * @param {Object} right - The right token object.
+     * @returns {boolean} Whether or not the tokens are on the same line.
+     */
+    function isTokenOnSameLine(left, right) {
+      return left.loc.end.line === right.loc.start.line;
+    }
+
+    /**
+     * Determines whether there should be newlines inside curlys
+     * @param {ASTNode} expression The expression contained in the curlys
+     * @param {boolean} hasLeftNewline `true` if the left curly has a newline in the current code.
+     * @returns {boolean} `true` if there should be newlines inside the function curlys
+     */
+    function shouldHaveNewlines(expression, hasLeftNewline) {
+      const isMultiline = expression.loc.start.line !== expression.loc.end.line;
+
+      switch (isMultiline ? option.multiline : option.singleline) {
+        case 'forbid': return false;
+        case 'require': return true;
+        case 'consistent':
+        default: return hasLeftNewline;
+      }
+    }
+
+    /**
+     * Validates curlys
+     * @param {Object} curlys An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
+     * @param {ASTNode} expression The expression inside the curly
+     * @returns {void}
+     */
+    function validateCurlys(curlys, expression) {
+      const leftCurly = curlys.leftCurly;
+      const rightCurly = curlys.rightCurly;
+      const tokenAfterLeftCurly = sourceCode.getTokenAfter(leftCurly);
+      const tokenBeforeRightCurly = sourceCode.getTokenBefore(rightCurly);
+      const hasLeftNewline = !isTokenOnSameLine(leftCurly, tokenAfterLeftCurly);
+      const hasRightNewline = !isTokenOnSameLine(tokenBeforeRightCurly, rightCurly);
+      const needsNewlines = shouldHaveNewlines(expression, hasLeftNewline);
+
+      if (hasLeftNewline && !needsNewlines) {
+        report(context, messages.unexpectedAfter, 'unexpectedAfter', {
+          node: leftCurly,
+          fix(fixer) {
+            return getText(context)
+              .slice(leftCurly.range[1], tokenAfterLeftCurly.range[0])
+              .trim()
+              ? null // If there is a comment between the { and the first element, don't do a fix.
+              : fixer.removeRange([leftCurly.range[1], tokenAfterLeftCurly.range[0]]);
+          },
+        });
+      } else if (!hasLeftNewline && needsNewlines) {
+        report(context, messages.expectedAfter, 'expectedAfter', {
+          node: leftCurly,
+          fix: (fixer) => fixer.insertTextAfter(leftCurly, '\n'),
+        });
+      }
+
+      if (hasRightNewline && !needsNewlines) {
+        report(context, messages.unexpectedBefore, 'unexpectedBefore', {
+          node: rightCurly,
+          fix(fixer) {
+            return getText(context)
+              .slice(tokenBeforeRightCurly.range[1], rightCurly.range[0])
+              .trim()
+              ? null // If there is a comment between the last element and the }, don't do a fix.
+              : fixer.removeRange([
+                tokenBeforeRightCurly.range[1],
+                rightCurly.range[0],
+              ]);
+          },
+        });
+      } else if (!hasRightNewline && needsNewlines) {
+        report(context, messages.expectedBefore, 'expectedBefore', {
+          node: rightCurly,
+          fix: (fixer) => fixer.insertTextBefore(rightCurly, '\n'),
+        });
+      }
+    }
+
+    // ----------------------------------------------------------------------
+    // Public
+    // ----------------------------------------------------------------------
+
+    return {
+      JSXExpressionContainer(node) {
+        const curlyTokens = {
+          leftCurly: sourceCode.getFirstToken(node),
+          rightCurly: sourceCode.getLastToken(node),
+        };
+        validateCurlys(curlyTokens, node.expression);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-curly-spacing.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-curly-spacing.d.ts","sourceRoot":"","sources":["jsx-curly-spacing.js"],"names":[],"mappings":"wBAqCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,432 @@
+/**
+ * @fileoverview Enforce or disallow spaces inside of curly braces in JSX attributes.
+ * @author Jamund Ferguson
+ * @author Brandyn Bennett
+ * @author Michael Ficarra
+ * @author Vignesh Anand
+ * @author Jamund Ferguson
+ * @author Yannick Croissant
+ * @author Erik Wendel
+ */
+
+'use strict';
+
+const has = require('hasown');
+const docsUrl = require('../util/docsUrl');
+const getSourceCode = require('../util/eslint').getSourceCode;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const SPACING = {
+  always: 'always',
+  never: 'never',
+};
+const SPACING_VALUES = [SPACING.always, SPACING.never];
+
+const messages = {
+  noNewlineAfter: 'There should be no newline after \'{{token}}\'',
+  noNewlineBefore: 'There should be no newline before \'{{token}}\'',
+  noSpaceAfter: 'There should be no space after \'{{token}}\'',
+  noSpaceBefore: 'There should be no space before \'{{token}}\'',
+  spaceNeededAfter: 'A space is required after \'{{token}}\'',
+  spaceNeededBefore: 'A space is required before \'{{token}}\'',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce or disallow spaces inside of curly braces in JSX attributes and expressions',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-curly-spacing'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: {
+      definitions: {
+        basicConfig: {
+          type: 'object',
+          properties: {
+            when: {
+              enum: SPACING_VALUES,
+            },
+            allowMultiline: {
+              type: 'boolean',
+            },
+            spacing: {
+              type: 'object',
+              properties: {
+                objectLiterals: {
+                  enum: SPACING_VALUES,
+                },
+              },
+            },
+          },
+        },
+        basicConfigOrBoolean: {
+          anyOf: [{
+            $ref: '#/definitions/basicConfig',
+          }, {
+            type: 'boolean',
+          }],
+        },
+      },
+      type: 'array',
+      items: [{
+        anyOf: [{
+          allOf: [{
+            $ref: '#/definitions/basicConfig',
+          }, {
+            type: 'object',
+            properties: {
+              attributes: {
+                $ref: '#/definitions/basicConfigOrBoolean',
+              },
+              children: {
+                $ref: '#/definitions/basicConfigOrBoolean',
+              },
+            },
+          }],
+        }, {
+          enum: SPACING_VALUES,
+        }],
+      }, {
+        type: 'object',
+        properties: {
+          allowMultiline: {
+            type: 'boolean',
+          },
+          spacing: {
+            type: 'object',
+            properties: {
+              objectLiterals: {
+                enum: SPACING_VALUES,
+              },
+            },
+          },
+        },
+        additionalProperties: false,
+      }],
+    },
+  },
+
+  create(context) {
+    function normalizeConfig(configOrTrue, defaults, lastPass) {
+      const config = configOrTrue === true ? {} : configOrTrue;
+      const when = config.when || defaults.when;
+      const allowMultiline = has(config, 'allowMultiline') ? config.allowMultiline : defaults.allowMultiline;
+      const spacing = config.spacing || {};
+      let objectLiteralSpaces = spacing.objectLiterals || defaults.objectLiteralSpaces;
+      if (lastPass) {
+        // On the final pass assign the values that should be derived from others if they are still undefined
+        objectLiteralSpaces = objectLiteralSpaces || when;
+      }
+
+      return {
+        when,
+        allowMultiline,
+        objectLiteralSpaces,
+      };
+    }
+
+    const DEFAULT_WHEN = SPACING.never;
+    const DEFAULT_ALLOW_MULTILINE = true;
+    const DEFAULT_ATTRIBUTES = true;
+    const DEFAULT_CHILDREN = false;
+
+    let originalConfig = context.options[0] || {};
+    if (SPACING_VALUES.indexOf(originalConfig) !== -1) {
+      originalConfig = Object.assign({ when: context.options[0] }, context.options[1]);
+    }
+    const defaultConfig = normalizeConfig(originalConfig, {
+      when: DEFAULT_WHEN,
+      allowMultiline: DEFAULT_ALLOW_MULTILINE,
+    });
+    const attributes = has(originalConfig, 'attributes') ? originalConfig.attributes : DEFAULT_ATTRIBUTES;
+    const attributesConfig = attributes ? normalizeConfig(attributes, defaultConfig, true) : null;
+    const children = has(originalConfig, 'children') ? originalConfig.children : DEFAULT_CHILDREN;
+    const childrenConfig = children ? normalizeConfig(children, defaultConfig, true) : null;
+
+    // --------------------------------------------------------------------------
+    // Helpers
+    // --------------------------------------------------------------------------
+
+    /**
+     * Determines whether two adjacent tokens have a newline between them.
+     * @param {Object} left - The left token object.
+     * @param {Object} right - The right token object.
+     * @returns {boolean} Whether or not there is a newline between the tokens.
+     */
+    function isMultiline(left, right) {
+      return left.loc.end.line !== right.loc.start.line;
+    }
+
+    /**
+     * Trims text of whitespace between two ranges
+     * @param {Fixer} fixer - the eslint fixer object
+     * @param {number} fromLoc - the start location
+     * @param {number} toLoc - the end location
+     * @param {string} mode - either 'start' or 'end'
+     * @param {string=} spacing - a spacing value that will optionally add a space to the removed text
+     * @returns {Object|*|{range, text}}
+     */
+    function fixByTrimmingWhitespace(fixer, fromLoc, toLoc, mode, spacing) {
+      let replacementText = getSourceCode(context).text.slice(fromLoc, toLoc);
+      if (mode === 'start') {
+        replacementText = replacementText.replace(/^\s+/gm, '');
+      } else {
+        replacementText = replacementText.replace(/\s+$/gm, '');
+      }
+      if (spacing === SPACING.always) {
+        if (mode === 'start') {
+          replacementText += ' ';
+        } else {
+          replacementText = ` ${replacementText}`;
+        }
+      }
+      return fixer.replaceTextRange([fromLoc, toLoc], replacementText);
+    }
+
+    /**
+    * Reports that there shouldn't be a newline after the first token
+    * @param {ASTNode} node - The node to report in the event of an error.
+    * @param {Token} token - The token to use for the report.
+    * @param {string} spacing
+    * @returns {void}
+    */
+    function reportNoBeginningNewline(node, token, spacing) {
+      report(context, messages.noNewlineAfter, 'noNewlineAfter', {
+        node,
+        loc: token.loc.start,
+        data: {
+          token: token.value,
+        },
+        fix(fixer) {
+          const nextToken = getSourceCode(context).getTokenAfter(token);
+          return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start', spacing);
+        },
+      });
+    }
+
+    /**
+    * Reports that there shouldn't be a newline before the last token
+    * @param {ASTNode} node - The node to report in the event of an error.
+    * @param {Token} token - The token to use for the report.
+    * @param {string} spacing
+    * @returns {void}
+    */
+    function reportNoEndingNewline(node, token, spacing) {
+      report(context, messages.noNewlineBefore, 'noNewlineBefore', {
+        node,
+        loc: token.loc.start,
+        data: {
+          token: token.value,
+        },
+        fix(fixer) {
+          const previousToken = getSourceCode(context).getTokenBefore(token);
+          return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end', spacing);
+        },
+      });
+    }
+
+    /**
+    * Reports that there shouldn't be a space after the first token
+    * @param {ASTNode} node - The node to report in the event of an error.
+    * @param {Token} token - The token to use for the report.
+    * @returns {void}
+    */
+    function reportNoBeginningSpace(node, token) {
+      report(context, messages.noSpaceAfter, 'noSpaceAfter', {
+        node,
+        loc: token.loc.start,
+        data: {
+          token: token.value,
+        },
+        fix(fixer) {
+          const sourceCode = getSourceCode(context);
+          const nextToken = sourceCode.getTokenAfter(token);
+          let nextComment;
+
+          // eslint >=4.x
+          if (sourceCode.getCommentsAfter) {
+            nextComment = sourceCode.getCommentsAfter(token);
+          // eslint 3.x
+          } else {
+            const potentialComment = sourceCode.getTokenAfter(token, { includeComments: true });
+            nextComment = nextToken === potentialComment ? [] : [potentialComment];
+          }
+
+          // Take comments into consideration to narrow the fix range to what is actually affected. (See #1414)
+          if (nextComment.length > 0) {
+            return fixByTrimmingWhitespace(fixer, token.range[1], Math.min(nextToken.range[0], nextComment[0].range[0]), 'start');
+          }
+
+          return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], 'start');
+        },
+      });
+    }
+
+    /**
+    * Reports that there shouldn't be a space before the last token
+    * @param {ASTNode} node - The node to report in the event of an error.
+    * @param {Token} token - The token to use for the report.
+    * @returns {void}
+    */
+    function reportNoEndingSpace(node, token) {
+      report(context, messages.noSpaceBefore, 'noSpaceBefore', {
+        node,
+        loc: token.loc.start,
+        data: {
+          token: token.value,
+        },
+        fix(fixer) {
+          const sourceCode = getSourceCode(context);
+          const previousToken = sourceCode.getTokenBefore(token);
+          let previousComment;
+
+          // eslint >=4.x
+          if (sourceCode.getCommentsBefore) {
+            previousComment = sourceCode.getCommentsBefore(token);
+          // eslint 3.x
+          } else {
+            const potentialComment = sourceCode.getTokenBefore(token, { includeComments: true });
+            previousComment = previousToken === potentialComment ? [] : [potentialComment];
+          }
+
+          // Take comments into consideration to narrow the fix range to what is actually affected. (See #1414)
+          if (previousComment.length > 0) {
+            return fixByTrimmingWhitespace(fixer, Math.max(previousToken.range[1], previousComment[0].range[1]), token.range[0], 'end');
+          }
+
+          return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], 'end');
+        },
+      });
+    }
+
+    /**
+    * Reports that there should be a space after the first token
+    * @param {ASTNode} node - The node to report in the event of an error.
+    * @param {Token} token - The token to use for the report.
+    * @returns {void}
+    */
+    function reportRequiredBeginningSpace(node, token) {
+      report(context, messages.spaceNeededAfter, 'spaceNeededAfter', {
+        node,
+        loc: token.loc.start,
+        data: {
+          token: token.value,
+        },
+        fix(fixer) {
+          return fixer.insertTextAfter(token, ' ');
+        },
+      });
+    }
+
+    /**
+    * Reports that there should be a space before the last token
+    * @param {ASTNode} node - The node to report in the event of an error.
+    * @param {Token} token - The token to use for the report.
+    * @returns {void}
+    */
+    function reportRequiredEndingSpace(node, token) {
+      report(context, messages.spaceNeededBefore, 'spaceNeededBefore', {
+        node,
+        loc: token.loc.start,
+        data: {
+          token: token.value,
+        },
+        fix(fixer) {
+          return fixer.insertTextBefore(token, ' ');
+        },
+      });
+    }
+
+    /**
+     * Determines if spacing in curly braces is valid.
+     * @param {ASTNode} node The AST node to check.
+     * @returns {void}
+     */
+    function validateBraceSpacing(node) {
+      let config;
+      switch (node.parent.type) {
+        case 'JSXAttribute':
+        case 'JSXOpeningElement':
+          config = attributesConfig;
+          break;
+
+        case 'JSXElement':
+        case 'JSXFragment':
+          config = childrenConfig;
+          break;
+
+        default:
+          return;
+      }
+      if (config === null) {
+        return;
+      }
+
+      const sourceCode = getSourceCode(context);
+      const first = sourceCode.getFirstToken(node);
+      const last = sourceCode.getLastToken(node);
+      let second = sourceCode.getTokenAfter(first, { includeComments: true });
+      let penultimate = sourceCode.getTokenBefore(last, { includeComments: true });
+
+      if (!second) {
+        second = sourceCode.getTokenAfter(first);
+        const leadingComments = sourceCode.getNodeByRangeIndex(second.range[0]).leadingComments;
+        second = leadingComments ? leadingComments[0] : second;
+      }
+      if (!penultimate) {
+        penultimate = sourceCode.getTokenBefore(last);
+        const trailingComments = sourceCode.getNodeByRangeIndex(penultimate.range[0]).trailingComments;
+        penultimate = trailingComments ? trailingComments[trailingComments.length - 1] : penultimate;
+      }
+
+      const isObjectLiteral = first.value === second.value;
+      const spacing = isObjectLiteral ? config.objectLiteralSpaces : config.when;
+      if (spacing === SPACING.always) {
+        if (!sourceCode.isSpaceBetweenTokens(first, second)) {
+          reportRequiredBeginningSpace(node, first);
+        } else if (!config.allowMultiline && isMultiline(first, second)) {
+          reportNoBeginningNewline(node, first, spacing);
+        }
+        if (!sourceCode.isSpaceBetweenTokens(penultimate, last)) {
+          reportRequiredEndingSpace(node, last);
+        } else if (!config.allowMultiline && isMultiline(penultimate, last)) {
+          reportNoEndingNewline(node, last, spacing);
+        }
+      } else if (spacing === SPACING.never) {
+        if (isMultiline(first, second)) {
+          if (!config.allowMultiline) {
+            reportNoBeginningNewline(node, first, spacing);
+          }
+        } else if (sourceCode.isSpaceBetweenTokens(first, second)) {
+          reportNoBeginningSpace(node, first);
+        }
+        if (isMultiline(penultimate, last)) {
+          if (!config.allowMultiline) {
+            reportNoEndingNewline(node, last, spacing);
+          }
+        } else if (sourceCode.isSpaceBetweenTokens(penultimate, last)) {
+          reportNoEndingSpace(node, last);
+        }
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      JSXExpressionContainer: validateBraceSpacing,
+      JSXSpreadAttribute: validateBraceSpacing,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-equals-spacing.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-equals-spacing.d.ts","sourceRoot":"","sources":["jsx-equals-spacing.js"],"names":[],"mappings":"wBAsBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Disallow or enforce spaces around equal signs in JSX attributes.
+ * @author ryym
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const getSourceCode = require('../util/eslint').getSourceCode;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noSpaceBefore: 'There should be no space before \'=\'',
+  noSpaceAfter: 'There should be no space after \'=\'',
+  needSpaceBefore: 'A space is required before \'=\'',
+  needSpaceAfter: 'A space is required after \'=\'',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce or disallow spaces around equal signs in JSX attributes',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-equals-spacing'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      enum: ['always', 'never'],
+    }],
+  },
+
+  create(context) {
+    const config = context.options[0] || 'never';
+
+    /**
+     * Determines a given attribute node has an equal sign.
+     * @param {ASTNode} attrNode - The attribute node.
+     * @returns {boolean} Whether or not the attriute node has an equal sign.
+     */
+    function hasEqual(attrNode) {
+      return attrNode.type !== 'JSXSpreadAttribute' && attrNode.value !== null;
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      JSXOpeningElement(node) {
+        node.attributes.forEach((attrNode) => {
+          if (!hasEqual(attrNode)) {
+            return;
+          }
+
+          const sourceCode = getSourceCode(context);
+          const equalToken = sourceCode.getTokenAfter(attrNode.name);
+          const spacedBefore = sourceCode.isSpaceBetweenTokens(attrNode.name, equalToken);
+          const spacedAfter = sourceCode.isSpaceBetweenTokens(equalToken, attrNode.value);
+
+          if (config === 'never') {
+            if (spacedBefore) {
+              report(context, messages.noSpaceBefore, 'noSpaceBefore', {
+                node: attrNode,
+                loc: equalToken.loc.start,
+                fix(fixer) {
+                  return fixer.removeRange([attrNode.name.range[1], equalToken.range[0]]);
+                },
+              });
+            }
+            if (spacedAfter) {
+              report(context, messages.noSpaceAfter, 'noSpaceAfter', {
+                node: attrNode,
+                loc: equalToken.loc.start,
+                fix(fixer) {
+                  return fixer.removeRange([equalToken.range[1], attrNode.value.range[0]]);
+                },
+              });
+            }
+          } else if (config === 'always') {
+            if (!spacedBefore) {
+              report(context, messages.needSpaceBefore, 'needSpaceBefore', {
+                node: attrNode,
+                loc: equalToken.loc.start,
+                fix(fixer) {
+                  return fixer.insertTextBefore(equalToken, ' ');
+                },
+              });
+            }
+            if (!spacedAfter) {
+              report(context, messages.needSpaceAfter, 'needSpaceAfter', {
+                node: attrNode,
+                loc: equalToken.loc.start,
+                fix(fixer) {
+                  return fixer.insertTextAfter(equalToken, ' ');
+                },
+              });
+            }
+          }
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-filename-extension.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-filename-extension.d.ts","sourceRoot":"","sources":["jsx-filename-extension.js"],"names":[],"mappings":"wBA8BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-filename-extension.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,120 @@
+/**
+ * @fileoverview Restrict file extensions that may contain JSX
+ * @author Joe Lencioni
+ */
+
+'use strict';
+
+const path = require('path');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = {
+  allow: 'always',
+  extensions: ['.jsx'],
+  ignoreFilesWithoutCode: false,
+};
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noJSXWithExtension: 'JSX not allowed in files with extension \'{{ext}}\'',
+  extensionOnlyForJSX: 'Only files containing JSX may use the extension \'{{ext}}\'',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow file extensions that may contain JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-filename-extension'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allow: {
+          enum: ['always', 'as-needed'],
+        },
+        extensions: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        ignoreFilesWithoutCode: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const filename = context.getFilename();
+
+    let jsxNode;
+
+    if (filename === '<text>') {
+      // No need to traverse any nodes.
+      return {};
+    }
+
+    const allow = (context.options[0] && context.options[0].allow) || DEFAULTS.allow;
+    const allowedExtensions = (context.options[0] && context.options[0].extensions) || DEFAULTS.extensions;
+    const ignoreFilesWithoutCode = (context.options[0] && context.options[0].ignoreFilesWithoutCode)
+      || DEFAULTS.ignoreFilesWithoutCode;
+    const isAllowedExtension = allowedExtensions.some((extension) => filename.slice(-extension.length) === extension);
+
+    function handleJSX(node) {
+      if (!jsxNode) {
+        jsxNode = node;
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      JSXElement: handleJSX,
+      JSXFragment: handleJSX,
+
+      'Program:exit'(node) {
+        if (jsxNode) {
+          if (!isAllowedExtension) {
+            report(context, messages.noJSXWithExtension, 'noJSXWithExtension', {
+              node: jsxNode,
+              data: {
+                ext: path.extname(filename),
+              },
+            });
+          }
+          return;
+        }
+
+        if (isAllowedExtension && allow === 'as-needed') {
+          if (ignoreFilesWithoutCode && node.body.length === 0) {
+            return;
+          }
+          report(context, messages.extensionOnlyForJSX, 'extensionOnlyForJSX', {
+            node,
+            data: {
+              ext: path.extname(filename),
+            },
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-first-prop-new-line.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-first-prop-new-line.d.ts","sourceRoot":"","sources":["jsx-first-prop-new-line.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-first-prop-new-line.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,83 @@
+/**
+ * @fileoverview Ensure proper position of the first property in JSX
+ * @author Joachim Seminck
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const propsUtil = require('../util/props');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  propOnNewLine: 'Property should be placed on a new line',
+  propOnSameLine: 'Property should be placed on the same line as the component declaration',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce proper position of the first property in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-first-prop-new-line'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      enum: ['always', 'never', 'multiline', 'multiline-multiprop', 'multiprop'],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || 'multiline-multiprop';
+
+    function isMultilineJSX(jsxNode) {
+      return jsxNode.loc.start.line < jsxNode.loc.end.line;
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        if (
+          (configuration === 'multiline' && isMultilineJSX(node))
+          || (configuration === 'multiline-multiprop' && isMultilineJSX(node) && node.attributes.length > 1)
+          || (configuration === 'multiprop' && node.attributes.length > 1)
+          || (configuration === 'always')
+        ) {
+          node.attributes.some((decl) => {
+            if (decl.loc.start.line === node.loc.start.line) {
+              report(context, messages.propOnNewLine, 'propOnNewLine', {
+                node: decl,
+                fix(fixer) {
+                  const nodeTypeArguments = propsUtil.getTypeArguments(node);
+                  return fixer.replaceTextRange([(nodeTypeArguments || node.name).range[1], decl.range[0]], '\n');
+                },
+              });
+            }
+            return true;
+          });
+        } else if (
+          (configuration === 'never' && node.attributes.length > 0)
+          || (configuration === 'multiprop' && isMultilineJSX(node) && node.attributes.length <= 1)
+        ) {
+          const firstNode = node.attributes[0];
+          if (node.loc.start.line < firstNode.loc.start.line) {
+            report(context, messages.propOnSameLine, 'propOnSameLine', {
+              node: firstNode,
+              fix(fixer) {
+                return fixer.replaceTextRange([node.name.range[1], firstNode.range[0]], ' ');
+              },
+            });
+          }
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-fragments.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-fragments.d.ts","sourceRoot":"","sources":["jsx-fragments.js"],"names":[],"mappings":"wBA6BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-fragments.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,213 @@
+/**
+ * @fileoverview Enforce shorthand or standard form for React fragments.
+ * @author Alex Zherdev
+ */
+
+'use strict';
+
+const elementType = require('jsx-ast-utils/elementType');
+const pragmaUtil = require('../util/pragma');
+const variableUtil = require('../util/variable');
+const testReactVersion = require('../util/version').testReactVersion;
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getText = require('../util/eslint').getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function replaceNode(source, node, text) {
+  return `${source.slice(0, node.range[0])}${text}${source.slice(node.range[1])}`;
+}
+
+const messages = {
+  fragmentsNotSupported: 'Fragments are only supported starting from React v16.2. Please disable the `react/jsx-fragments` rule in `eslint` settings or upgrade your version of React.',
+  preferPragma: 'Prefer {{react}}.{{fragment}} over fragment shorthand',
+  preferFragment: 'Prefer fragment shorthand over {{react}}.{{fragment}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce shorthand or standard form for React fragments',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-fragments'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      enum: ['syntax', 'element'],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || 'syntax';
+    const reactPragma = pragmaUtil.getFromContext(context);
+    const fragmentPragma = pragmaUtil.getFragmentFromContext(context);
+    const openFragShort = '<>';
+    const closeFragShort = '</>';
+    const openFragLong = `<${reactPragma}.${fragmentPragma}>`;
+    const closeFragLong = `</${reactPragma}.${fragmentPragma}>`;
+
+    function reportOnReactVersion(node) {
+      if (!testReactVersion(context, '>= 16.2.0')) {
+        report(context, messages.fragmentsNotSupported, 'fragmentsNotSupported', {
+          node,
+        });
+        return true;
+      }
+
+      return false;
+    }
+
+    function getFixerToLong(jsxFragment) {
+      if (!jsxFragment.closingFragment || !jsxFragment.openingFragment) {
+        // the old TS parser crashes here
+        // TODO: FIXME: can we fake these two descriptors?
+        return null;
+      }
+      return function fix(fixer) {
+        let source = getText(context);
+        source = replaceNode(source, jsxFragment.closingFragment, closeFragLong);
+        source = replaceNode(source, jsxFragment.openingFragment, openFragLong);
+        const lengthDiff = openFragLong.length - getText(context, jsxFragment.openingFragment).length
+          + closeFragLong.length - getText(context, jsxFragment.closingFragment).length;
+        const range = jsxFragment.range;
+        return fixer.replaceTextRange(range, source.slice(range[0], range[1] + lengthDiff));
+      };
+    }
+
+    function getFixerToShort(jsxElement) {
+      return function fix(fixer) {
+        let source = getText(context);
+        let lengthDiff;
+        if (jsxElement.closingElement) {
+          source = replaceNode(source, jsxElement.closingElement, closeFragShort);
+          source = replaceNode(source, jsxElement.openingElement, openFragShort);
+          lengthDiff = getText(context, jsxElement.openingElement).length - openFragShort.length
+            + getText(context, jsxElement.closingElement).length - closeFragShort.length;
+        } else {
+          source = replaceNode(source, jsxElement.openingElement, `${openFragShort}${closeFragShort}`);
+          lengthDiff = getText(context, jsxElement.openingElement).length - openFragShort.length
+            - closeFragShort.length;
+        }
+
+        const range = jsxElement.range;
+        return fixer.replaceTextRange(range, source.slice(range[0], range[1] - lengthDiff));
+      };
+    }
+
+    function refersToReactFragment(node, name) {
+      const variableInit = variableUtil.findVariableByName(context, node, name);
+      if (!variableInit) {
+        return false;
+      }
+
+      // const { Fragment } = React;
+      if (variableInit.type === 'Identifier' && variableInit.name === reactPragma) {
+        return true;
+      }
+
+      // const Fragment = React.Fragment;
+      if (
+        variableInit.type === 'MemberExpression'
+        && variableInit.object.type === 'Identifier'
+        && variableInit.object.name === reactPragma
+        && variableInit.property.type === 'Identifier'
+        && variableInit.property.name === fragmentPragma
+      ) {
+        return true;
+      }
+
+      // const { Fragment } = require('react');
+      if (
+        variableInit.callee
+        && variableInit.callee.name === 'require'
+        && variableInit.arguments
+        && variableInit.arguments[0]
+        && variableInit.arguments[0].value === 'react'
+      ) {
+        return true;
+      }
+
+      return false;
+    }
+
+    const jsxElements = [];
+    const fragmentNames = new Set([`${reactPragma}.${fragmentPragma}`]);
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      JSXElement(node) {
+        jsxElements.push(node);
+      },
+
+      JSXFragment(node) {
+        if (reportOnReactVersion(node)) {
+          return;
+        }
+
+        if (configuration === 'element') {
+          report(context, messages.preferPragma, 'preferPragma', {
+            node,
+            data: {
+              react: reactPragma,
+              fragment: fragmentPragma,
+            },
+            fix: getFixerToLong(node),
+          });
+        }
+      },
+
+      ImportDeclaration(node) {
+        if (node.source && node.source.value === 'react') {
+          node.specifiers.forEach((spec) => {
+            if (
+              'imported' in spec
+              && spec.imported
+              && 'name' in spec.imported
+              && spec.imported.name === fragmentPragma
+            ) {
+              if (spec.local) {
+                fragmentNames.add(spec.local.name);
+              }
+            }
+          });
+        }
+      },
+
+      'Program:exit'() {
+        jsxElements.forEach((node) => {
+          const openingEl = node.openingElement;
+          const elName = elementType(openingEl);
+
+          if (fragmentNames.has(elName) || refersToReactFragment(node, elName)) {
+            if (reportOnReactVersion(node)) {
+              return;
+            }
+
+            const attrs = openingEl.attributes;
+            if (configuration === 'syntax' && !(attrs && attrs.length > 0)) {
+              report(context, messages.preferFragment, 'preferFragment', {
+                node,
+                data: {
+                  react: reactPragma,
+                  fragment: fragmentPragma,
+                },
+                fix: getFixerToShort(node),
+              });
+            }
+          }
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-handler-names.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-handler-names.d.ts","sourceRoot":"","sources":["jsx-handler-names.js"],"names":[],"mappings":"wBA6BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,209 @@
+/**
+ * @fileoverview Enforce event handler naming conventions in JSX
+ * @author Jake Marsh
+ */
+
+'use strict';
+
+const minimatch = require('minimatch');
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  badHandlerName: 'Handler function for {{propKey}} prop key must be a camelCase name beginning with \'{{handlerPrefix}}\' only',
+  badPropKey: 'Prop key for {{propValue}} must begin with \'{{handlerPropPrefix}}\'',
+};
+
+function isPrefixDisabled(prefix) {
+  return prefix === false;
+}
+
+function isInlineHandler(node) {
+  return node.value.expression.type === 'ArrowFunctionExpression';
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce event handler naming conventions in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-handler-names'),
+    },
+
+    messages,
+
+    schema: [{
+      anyOf: [
+        {
+          type: 'object',
+          properties: {
+            eventHandlerPrefix: { type: 'string' },
+            eventHandlerPropPrefix: { type: 'string' },
+            checkLocalVariables: { type: 'boolean' },
+            checkInlineFunction: { type: 'boolean' },
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        }, {
+          type: 'object',
+          properties: {
+            eventHandlerPrefix: { type: 'string' },
+            eventHandlerPropPrefix: {
+              type: 'boolean',
+              enum: [false],
+            },
+            checkLocalVariables: { type: 'boolean' },
+            checkInlineFunction: { type: 'boolean' },
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        }, {
+          type: 'object',
+          properties: {
+            eventHandlerPrefix: {
+              type: 'boolean',
+              enum: [false],
+            },
+            eventHandlerPropPrefix: { type: 'string' },
+            checkLocalVariables: { type: 'boolean' },
+            checkInlineFunction: { type: 'boolean' },
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        }, {
+          type: 'object',
+          properties: {
+            checkLocalVariables: { type: 'boolean' },
+          },
+          additionalProperties: false,
+        }, {
+          type: 'object',
+          properties: {
+            checkInlineFunction: { type: 'boolean' },
+          },
+          additionalProperties: false,
+        },
+        {
+          type: 'object',
+          properties: {
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
+          },
+        },
+      ],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+
+    const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix)
+      ? null
+      : configuration.eventHandlerPrefix || 'handle';
+    const eventHandlerPropPrefix = isPrefixDisabled(configuration.eventHandlerPropPrefix)
+      ? null
+      : configuration.eventHandlerPropPrefix || 'on';
+
+    const EVENT_HANDLER_REGEX = !eventHandlerPrefix
+      ? null
+      : new RegExp(`^((props\\.${eventHandlerPropPrefix || ''})|((.*\\.)?${eventHandlerPrefix}))[0-9]*[A-Z].*$`);
+    const PROP_EVENT_HANDLER_REGEX = !eventHandlerPropPrefix
+      ? null
+      : new RegExp(`^(${eventHandlerPropPrefix}[A-Z].*|ref)$`);
+
+    const checkLocal = !!configuration.checkLocalVariables;
+
+    const checkInlineFunction = !!configuration.checkInlineFunction;
+
+    const ignoreComponentNames = configuration.ignoreComponentNames || [];
+
+    return {
+      JSXAttribute(node) {
+        const componentName = node.parent.name.name;
+
+        const isComponentNameIgnored = ignoreComponentNames.some((ignoredComponentNamePattern) => minimatch(
+          componentName,
+          ignoredComponentNamePattern
+        ));
+
+        if (
+          !node.value
+          || !node.value.expression
+          || (!checkInlineFunction && isInlineHandler(node))
+          || (
+            !checkLocal
+            && (isInlineHandler(node)
+              ? !node.value.expression.body.callee || !node.value.expression.body.callee.object
+              : !node.value.expression.object
+            )
+          )
+          || isComponentNameIgnored
+        ) {
+          return;
+        }
+
+        const propKey = typeof node.name === 'object' ? node.name.name : node.name;
+        const expression = node.value.expression;
+        const propValue = getText(
+          context,
+          checkInlineFunction && isInlineHandler(node) ? expression.body.callee : expression
+        ).replace(/\s*/g, '').replace(/^this\.|.*::/, '');
+
+        if (propKey === 'ref') {
+          return;
+        }
+
+        const propIsEventHandler = PROP_EVENT_HANDLER_REGEX && PROP_EVENT_HANDLER_REGEX.test(propKey);
+        const propFnIsNamedCorrectly = EVENT_HANDLER_REGEX && EVENT_HANDLER_REGEX.test(propValue);
+
+        if (
+          propIsEventHandler
+          && propFnIsNamedCorrectly !== null
+          && !propFnIsNamedCorrectly
+        ) {
+          report(context, messages.badHandlerName, 'badHandlerName', {
+            node,
+            data: {
+              propKey,
+              handlerPrefix: eventHandlerPrefix,
+            },
+          });
+        } else if (
+          propFnIsNamedCorrectly
+          && propIsEventHandler !== null
+          && !propIsEventHandler
+        ) {
+          report(context, messages.badPropKey, 'badPropKey', {
+            node,
+            data: {
+              propValue,
+              handlerPropPrefix: eventHandlerPropPrefix,
+            },
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-indent-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-indent-props.d.ts","sourceRoot":"","sources":["jsx-indent-props.js"],"names":[],"mappings":"wBA+CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,218 @@
+/**
+ * @fileoverview Validate props indentation in JSX
+ * @author Yannick Croissant
+
+ * This rule has been ported and modified from eslint and nodeca.
+ * @author Vitaly Puzrin
+ * @author Gyandeep Singh
+ * @copyright 2015 Vitaly Puzrin. All rights reserved.
+ * @copyright 2015 Gyandeep Singh. All rights reserved.
+ Copyright (C) 2014 by Vitaly Puzrin
+
+ 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.
+ */
+
+'use strict';
+
+const repeat = require('string.prototype.repeat');
+
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const reportC = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  wrongIndent: 'Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce props indentation in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-indent-props'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      anyOf: [{
+        enum: ['tab', 'first'],
+      }, {
+        type: 'integer',
+      }, {
+        type: 'object',
+        properties: {
+          indentMode: {
+            anyOf: [{
+              enum: ['tab', 'first'],
+            }, {
+              type: 'integer',
+            }],
+          },
+          ignoreTernaryOperator: {
+            type: 'boolean',
+          },
+        },
+      }],
+    }],
+  },
+
+  create(context) {
+    const extraColumnStart = 0;
+    let indentType = 'space';
+    /** @type {number|'first'} */
+    let indentSize = 4;
+    const line = {
+      isUsingOperator: false,
+      currentOperator: false,
+    };
+    let ignoreTernaryOperator = false;
+
+    if (context.options.length) {
+      const isConfigObject = typeof context.options[0] === 'object';
+      const indentMode = isConfigObject
+        ? context.options[0].indentMode
+        : context.options[0];
+
+      if (indentMode === 'first') {
+        indentSize = 'first';
+        indentType = 'space';
+      } else if (indentMode === 'tab') {
+        indentSize = 1;
+        indentType = 'tab';
+      } else if (typeof indentMode === 'number') {
+        indentSize = indentMode;
+        indentType = 'space';
+      }
+
+      if (isConfigObject && context.options[0].ignoreTernaryOperator) {
+        ignoreTernaryOperator = true;
+      }
+    }
+
+    /**
+     * Reports a given indent violation and properly pluralizes the message
+     * @param {ASTNode} node Node violating the indent rule
+     * @param {number} needed Expected indentation character count
+     * @param {number} gotten Indentation character count in the actual node/code
+     */
+    function report(node, needed, gotten) {
+      const msgContext = {
+        needed,
+        type: indentType,
+        characters: needed === 1 ? 'character' : 'characters',
+        gotten,
+      };
+
+      reportC(context, messages.wrongIndent, 'wrongIndent', {
+        node,
+        data: msgContext,
+        fix(fixer) {
+          return fixer.replaceTextRange([node.range[0] - node.loc.start.column, node.range[0]],
+            repeat(indentType === 'space' ? ' ' : '\t', needed)
+          );
+        },
+      });
+    }
+
+    /**
+     * Get node indent
+     * @param {ASTNode} node Node to examine
+     * @return {number} Indent
+     */
+    function getNodeIndent(node) {
+      let src = getText(context, node, node.loc.start.column + extraColumnStart);
+      const lines = src.split('\n');
+      src = lines[0];
+
+      let regExp;
+      if (indentType === 'space') {
+        regExp = /^[ ]+/;
+      } else {
+        regExp = /^[\t]+/;
+      }
+
+      const indent = regExp.exec(src);
+      const useOperator = /^([ ]|[\t])*[:]/.test(src) || /^([ ]|[\t])*[?]/.test(src);
+      const useBracket = /[<]/.test(src);
+
+      line.currentOperator = false;
+      if (useOperator) {
+        line.isUsingOperator = true;
+        line.currentOperator = true;
+      } else if (useBracket) {
+        line.isUsingOperator = false;
+      }
+
+      return indent ? indent[0].length : 0;
+    }
+
+    /**
+     * Check indent for nodes list
+     * @param {ASTNode[]} nodes list of node objects
+     * @param {number} indent needed indent
+     */
+    function checkNodesIndent(nodes, indent) {
+      let nestedIndent = indent;
+      nodes.forEach((node) => {
+        const nodeIndent = getNodeIndent(node);
+        if (
+          line.isUsingOperator
+          && !line.currentOperator
+          && indentSize !== 'first'
+          && !ignoreTernaryOperator
+        ) {
+          nestedIndent += indentSize;
+          line.isUsingOperator = false;
+        }
+        if (
+          node.type !== 'ArrayExpression' && node.type !== 'ObjectExpression'
+          && nodeIndent !== nestedIndent && astUtil.isNodeFirstInLine(context, node)
+        ) {
+          report(node, nestedIndent, nodeIndent);
+        }
+      });
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        if (!node.attributes.length) {
+          return;
+        }
+        let propIndent;
+        if (indentSize === 'first') {
+          const firstPropNode = node.attributes[0];
+          propIndent = firstPropNode.loc.start.column;
+        } else {
+          const elementIndent = getNodeIndent(node);
+          propIndent = elementIndent + indentSize;
+        }
+        checkNodesIndent(node.attributes, propIndent);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-indent.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-indent.d.ts","sourceRoot":"","sources":["jsx-indent.js"],"names":[],"mappings":"wBAoDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-indent.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,447 @@
+/**
+ * @fileoverview Validate JSX indentation
+ * @author Yannick Croissant
+
+ * This rule has been ported and modified from eslint and nodeca.
+ * @author Vitaly Puzrin
+ * @author Gyandeep Singh
+ * @copyright 2015 Vitaly Puzrin. All rights reserved.
+ * @copyright 2015 Gyandeep Singh. All rights reserved.
+ Copyright (C) 2014 by Vitaly Puzrin
+
+ 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.
+ */
+
+'use strict';
+
+const matchAll = require('string.prototype.matchall');
+const repeat = require('string.prototype.repeat');
+
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const reportC = require('../util/report');
+const jsxUtil = require('../util/jsx');
+const eslintUtil = require('../util/eslint');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  wrongIndent: 'Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce JSX indentation',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-indent'),
+    },
+    fixable: 'whitespace',
+
+    messages,
+
+    schema: [{
+      anyOf: [{
+        enum: ['tab'],
+      }, {
+        type: 'integer',
+      }],
+    }, {
+      type: 'object',
+      properties: {
+        checkAttributes: {
+          type: 'boolean',
+        },
+        indentLogicalExpressions: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const extraColumnStart = 0;
+    let indentType = 'space';
+    let indentSize = 4;
+
+    if (context.options.length) {
+      if (context.options[0] === 'tab') {
+        indentSize = 1;
+        indentType = 'tab';
+      } else if (typeof context.options[0] === 'number') {
+        indentSize = context.options[0];
+        indentType = 'space';
+      }
+    }
+
+    const indentChar = indentType === 'space' ? ' ' : '\t';
+    const options = context.options[1] || {};
+    const checkAttributes = options.checkAttributes || false;
+    const indentLogicalExpressions = options.indentLogicalExpressions || false;
+
+    /**
+     * Responsible for fixing the indentation issue fix
+     * @param {ASTNode} node Node violating the indent rule
+     * @param {number} needed Expected indentation character count
+     * @returns {Function} function to be executed by the fixer
+     * @private
+     */
+    function getFixerFunction(node, needed) {
+      const indent = repeat(indentChar, needed);
+
+      if (node.type === 'JSXText' || node.type === 'Literal') {
+        return function fix(fixer) {
+          const regExp = /\n[\t ]*(\S)/g;
+          const fixedText = node.raw.replace(regExp, (match, p1) => `\n${indent}${p1}`);
+          return fixer.replaceText(node, fixedText);
+        };
+      }
+
+      if (node.type === 'ReturnStatement') {
+        const raw = getText(context, node);
+        const lines = raw.split('\n');
+        if (lines.length > 1) {
+          return function fix(fixer) {
+            const lastLineStart = raw.lastIndexOf('\n');
+            const lastLine = raw.slice(lastLineStart).replace(/^\n[\t ]*(\S)/, (match, p1) => `\n${indent}${p1}`);
+            return fixer.replaceTextRange(
+              [node.range[0] + lastLineStart, node.range[1]],
+              lastLine
+            );
+          };
+        }
+      }
+
+      return function fix(fixer) {
+        return fixer.replaceTextRange(
+          [node.range[0] - node.loc.start.column, node.range[0]],
+          indent
+        );
+      };
+    }
+
+    /**
+     * Reports a given indent violation and properly pluralizes the message
+     * @param {ASTNode} node Node violating the indent rule
+     * @param {number} needed Expected indentation character count
+     * @param {number} gotten Indentation character count in the actual node/code
+     * @param {Object} [loc] Error line and column location
+     */
+    function report(node, needed, gotten, loc) {
+      const msgContext = {
+        needed,
+        type: indentType,
+        characters: needed === 1 ? 'character' : 'characters',
+        gotten,
+      };
+
+      reportC(context, messages.wrongIndent, 'wrongIndent', Object.assign({
+        node,
+        data: msgContext,
+        fix: getFixerFunction(node, needed),
+      }, loc && { loc }));
+    }
+
+    /**
+     * Get node indent
+     * @param {ASTNode} node Node to examine
+     * @param {boolean} [byLastLine] get indent of node's last line
+     * @param {boolean} [excludeCommas] skip comma on start of line
+     * @return {number} Indent
+     */
+    function getNodeIndent(node, byLastLine, excludeCommas) {
+      let src = getText(context, node, node.loc.start.column + extraColumnStart);
+      const lines = src.split('\n');
+      if (byLastLine) {
+        src = lines[lines.length - 1];
+      } else {
+        src = lines[0];
+      }
+
+      const skip = excludeCommas ? ',' : '';
+
+      let regExp;
+      if (indentType === 'space') {
+        regExp = new RegExp(`^[ ${skip}]+`);
+      } else {
+        regExp = new RegExp(`^[\t${skip}]+`);
+      }
+
+      const indent = regExp.exec(src);
+      return indent ? indent[0].length : 0;
+    }
+
+    /**
+     * Check if the node is the right member of a logical expression
+     * @param {ASTNode} node The node to check
+     * @return {boolean} true if its the case, false if not
+     */
+    function isRightInLogicalExp(node) {
+      return (
+        node.parent
+        && node.parent.parent
+        && node.parent.parent.type === 'LogicalExpression'
+        && node.parent.parent.right === node.parent
+        && !indentLogicalExpressions
+      );
+    }
+
+    /**
+     * Check if the node is the alternate member of a conditional expression
+     * @param {ASTNode} node The node to check
+     * @return {boolean} true if its the case, false if not
+     */
+    function isAlternateInConditionalExp(node) {
+      return (
+        node.parent
+        && node.parent.parent
+        && node.parent.parent.type === 'ConditionalExpression'
+        && node.parent.parent.alternate === node.parent
+        && getSourceCode(context).getTokenBefore(node).value !== '('
+      );
+    }
+
+    /**
+     * Check if the node is within a DoExpression block but not the first expression (which need to be indented)
+     * @param {ASTNode} node The node to check
+     * @return {boolean} true if its the case, false if not
+     */
+    function isSecondOrSubsequentExpWithinDoExp(node) {
+      /*
+        It returns true when node.parent.parent.parent.parent matches:
+
+        DoExpression({
+          ...,
+          body: BlockStatement({
+            ...,
+            body: [
+              ...,  // 1-n times
+              ExpressionStatement({
+                ...,
+                expression: JSXElement({
+                  ...,
+                  openingElement: JSXOpeningElement()  // the node
+                })
+              }),
+              ...  // 0-n times
+            ]
+          })
+        })
+
+        except:
+
+        DoExpression({
+          ...,
+          body: BlockStatement({
+            ...,
+            body: [
+              ExpressionStatement({
+                ...,
+                expression: JSXElement({
+                  ...,
+                  openingElement: JSXOpeningElement()  // the node
+                })
+              }),
+              ...  // 0-n times
+            ]
+          })
+        })
+      */
+      const isInExpStmt = (
+        node.parent
+        && node.parent.parent
+        && node.parent.parent.type === 'ExpressionStatement'
+      );
+      if (!isInExpStmt) {
+        return false;
+      }
+
+      const expStmt = node.parent.parent;
+      const isInBlockStmtWithinDoExp = (
+        expStmt.parent
+        && expStmt.parent.type === 'BlockStatement'
+        && expStmt.parent.parent
+        && expStmt.parent.parent.type === 'DoExpression'
+      );
+      if (!isInBlockStmtWithinDoExp) {
+        return false;
+      }
+
+      const blockStmt = expStmt.parent;
+      const blockStmtFirstExp = blockStmt.body[0];
+      return !(blockStmtFirstExp === expStmt);
+    }
+
+    /**
+     * Check indent for nodes list
+     * @param {ASTNode} node The node to check
+     * @param {number} indent needed indent
+     * @param {boolean} [excludeCommas] skip comma on start of line
+     */
+    function checkNodesIndent(node, indent, excludeCommas) {
+      const nodeIndent = getNodeIndent(node, false, excludeCommas);
+      const isCorrectRightInLogicalExp = isRightInLogicalExp(node) && (nodeIndent - indent) === indentSize;
+      const isCorrectAlternateInCondExp = isAlternateInConditionalExp(node) && (nodeIndent - indent) === 0;
+      if (
+        nodeIndent !== indent
+        && astUtil.isNodeFirstInLine(context, node)
+        && !isCorrectRightInLogicalExp
+        && !isCorrectAlternateInCondExp
+      ) {
+        report(node, indent, nodeIndent);
+      }
+    }
+
+    /**
+     * Check indent for Literal Node or JSXText Node
+     * @param {ASTNode} node The node to check
+     * @param {number} indent needed indent
+     */
+    function checkLiteralNodeIndent(node, indent) {
+      const value = node.value;
+      const regExp = indentType === 'space' ? /\n( *)[\t ]*\S/g : /\n(\t*)[\t ]*\S/g;
+      const nodeIndentsPerLine = Array.from(
+        matchAll(String(value), regExp),
+        (match) => (match[1] ? match[1].length : 0)
+      );
+      const hasFirstInLineNode = nodeIndentsPerLine.length > 0;
+      if (
+        hasFirstInLineNode
+        && !nodeIndentsPerLine.every((actualIndent) => actualIndent === indent)
+      ) {
+        nodeIndentsPerLine.forEach((nodeIndent) => {
+          report(node, indent, nodeIndent);
+        });
+      }
+    }
+
+    function handleOpeningElement(node) {
+      const sourceCode = getSourceCode(context);
+      let prevToken = sourceCode.getTokenBefore(node);
+      if (!prevToken) {
+        return;
+      }
+      // Use the parent in a list or an array
+      if (prevToken.type === 'JSXText' || ((prevToken.type === 'Punctuator') && prevToken.value === ',')) {
+        prevToken = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
+        prevToken = prevToken.type === 'Literal' || prevToken.type === 'JSXText' ? prevToken.parent : prevToken;
+      // Use the first non-punctuator token in a conditional expression
+      } else if (prevToken.type === 'Punctuator' && prevToken.value === ':') {
+        do {
+          prevToken = sourceCode.getTokenBefore(prevToken);
+        } while (prevToken.type === 'Punctuator' && prevToken.value !== '/');
+        prevToken = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
+        while (prevToken.parent && prevToken.parent.type !== 'ConditionalExpression') {
+          prevToken = prevToken.parent;
+        }
+      }
+      prevToken = prevToken.type === 'JSXExpressionContainer' ? prevToken.expression : prevToken;
+      const parentElementIndent = getNodeIndent(prevToken);
+      const indent = (
+        prevToken.loc.start.line === node.loc.start.line
+        || isRightInLogicalExp(node)
+        || isAlternateInConditionalExp(node)
+        || isSecondOrSubsequentExpWithinDoExp(node)
+      ) ? 0 : indentSize;
+      checkNodesIndent(node, parentElementIndent + indent);
+    }
+
+    function handleClosingElement(node) {
+      if (!node.parent) {
+        return;
+      }
+      const peerElementIndent = getNodeIndent(node.parent.openingElement || node.parent.openingFragment);
+      checkNodesIndent(node, peerElementIndent);
+    }
+
+    function handleAttribute(node) {
+      if (!checkAttributes || (!node.value || node.value.type !== 'JSXExpressionContainer')) {
+        return;
+      }
+      const nameIndent = getNodeIndent(node.name);
+      const lastToken = getSourceCode(context).getLastToken(node.value);
+      const firstInLine = astUtil.getFirstNodeInLine(context, lastToken);
+      const indent = node.name.loc.start.line === firstInLine.loc.start.line ? 0 : nameIndent;
+      checkNodesIndent(firstInLine, indent);
+    }
+
+    function handleLiteral(node) {
+      if (!node.parent) {
+        return;
+      }
+      if (node.parent.type !== 'JSXElement' && node.parent.type !== 'JSXFragment') {
+        return;
+      }
+      const parentNodeIndent = getNodeIndent(node.parent);
+      checkLiteralNodeIndent(node, parentNodeIndent + indentSize);
+    }
+
+    return {
+      JSXOpeningElement: handleOpeningElement,
+      JSXOpeningFragment: handleOpeningElement,
+      JSXClosingElement: handleClosingElement,
+      JSXClosingFragment: handleClosingElement,
+      JSXAttribute: handleAttribute,
+      JSXExpressionContainer(node) {
+        if (!node.parent) {
+          return;
+        }
+        const parentNodeIndent = getNodeIndent(node.parent);
+        checkNodesIndent(node, parentNodeIndent + indentSize);
+      },
+      Literal: handleLiteral,
+      JSXText: handleLiteral,
+
+      ReturnStatement(node) {
+        if (
+          !node.parent
+          || !jsxUtil.isJSX(node.argument)
+        ) {
+          return;
+        }
+
+        let fn = node.parent;
+        while (fn && fn.type !== 'FunctionDeclaration' && fn.type !== 'FunctionExpression') {
+          fn = fn.parent;
+        }
+        if (
+          !fn
+          || !jsxUtil.isReturningJSX(context, node, true)
+        ) {
+          return;
+        }
+
+        const openingIndent = getNodeIndent(node);
+        const closingIndent = getNodeIndent(node, true);
+
+        if (openingIndent !== closingIndent) {
+          report(node, openingIndent, closingIndent);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-key.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-key.d.ts","sourceRoot":"","sources":["jsx-key.js"],"names":[],"mappings":"wBAmCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-key.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,303 @@
+/**
+ * @fileoverview Report missing `key` props in iterators/collection literals.
+ * @author Ben Mosher
+ */
+
+'use strict';
+
+const hasProp = require('jsx-ast-utils/hasProp');
+const propName = require('jsx-ast-utils/propName');
+const values = require('object.values');
+const docsUrl = require('../util/docsUrl');
+const pragmaUtil = require('../util/pragma');
+const report = require('../util/report');
+const astUtil = require('../util/ast');
+const getText = require('../util/eslint').getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const defaultOptions = {
+  checkFragmentShorthand: false,
+  checkKeyMustBeforeSpread: false,
+  warnOnDuplicates: false,
+};
+
+const messages = {
+  missingIterKey: 'Missing "key" prop for element in iterator',
+  missingIterKeyUsePrag: 'Missing "key" prop for element in iterator. Shorthand fragment syntax does not support providing keys. Use {{reactPrag}}.{{fragPrag}} instead',
+  missingArrayKey: 'Missing "key" prop for element in array',
+  missingArrayKeyUsePrag: 'Missing "key" prop for element in array. Shorthand fragment syntax does not support providing keys. Use {{reactPrag}}.{{fragPrag}} instead',
+  keyBeforeSpread: '`key` prop must be placed before any `{...spread}, to avoid conflicting with React’s new JSX transform: https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html`',
+  nonUniqueKeys: '`key` prop must be unique',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow missing `key` props in iterators/collection literals',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('jsx-key'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        checkFragmentShorthand: {
+          type: 'boolean',
+          default: defaultOptions.checkFragmentShorthand,
+        },
+        checkKeyMustBeforeSpread: {
+          type: 'boolean',
+          default: defaultOptions.checkKeyMustBeforeSpread,
+        },
+        warnOnDuplicates: {
+          type: 'boolean',
+          default: defaultOptions.warnOnDuplicates,
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const options = Object.assign({}, defaultOptions, context.options[0]);
+    const checkFragmentShorthand = options.checkFragmentShorthand;
+    const checkKeyMustBeforeSpread = options.checkKeyMustBeforeSpread;
+    const warnOnDuplicates = options.warnOnDuplicates;
+    const reactPragma = pragmaUtil.getFromContext(context);
+    const fragmentPragma = pragmaUtil.getFragmentFromContext(context);
+
+    function isKeyAfterSpread(attributes) {
+      let hasFoundSpread = false;
+      return attributes.some((attribute) => {
+        if (attribute.type === 'JSXSpreadAttribute') {
+          hasFoundSpread = true;
+          return false;
+        }
+        if (attribute.type !== 'JSXAttribute') {
+          return false;
+        }
+        return hasFoundSpread && propName(attribute) === 'key';
+      });
+    }
+
+    function checkIteratorElement(node) {
+      if (node.type === 'JSXElement') {
+        if (!hasProp(node.openingElement.attributes, 'key')) {
+          report(context, messages.missingIterKey, 'missingIterKey', { node });
+        } else {
+          const attrs = node.openingElement.attributes;
+
+          if (checkKeyMustBeforeSpread && isKeyAfterSpread(attrs)) {
+            report(context, messages.keyBeforeSpread, 'keyBeforeSpread', { node });
+          }
+        }
+      } else if (checkFragmentShorthand && node.type === 'JSXFragment') {
+        report(context, messages.missingIterKeyUsePrag, 'missingIterKeyUsePrag', {
+          node,
+          data: {
+            reactPrag: reactPragma,
+            fragPrag: fragmentPragma,
+          },
+        });
+      }
+    }
+
+    function getReturnStatements(node) {
+      const returnStatements = arguments[1] || [];
+      if (node.type === 'IfStatement') {
+        if (node.consequent) {
+          getReturnStatements(node.consequent, returnStatements);
+        }
+        if (node.alternate) {
+          getReturnStatements(node.alternate, returnStatements);
+        }
+      } else if (node.type === 'ReturnStatement') {
+        returnStatements.push(node);
+      } else if (Array.isArray(node.body)) {
+        node.body.forEach((item) => {
+          if (item.type === 'IfStatement') {
+            getReturnStatements(item, returnStatements);
+          }
+
+          if (item.type === 'ReturnStatement') {
+            returnStatements.push(item);
+          }
+        });
+      }
+
+      return returnStatements;
+    }
+
+    /**
+     * Checks if the given node is a function expression or arrow function,
+     * and checks if there is a missing key prop in return statement's arguments
+     * @param {ASTNode} node
+     */
+    function checkFunctionsBlockStatement(node) {
+      if (astUtil.isFunctionLikeExpression(node)) {
+        if (node.body.type === 'BlockStatement') {
+          getReturnStatements(node.body)
+            .filter((returnStatement) => returnStatement && returnStatement.argument)
+            .forEach((returnStatement) => {
+              checkIteratorElement(returnStatement.argument);
+            });
+        }
+      }
+    }
+
+    /**
+     * Checks if the given node is an arrow function that has an JSX Element or JSX Fragment in its body,
+     * and the JSX is missing a key prop
+     * @param {ASTNode} node
+     */
+    function checkArrowFunctionWithJSX(node) {
+      const isArrFn = node && node.type === 'ArrowFunctionExpression';
+      const shouldCheckNode = (n) => n && (n.type === 'JSXElement' || n.type === 'JSXFragment');
+      if (isArrFn && shouldCheckNode(node.body)) {
+        checkIteratorElement(node.body);
+      }
+      if (node.body.type === 'ConditionalExpression') {
+        if (shouldCheckNode(node.body.consequent)) {
+          checkIteratorElement(node.body.consequent);
+        }
+        if (shouldCheckNode(node.body.alternate)) {
+          checkIteratorElement(node.body.alternate);
+        }
+      } else if (node.body.type === 'LogicalExpression' && shouldCheckNode(node.body.right)) {
+        checkIteratorElement(node.body.right);
+      }
+    }
+
+    const childrenToArraySelector = `:matches(
+      CallExpression
+        [callee.object.object.name=${reactPragma}]
+        [callee.object.property.name=Children]
+        [callee.property.name=toArray],
+      CallExpression
+        [callee.object.name=Children]
+        [callee.property.name=toArray]
+    )`.replace(/\s/g, '');
+    let isWithinChildrenToArray = false;
+
+    const seen = new WeakSet();
+
+    return {
+      [childrenToArraySelector]() {
+        isWithinChildrenToArray = true;
+      },
+
+      [`${childrenToArraySelector}:exit`]() {
+        isWithinChildrenToArray = false;
+      },
+
+      'ArrayExpression, JSXElement > JSXElement'(node) {
+        if (isWithinChildrenToArray) {
+          return;
+        }
+
+        const jsx = (node.type === 'ArrayExpression' ? node.elements : node.parent.children).filter((x) => x && x.type === 'JSXElement');
+        if (jsx.length === 0) {
+          return;
+        }
+
+        const map = {};
+        jsx.forEach((element) => {
+          const attrs = element.openingElement.attributes;
+          const keys = attrs.filter((x) => x.name && x.name.name === 'key');
+
+          if (keys.length === 0) {
+            if (node.type === 'ArrayExpression') {
+              report(context, messages.missingArrayKey, 'missingArrayKey', {
+                node: element,
+              });
+            }
+          } else {
+            keys.forEach((attr) => {
+              const value = getText(context, attr.value);
+              if (!map[value]) { map[value] = []; }
+              map[value].push(attr);
+
+              if (checkKeyMustBeforeSpread && isKeyAfterSpread(attrs)) {
+                report(context, messages.keyBeforeSpread, 'keyBeforeSpread', {
+                  node: node.type === 'ArrayExpression' ? node : node.parent,
+                });
+              }
+            });
+          }
+        });
+
+        if (warnOnDuplicates) {
+          values(map).filter((v) => v.length > 1).forEach((v) => {
+            v.forEach((n) => {
+              if (!seen.has(n)) {
+                seen.add(n);
+                report(context, messages.nonUniqueKeys, 'nonUniqueKeys', {
+                  node: n,
+                });
+              }
+            });
+          });
+        }
+      },
+
+      JSXFragment(node) {
+        if (!checkFragmentShorthand || isWithinChildrenToArray) {
+          return;
+        }
+
+        if (node.parent.type === 'ArrayExpression') {
+          report(context, messages.missingArrayKeyUsePrag, 'missingArrayKeyUsePrag', {
+            node,
+            data: {
+              reactPrag: reactPragma,
+              fragPrag: fragmentPragma,
+            },
+          });
+        }
+      },
+
+      // Array.prototype.map
+      // eslint-disable-next-line no-multi-str
+      'CallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
+       CallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"],\
+       OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
+       OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"]'(node) {
+        if (isWithinChildrenToArray) {
+          return;
+        }
+
+        const fn = node.arguments.length > 0 && node.arguments[0];
+        if (!fn || !astUtil.isFunctionLikeExpression(fn)) {
+          return;
+        }
+
+        checkArrowFunctionWithJSX(fn);
+
+        checkFunctionsBlockStatement(fn);
+      },
+
+      // Array.from
+      'CallExpression[callee.type="MemberExpression"][callee.property.name="from"]'(node) {
+        if (isWithinChildrenToArray) {
+          return;
+        }
+
+        const fn = node.arguments.length > 1 && node.arguments[1];
+        if (!astUtil.isFunctionLikeExpression(fn)) {
+          return;
+        }
+
+        checkArrowFunctionWithJSX(fn);
+
+        checkFunctionsBlockStatement(fn);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-max-depth.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-max-depth.d.ts","sourceRoot":"","sources":["jsx-max-depth.js"],"names":[],"mappings":"wBAsBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-depth.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,162 @@
+/**
+ * @fileoverview Validate JSX maximum depth
+ * @author Chris<wfsr@foxmail.com>
+ */
+
+'use strict';
+
+const has = require('hasown');
+const includes = require('array-includes');
+const variableUtil = require('../util/variable');
+const jsxUtil = require('../util/jsx');
+const docsUrl = require('../util/docsUrl');
+const reportC = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  wrongDepth: 'Expected the depth of nested jsx elements to be <= {{needed}}, but found {{found}}.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce JSX maximum depth',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-max-depth'),
+    },
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          max: {
+            type: 'integer',
+            minimum: 0,
+          },
+        },
+        additionalProperties: false,
+      },
+    ],
+  },
+  create(context) {
+    const DEFAULT_DEPTH = 2;
+
+    const option = context.options[0] || {};
+    const maxDepth = has(option, 'max') ? option.max : DEFAULT_DEPTH;
+
+    function isExpression(node) {
+      return node.type === 'JSXExpressionContainer';
+    }
+
+    function hasJSX(node) {
+      return jsxUtil.isJSX(node) || (isExpression(node) && jsxUtil.isJSX(node.expression));
+    }
+
+    function isLeaf(node) {
+      const children = node.children;
+
+      return !children || children.length === 0 || !children.some(hasJSX);
+    }
+
+    function getDepth(node) {
+      let count = 0;
+
+      while (jsxUtil.isJSX(node.parent) || isExpression(node.parent)) {
+        node = node.parent;
+        if (jsxUtil.isJSX(node)) {
+          count += 1;
+        }
+      }
+
+      return count;
+    }
+
+    function report(node, depth) {
+      reportC(context, messages.wrongDepth, 'wrongDepth', {
+        node,
+        data: {
+          found: depth,
+          needed: maxDepth,
+        },
+      });
+    }
+
+    function findJSXElementOrFragment(startNode, name, previousReferences) {
+      function find(refs, prevRefs) {
+        for (let i = refs.length - 1; i >= 0; i--) {
+          if (typeof refs[i].writeExpr !== 'undefined') {
+            const writeExpr = refs[i].writeExpr;
+
+            return (jsxUtil.isJSX(writeExpr)
+              && writeExpr)
+              || ((writeExpr && writeExpr.type === 'Identifier')
+              && findJSXElementOrFragment(startNode, writeExpr.name, prevRefs));
+          }
+        }
+
+        return null;
+      }
+
+      const variable = variableUtil.getVariableFromContext(context, startNode, name);
+      if (variable && variable.references) {
+        const containDuplicates = previousReferences.some((ref) => includes(variable.references, ref));
+
+        // Prevent getting stuck in circular references
+        if (containDuplicates) {
+          return false;
+        }
+
+        return find(variable.references, previousReferences.concat(variable.references));
+      }
+
+      return false;
+    }
+
+    function checkDescendant(baseDepth, children) {
+      baseDepth += 1;
+      (children || []).filter((node) => hasJSX(node)).forEach((node) => {
+        if (baseDepth > maxDepth) {
+          report(node, baseDepth);
+        } else if (!isLeaf(node)) {
+          checkDescendant(baseDepth, node.children);
+        }
+      });
+    }
+
+    function handleJSX(node) {
+      if (!isLeaf(node)) {
+        return;
+      }
+
+      const depth = getDepth(node);
+      if (depth > maxDepth) {
+        report(node, depth);
+      }
+    }
+
+    return {
+      JSXElement: handleJSX,
+      JSXFragment: handleJSX,
+
+      JSXExpressionContainer(node) {
+        if (node.expression.type !== 'Identifier') {
+          return;
+        }
+
+        const element = findJSXElementOrFragment(node, node.expression.name, []);
+
+        if (element) {
+          const baseDepth = getDepth(node);
+          checkDescendant(baseDepth, element.children);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-max-props-per-line.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-max-props-per-line.d.ts","sourceRoot":"","sources":["jsx-max-props-per-line.js"],"names":[],"mappings":"wBA0BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,155 @@
+/**
+ * @fileoverview Limit maximum of props on a single line in JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const report = require('../util/report');
+
+function getPropName(context, propNode) {
+  if (propNode.type === 'JSXSpreadAttribute') {
+    return getText(context, propNode.argument);
+  }
+  return propNode.name.name;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  newLine: 'Prop `{{prop}}` must be placed on a new line',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce maximum of props on a single line in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-max-props-per-line'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      anyOf: [{
+        type: 'object',
+        properties: {
+          maximum: {
+            type: 'object',
+            properties: {
+              single: {
+                type: 'integer',
+                minimum: 1,
+              },
+              multi: {
+                type: 'integer',
+                minimum: 1,
+              },
+            },
+          },
+        },
+        additionalProperties: false,
+      }, {
+        type: 'object',
+        properties: {
+          maximum: {
+            type: 'number',
+            minimum: 1,
+          },
+          when: {
+            type: 'string',
+            enum: ['always', 'multiline'],
+          },
+        },
+        additionalProperties: false,
+      }],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const maximum = configuration.maximum || 1;
+
+    const maxConfig = typeof maximum === 'number'
+      ? {
+        single: configuration.when === 'multiline' ? Infinity : maximum,
+        multi: maximum,
+      }
+      : {
+        single: maximum.single || Infinity,
+        multi: maximum.multi || Infinity,
+      };
+
+    function generateFixFunction(line, max) {
+      const output = [];
+      const front = line[0].range[0];
+      const back = line[line.length - 1].range[1];
+
+      for (let i = 0; i < line.length; i += max) {
+        const nodes = line.slice(i, i + max);
+        output.push(nodes.reduce((prev, curr) => {
+          if (prev === '') {
+            return getText(context, curr);
+          }
+          return `${prev} ${getText(context, curr)}`;
+        }, ''));
+      }
+
+      const code = output.join('\n');
+
+      return function fix(fixer) {
+        return fixer.replaceTextRange([front, back], code);
+      };
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        if (!node.attributes.length) {
+          return;
+        }
+
+        const isSingleLineTag = node.loc.start.line === node.loc.end.line;
+
+        if ((isSingleLineTag ? maxConfig.single : maxConfig.multi) === Infinity) {
+          return;
+        }
+
+        const firstProp = node.attributes[0];
+        const linePartitionedProps = [[firstProp]];
+
+        node.attributes.reduce((last, decl) => {
+          if (last.loc.end.line === decl.loc.start.line) {
+            linePartitionedProps[linePartitionedProps.length - 1].push(decl);
+          } else {
+            linePartitionedProps.push([decl]);
+          }
+          return decl;
+        });
+
+        linePartitionedProps.forEach((propsInLine) => {
+          const maxPropsCountPerLine = isSingleLineTag && propsInLine[0].loc.start.line === node.loc.start.line
+            ? maxConfig.single
+            : maxConfig.multi;
+
+          if (propsInLine.length > maxPropsCountPerLine) {
+            const name = getPropName(context, propsInLine[maxPropsCountPerLine]);
+            report(context, messages.newLine, 'newLine', {
+              node: propsInLine[maxPropsCountPerLine],
+              data: {
+                prop: name,
+              },
+              fix: generateFixFunction(propsInLine, maxPropsCountPerLine),
+            });
+          }
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-newline.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-newline.d.ts","sourceRoot":"","sources":["jsx-newline.js"],"names":[],"mappings":"wBA0BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,166 @@
+/**
+ * @fileoverview Require or prevent a new line after jsx elements and expressions.
+ * @author Johnny Zabala
+ * @author Joseph Stiles
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  require: 'JSX element should start in a new line',
+  prevent: 'JSX element should not start in a new line',
+  allowMultilines: 'Multiline JSX elements should start in a new line',
+};
+
+function isMultilined(node) {
+  return node && node.loc.start.line !== node.loc.end.line;
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Require or prevent a new line after jsx elements and expressions.',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-newline'),
+    },
+    fixable: 'code',
+
+    messages,
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          prevent: {
+            default: false,
+            type: 'boolean',
+          },
+          allowMultilines: {
+            default: false,
+            type: 'boolean',
+          },
+        },
+        additionalProperties: false,
+        if: {
+          properties: {
+            allowMultilines: {
+              const: true,
+            },
+          },
+        },
+        then: {
+          properties: {
+            prevent: {
+              const: true,
+            },
+          },
+          required: [
+            'prevent',
+          ],
+        },
+      },
+    ],
+  },
+  create(context) {
+    const jsxElementParents = new Set();
+
+    function isBlockCommentInCurlyBraces(element) {
+      const elementRawValue = getText(context, element);
+      return /^\s*{\/\*/.test(elementRawValue);
+    }
+
+    function isNonBlockComment(element) {
+      return !isBlockCommentInCurlyBraces(element) && (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer');
+    }
+
+    return {
+      'Program:exit'() {
+        jsxElementParents.forEach((parent) => {
+          parent.children.forEach((element, index, elements) => {
+            if (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer') {
+              const configuration = context.options[0] || {};
+              const prevent = configuration.prevent || false;
+              const allowMultilines = configuration.allowMultilines || false;
+
+              const firstAdjacentSibling = elements[index + 1];
+              const secondAdjacentSibling = elements[index + 2];
+
+              const hasSibling = firstAdjacentSibling
+              && secondAdjacentSibling
+              && (firstAdjacentSibling.type === 'Literal' || firstAdjacentSibling.type === 'JSXText');
+
+              if (!hasSibling) return;
+
+              // Check adjacent sibling has the proper amount of newlines
+              const isWithoutNewLine = !/\n\s*\n/.test(firstAdjacentSibling.value);
+
+              if (isBlockCommentInCurlyBraces(element)) return;
+              if (
+                allowMultilines
+                && (
+                  isMultilined(element)
+                  || isMultilined(elements.slice(index + 2).find(isNonBlockComment))
+                )
+              ) {
+                if (!isWithoutNewLine) return;
+
+                const regex = /(\n)(?!.*\1)/g;
+                const replacement = '\n\n';
+                const messageId = 'allowMultilines';
+
+                report(context, messages[messageId], messageId, {
+                  node: secondAdjacentSibling,
+                  fix(fixer) {
+                    return fixer.replaceText(
+                      firstAdjacentSibling,
+                      getText(context, firstAdjacentSibling).replace(regex, replacement)
+                    );
+                  },
+                });
+
+                return;
+              }
+
+              if (isWithoutNewLine === prevent) return;
+
+              const messageId = prevent
+                ? 'prevent'
+                : 'require';
+
+              const regex = prevent
+                ? /(\n\n)(?!.*\1)/g
+                : /(\n)(?!.*\1)/g;
+
+              const replacement = prevent
+                ? '\n'
+                : '\n\n';
+
+              report(context, messages[messageId], messageId, {
+                node: secondAdjacentSibling,
+                fix(fixer) {
+                  return fixer.replaceText(
+                    firstAdjacentSibling,
+                    // double or remove the last newline
+                    getText(context, firstAdjacentSibling).replace(regex, replacement)
+                  );
+                },
+              });
+            }
+          });
+        });
+      },
+      ':matches(JSXElement, JSXFragment) > :matches(JSXElement, JSXExpressionContainer)': (node) => {
+        jsxElementParents.add(node.parent);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-bind.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-bind.d.ts","sourceRoot":"","sources":["jsx-no-bind.js"],"names":[],"mappings":"wBA2BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,209 @@
+/**
+ * @fileoverview Prevents usage of Function.prototype.bind and arrow functions
+ *               in React component props.
+ * @author Daniel Lo Nigro <dan.cx>
+ * @author Jacky Ho
+ */
+
+'use strict';
+
+const propName = require('jsx-ast-utils/propName');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+const getAncestors = require('../util/eslint').getAncestors;
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+const messages = {
+  bindCall: 'JSX props should not use .bind()',
+  arrowFunc: 'JSX props should not use arrow functions',
+  bindExpression: 'JSX props should not use ::',
+  func: 'JSX props should not use functions',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow `.bind()` or arrow functions in JSX props',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('jsx-no-bind'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowArrowFunctions: {
+          default: false,
+          type: 'boolean',
+        },
+        allowBind: {
+          default: false,
+          type: 'boolean',
+        },
+        allowFunctions: {
+          default: false,
+          type: 'boolean',
+        },
+        ignoreRefs: {
+          default: false,
+          type: 'boolean',
+        },
+        ignoreDOMComponents: {
+          default: false,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+
+    // Keep track of all the variable names pointing to a bind call,
+    // bind expression or an arrow function in different block statements
+    const blockVariableNameSets = {};
+
+    /**
+     * @param {string | number} blockStart
+     */
+    function setBlockVariableNameSet(blockStart) {
+      blockVariableNameSets[blockStart] = {
+        arrowFunc: new Set(),
+        bindCall: new Set(),
+        bindExpression: new Set(),
+        func: new Set(),
+      };
+    }
+
+    function getNodeViolationType(node) {
+      if (
+        !configuration.allowBind
+        && astUtil.isCallExpression(node)
+        && node.callee.type === 'MemberExpression'
+        && node.callee.property.type === 'Identifier'
+        && node.callee.property.name === 'bind'
+      ) {
+        return 'bindCall';
+      }
+      if (node.type === 'ConditionalExpression') {
+        return getNodeViolationType(node.test)
+               || getNodeViolationType(node.consequent)
+               || getNodeViolationType(node.alternate);
+      }
+      if (!configuration.allowArrowFunctions && node.type === 'ArrowFunctionExpression') {
+        return 'arrowFunc';
+      }
+      if (
+        !configuration.allowFunctions
+        && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration')
+      ) {
+        return 'func';
+      }
+      if (!configuration.allowBind && node.type === 'BindExpression') {
+        return 'bindExpression';
+      }
+
+      return null;
+    }
+
+    /**
+     * @param {string | number} violationType
+     * @param {unknown} variableName
+     * @param {string | number} blockStart
+     */
+    function addVariableNameToSet(violationType, variableName, blockStart) {
+      blockVariableNameSets[blockStart][violationType].add(variableName);
+    }
+
+    function getBlockStatementAncestors(node) {
+      return getAncestors(context, node).filter(
+        (ancestor) => ancestor.type === 'BlockStatement'
+      ).reverse();
+    }
+
+    function reportVariableViolation(node, name, blockStart) {
+      const blockSets = blockVariableNameSets[blockStart];
+      const violationTypes = Object.keys(blockSets);
+
+      return violationTypes.find((type) => {
+        if (blockSets[type].has(name)) {
+          report(context, messages[type], type, {
+            node,
+          });
+          return true;
+        }
+
+        return false;
+      });
+    }
+
+    function findVariableViolation(node, name) {
+      getBlockStatementAncestors(node).find(
+        (block) => reportVariableViolation(node, name, block.range[0])
+      );
+    }
+
+    return {
+      BlockStatement(node) {
+        setBlockVariableNameSet(node.range[0]);
+      },
+
+      FunctionDeclaration(node) {
+        const blockAncestors = getBlockStatementAncestors(node);
+        const variableViolationType = getNodeViolationType(node);
+
+        if (blockAncestors.length > 0 && variableViolationType) {
+          addVariableNameToSet(variableViolationType, node.id.name, blockAncestors[0].range[0]);
+        }
+      },
+
+      VariableDeclarator(node) {
+        if (!node.init) {
+          return;
+        }
+        const blockAncestors = getBlockStatementAncestors(node);
+        const variableViolationType = getNodeViolationType(node.init);
+
+        if (
+          blockAncestors.length > 0
+          && variableViolationType
+          && 'kind' in node.parent
+          && node.parent.kind === 'const' // only support const right now
+        ) {
+          addVariableNameToSet(variableViolationType, 'name' in node.id ? node.id.name : undefined, blockAncestors[0].range[0]);
+        }
+      },
+
+      JSXAttribute(node) {
+        const isRef = configuration.ignoreRefs && propName(node) === 'ref';
+        if (isRef || !node.value || !node.value.expression) {
+          return;
+        }
+        const isDOMComponent = jsxUtil.isDOMComponent(node.parent);
+        if (configuration.ignoreDOMComponents && isDOMComponent) {
+          return;
+        }
+        const valueNode = node.value.expression;
+        const valueNodeType = valueNode.type;
+        const nodeViolationType = getNodeViolationType(valueNode);
+
+        if (valueNodeType === 'Identifier') {
+          findVariableViolation(node, valueNode.name);
+        } else if (nodeViolationType) {
+          report(context, messages[nodeViolationType], nodeViolationType, {
+            node,
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-comment-textnodes.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-comment-textnodes.d.ts","sourceRoot":"","sources":["jsx-no-comment-textnodes.js"],"names":[],"mappings":"wBAyCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-comment-textnodes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,71 @@
+/**
+ * @fileoverview Comments inside children section of tag should be placed inside braces.
+ * @author Ben Vinegar
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  putCommentInBraces: 'Comments inside children section of tag should be placed inside braces',
+};
+
+/**
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @returns {void}
+ */
+function checkText(context, node) {
+  // since babel-eslint has the wrong node.raw, we'll get the source text
+  const rawValue = getText(context, node);
+  if (/^\s*\/(\/|\*)/m.test(rawValue)) {
+    // inside component, e.g. <div>literal</div>
+    if (
+      node.parent.type !== 'JSXAttribute'
+      && node.parent.type !== 'JSXExpressionContainer'
+      && node.parent.type.indexOf('JSX') !== -1
+    ) {
+      report(context, messages.putCommentInBraces, 'putCommentInBraces', {
+        node,
+      });
+    }
+  }
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow comments from being inserted as text nodes',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('jsx-no-comment-textnodes'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      Literal(node) {
+        checkText(context, node);
+      },
+      JSXText(node) {
+        checkText(context, node);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-constructed-context-values.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-constructed-context-values.d.ts","sourceRoot":"","sources":["jsx-no-constructed-context-values.js"],"names":[],"mappings":"wBA2KW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-constructed-context-values.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,272 @@
+/**
+ * @fileoverview Prevents jsx context provider values from taking values that
+ *               will cause needless rerenders.
+ * @author Dylan Oshima
+ */
+
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const getScope = require('../util/eslint').getScope;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+// Recursively checks if an element is a construction.
+// A construction is a variable that changes identity every render.
+function isConstruction(node, callScope) {
+  switch (node.type) {
+    case 'Literal':
+      if (node.regex != null) {
+        return { type: 'regular expression', node };
+      }
+      return null;
+    case 'Identifier': {
+      const variableScoping = callScope.set.get(node.name);
+
+      if (variableScoping == null || variableScoping.defs == null) {
+        // If it's not in scope, we don't care.
+        return null; // Handled
+      }
+
+      // Gets the last variable identity
+      const variableDefs = variableScoping.defs;
+      const def = variableDefs[variableDefs.length - 1];
+      if (def != null
+        && def.type !== 'Variable'
+        && def.type !== 'FunctionName'
+      ) {
+        // Parameter or an unusual pattern. Bail out.
+        return null; // Unhandled
+      }
+
+      if (def.node.type === 'FunctionDeclaration') {
+        return { type: 'function declaration', node: def.node, usage: node };
+      }
+
+      const init = def.node.init;
+      if (init == null) {
+        return null;
+      }
+
+      const initConstruction = isConstruction(init, callScope);
+      if (initConstruction == null) {
+        return null;
+      }
+
+      return {
+        type: initConstruction.type,
+        node: initConstruction.node,
+        usage: node,
+      };
+    }
+    case 'ObjectExpression':
+      // Any object initialized inline will create a new identity
+      return { type: 'object', node };
+    case 'ArrayExpression':
+      return { type: 'array', node };
+    case 'ArrowFunctionExpression':
+    case 'FunctionExpression':
+      // Functions that are initialized inline will have a new identity
+      return { type: 'function expression', node };
+    case 'ClassExpression':
+      return { type: 'class expression', node };
+    case 'NewExpression':
+      // `const a = new SomeClass();` is a construction
+      return { type: 'new expression', node };
+    case 'ConditionalExpression':
+      return (isConstruction(node.consequent, callScope)
+        || isConstruction(node.alternate, callScope)
+      );
+    case 'LogicalExpression':
+      return (isConstruction(node.left, callScope)
+        || isConstruction(node.right, callScope)
+      );
+    case 'MemberExpression': {
+      const objConstruction = isConstruction(node.object, callScope);
+      if (objConstruction == null) {
+        return null;
+      }
+      return {
+        type: objConstruction.type,
+        node: objConstruction.node,
+        usage: node.object,
+      };
+    }
+    case 'JSXFragment':
+      return { type: 'JSX fragment', node };
+    case 'JSXElement':
+      return { type: 'JSX element', node };
+    case 'AssignmentExpression': {
+      const construct = isConstruction(node.right, callScope);
+      if (construct != null) {
+        return {
+          type: 'assignment expression',
+          node: construct.node,
+          usage: node,
+        };
+      }
+      return null;
+    }
+    case 'TypeCastExpression':
+    case 'TSAsExpression':
+      return isConstruction(node.expression, callScope);
+    default:
+      return null;
+  }
+}
+
+function isReactContext(context, node) {
+  let scope = getScope(context, node);
+  let variableScoping = null;
+  const contextName = node.name;
+
+  while (scope && !variableScoping) { // Walk up the scope chain to find the variable
+    variableScoping = scope.set.get(contextName);
+    scope = scope.upper;
+  }
+
+  if (!variableScoping) { // Context was not found in scope
+    return false;
+  }
+
+  // Get the variable's definition
+  const def = variableScoping.defs[0];
+
+  if (!def || def.node.type !== 'VariableDeclarator') {
+    return false;
+  }
+
+  const init = def.node.init; // Variable initializer
+
+  const isCreateContext = init
+    && init.type === 'CallExpression'
+    && (
+      (
+        init.callee.type === 'Identifier'
+        && init.callee.name === 'createContext'
+      ) || (
+        init.callee.type === 'MemberExpression'
+        && init.callee.object.name === 'React'
+        && init.callee.property.name === 'createContext'
+      )
+    );
+
+  return isCreateContext;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  withIdentifierMsg: "The '{{variableName}}' {{type}} (at line {{nodeLine}}) passed as the value prop to the Context provider (at line {{usageLine}}) changes every render. To fix this consider wrapping it in a useMemo hook.",
+  withIdentifierMsgFunc: "The '{{variableName}}' {{type}} (at line {{nodeLine}}) passed as the value prop to the Context provider (at line {{usageLine}}) changes every render. To fix this consider wrapping it in a useCallback hook.",
+  defaultMsg: 'The {{type}} passed as the value prop to the Context provider (at line {{nodeLine}}) changes every render. To fix this consider wrapping it in a useMemo hook.',
+  defaultMsgFunc: 'The {{type}} passed as the value prop to the Context provider (at line {{nodeLine}}) changes every render. To fix this consider wrapping it in a useCallback hook.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallows JSX context provider values from taking values that will cause needless rerenders',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('jsx-no-constructed-context-values'),
+    },
+    messages,
+    schema: false,
+  },
+
+  // eslint-disable-next-line arrow-body-style
+  create: Components.detect((context, components, utils) => {
+    return {
+      JSXOpeningElement(node) {
+        const openingElementName = node.name;
+
+        if (openingElementName.type === 'JSXMemberExpression') {
+          const isJSXContext = openingElementName.property.name === 'Provider';
+          if (!isJSXContext) {
+            // Member is not Provider
+            return;
+          }
+        } else if (openingElementName.type === 'JSXIdentifier') {
+          const isJSXContext = isReactContext(context, openingElementName);
+          if (!isJSXContext) {
+            // Member is not context
+            return;
+          }
+        } else {
+          return;
+        }
+
+        // Contexts can take in more than just a value prop
+        // so we need to iterate through all of them
+        const jsxValueAttribute = node.attributes.find(
+          (attribute) => attribute.type === 'JSXAttribute' && attribute.name.name === 'value'
+        );
+
+        if (jsxValueAttribute == null) {
+          // No value prop was passed
+          return;
+        }
+
+        const valueNode = jsxValueAttribute.value;
+        if (!valueNode) {
+          // attribute is a boolean shorthand
+          return;
+        }
+        if (valueNode.type !== 'JSXExpressionContainer') {
+          // value could be a literal
+          return;
+        }
+
+        const valueExpression = valueNode.expression;
+        const invocationScope = getScope(context, node);
+
+        // Check if the value prop is a construction
+        const constructInfo = isConstruction(valueExpression, invocationScope);
+        if (constructInfo == null) {
+          return;
+        }
+
+        if (!utils.getParentComponent(node)) {
+          return;
+        }
+
+        // Report found error
+        const constructType = constructInfo.type;
+        const constructNode = constructInfo.node;
+        const constructUsage = constructInfo.usage;
+        const data = {
+          type: constructType, nodeLine: constructNode.loc.start.line,
+        };
+        let messageId = 'defaultMsg';
+
+        // Variable passed to value prop
+        if (constructUsage != null) {
+          messageId = 'withIdentifierMsg';
+          data.usageLine = constructUsage.loc.start.line;
+          data.variableName = constructUsage.name;
+        }
+
+        // Type of expression
+        if (
+          constructType === 'function expression'
+          || constructType === 'function declaration'
+        ) {
+          messageId += 'Func';
+        }
+
+        report(context, messages[messageId], messageId, {
+          node: constructNode,
+          data,
+        });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-duplicate-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-duplicate-props.d.ts","sourceRoot":"","sources":["jsx-no-duplicate-props.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,77 @@
+/**
+ * @fileoverview Enforce no duplicate props
+ * @author Markus Ånöstam
+ */
+
+'use strict';
+
+const has = require('hasown');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noDuplicateProps: 'No duplicate props allowed',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow duplicate properties in JSX',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('jsx-no-duplicate-props'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignoreCase: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const ignoreCase = configuration.ignoreCase || false;
+
+    return {
+      JSXOpeningElement(node) {
+        const props = {};
+
+        node.attributes.forEach((decl) => {
+          if (decl.type === 'JSXSpreadAttribute') {
+            return;
+          }
+
+          let name = decl.name.name;
+
+          if (typeof name !== 'string') {
+            return;
+          }
+
+          if (ignoreCase) {
+            name = name.toLowerCase();
+          }
+
+          if (has(props, name)) {
+            report(context, messages.noDuplicateProps, 'noDuplicateProps', {
+              node: decl,
+            });
+          } else {
+            props[name] = 1;
+          }
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-leaked-render.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-leaked-render.d.ts","sourceRoot":"","sources":["jsx-no-leaked-render.js"],"names":[],"mappings":"wBA+GW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-leaked-render.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,204 @@
+/**
+ * @fileoverview Prevent problematic leaked values from being rendered
+ * @author Mario Beltrán
+ */
+
+'use strict';
+
+const find = require('es-iterator-helpers/Iterator.prototype.find');
+const from = require('es-iterator-helpers/Iterator.from');
+
+const getText = require('../util/eslint').getText;
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const variableUtil = require('../util/variable');
+const testReactVersion = require('../util/version').testReactVersion;
+const isParenthesized = require('../util/ast').isParenthesized;
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const messages = {
+  noPotentialLeakedRender: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
+};
+
+const COERCE_STRATEGY = 'coerce';
+const TERNARY_STRATEGY = 'ternary';
+const DEFAULT_VALID_STRATEGIES = [TERNARY_STRATEGY, COERCE_STRATEGY];
+const COERCE_VALID_LEFT_SIDE_EXPRESSIONS = ['UnaryExpression', 'BinaryExpression', 'CallExpression'];
+const TERNARY_INVALID_ALTERNATE_VALUES = [undefined, null, false];
+
+function trimLeftNode(node) {
+  // Remove double unary expression (boolean coercion), so we avoid trimming valid negations
+  if (node.type === 'UnaryExpression' && node.argument.type === 'UnaryExpression') {
+    return trimLeftNode(node.argument.argument);
+  }
+
+  return node;
+}
+
+function getIsCoerceValidNestedLogicalExpression(node) {
+  if (node.type === 'LogicalExpression') {
+    return getIsCoerceValidNestedLogicalExpression(node.left) && getIsCoerceValidNestedLogicalExpression(node.right);
+  }
+
+  return COERCE_VALID_LEFT_SIDE_EXPRESSIONS.some((validExpression) => validExpression === node.type);
+}
+
+function extractExpressionBetweenLogicalAnds(node) {
+  if (node.type !== 'LogicalExpression') return [node];
+  if (node.operator !== '&&') return [node];
+  return [].concat(
+    extractExpressionBetweenLogicalAnds(node.left),
+    extractExpressionBetweenLogicalAnds(node.right)
+  );
+}
+
+function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNode) {
+  const rightSideText = getText(context, rightNode);
+
+  if (fixStrategy === COERCE_STRATEGY) {
+    const expressions = extractExpressionBetweenLogicalAnds(leftNode);
+    const newText = expressions.map((node) => {
+      let nodeText = getText(context, node);
+      if (isParenthesized(context, node)) {
+        nodeText = `(${nodeText})`;
+      }
+      if (node.parent && node.parent.type === 'ConditionalExpression' && node.parent.consequent.value === false) {
+        return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!'}${nodeText}`;
+      }
+      return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!!'}${nodeText}`;
+    }).join(' && ');
+
+    if (rightNode.parent && rightNode.parent.type === 'ConditionalExpression' && rightNode.parent.consequent.value === false) {
+      const consequentVal = rightNode.parent.consequent.raw || rightNode.parent.consequent.name;
+      const alternateVal = rightNode.parent.alternate.raw || rightNode.parent.alternate.name;
+      if (rightNode.parent.test && rightNode.parent.test.type === 'LogicalExpression') {
+        return fixer.replaceText(reportedNode, `${newText} ? ${consequentVal} : ${alternateVal}`);
+      }
+      return fixer.replaceText(reportedNode, `${newText} && ${alternateVal}`);
+    }
+
+    if (rightNode.type === 'ConditionalExpression' || rightNode.type === 'LogicalExpression') {
+      return fixer.replaceText(reportedNode, `${newText} && (${rightSideText})`);
+    }
+    if (rightNode.type === 'JSXElement') {
+      const rightSideTextLines = rightSideText.split('\n');
+      if (rightSideTextLines.length > 1) {
+        const rightSideTextLastLine = rightSideTextLines[rightSideTextLines.length - 1];
+        const indentSpacesStart = ' '.repeat(rightSideTextLastLine.search(/\S/));
+        const indentSpacesClose = ' '.repeat(rightSideTextLastLine.search(/\S/) - 2);
+        return fixer.replaceText(reportedNode, `${newText} && (\n${indentSpacesStart}${rightSideText}\n${indentSpacesClose})`);
+      }
+    }
+    if (rightNode.type === 'Literal') {
+      return null;
+    }
+    return fixer.replaceText(reportedNode, `${newText} && ${rightSideText}`);
+  }
+
+  if (fixStrategy === TERNARY_STRATEGY) {
+    let leftSideText = getText(context, trimLeftNode(leftNode));
+    if (isParenthesized(context, leftNode)) {
+      leftSideText = `(${leftSideText})`;
+    }
+    return fixer.replaceText(reportedNode, `${leftSideText} ? ${rightSideText} : null`);
+  }
+
+  throw new TypeError('Invalid value for "validStrategies" option');
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow problematic leaked values from being rendered',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('jsx-no-leaked-render'),
+    },
+
+    messages,
+
+    fixable: 'code',
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          validStrategies: {
+            type: 'array',
+            items: {
+              enum: [
+                TERNARY_STRATEGY,
+                COERCE_STRATEGY,
+              ],
+            },
+            uniqueItems: true,
+            default: DEFAULT_VALID_STRATEGIES,
+          },
+        },
+        additionalProperties: false,
+      },
+    ],
+  },
+
+  create(context) {
+    const config = context.options[0] || {};
+    const validStrategies = new Set(config.validStrategies || DEFAULT_VALID_STRATEGIES);
+    const fixStrategy = find(from(validStrategies), () => true);
+
+    return {
+      'JSXExpressionContainer > LogicalExpression[operator="&&"]'(node) {
+        const leftSide = node.left;
+
+        const isCoerceValidLeftSide = COERCE_VALID_LEFT_SIDE_EXPRESSIONS
+          .some((validExpression) => validExpression === leftSide.type);
+        if (validStrategies.has(COERCE_STRATEGY)) {
+          if (isCoerceValidLeftSide || getIsCoerceValidNestedLogicalExpression(leftSide)) {
+            return;
+          }
+          const leftSideVar = variableUtil.getVariableFromContext(context, node, leftSide.name);
+          if (leftSideVar) {
+            const leftSideValue = leftSideVar.defs
+              && leftSideVar.defs.length
+              && leftSideVar.defs[0].node.init
+              && leftSideVar.defs[0].node.init.value;
+            if (typeof leftSideValue === 'boolean') {
+              return;
+            }
+          }
+        }
+
+        if (testReactVersion(context, '>= 18') && leftSide.type === 'Literal' && leftSide.value === '') {
+          return;
+        }
+        report(context, messages.noPotentialLeakedRender, 'noPotentialLeakedRender', {
+          node,
+          fix(fixer) {
+            return ruleFixer(context, fixStrategy, fixer, node, leftSide, node.right);
+          },
+        });
+      },
+
+      'JSXExpressionContainer > ConditionalExpression'(node) {
+        if (validStrategies.has(TERNARY_STRATEGY)) {
+          return;
+        }
+
+        const isValidTernaryAlternate = TERNARY_INVALID_ALTERNATE_VALUES.indexOf(node.alternate.value) === -1;
+        const isJSXElementAlternate = node.alternate.type === 'JSXElement';
+        if (isValidTernaryAlternate || isJSXElementAlternate) {
+          return;
+        }
+
+        report(context, messages.noPotentialLeakedRender, 'noPotentialLeakedRender', {
+          node,
+          fix(fixer) {
+            return ruleFixer(context, fixStrategy, fixer, node, node.test, node.consequent);
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+declare const _exports: RuleModule;
+export = _exports;
+export type RuleModule = import("eslint").Rule.RuleModule;
+export type Config = {
+    type: "element";
+} & import("../../types/rules/jsx-no-literals").ElementConfigProperties & import("../../types/rules/jsx-no-literals").ElementOverrides;
+export type RawConfig = import("../../types/rules/jsx-no-literals").RawElementConfig & import("../../types/rules/jsx-no-literals").RawElementOverrides;
+export type ResolvedConfig = import("../../types/rules/jsx-no-literals").OverrideConfig | import("../../types/rules/jsx-no-literals").Config;
+export type OverrideConfig = import("../../types/rules/jsx-no-literals").OverrideConfigProperties & import("../../types/rules/jsx-no-literals").ElementConfigProperties;
+export type ElementConfig = {
+    type: "element";
+} & import("../../types/rules/jsx-no-literals").ElementConfigProperties;
+//# sourceMappingURL=jsx-no-literals.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-literals.d.ts","sourceRoot":"","sources":["jsx-no-literals.js"],"names":[],"mappings":"wBAoJW,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,524 @@
+/**
+ * @fileoverview Prevent using string literals in React component definition
+ * @author Caleb Morris
+ * @author David Buchan-Swanson
+ */
+
+'use strict';
+
+const iterFrom = require('es-iterator-helpers/Iterator.from');
+const map = require('es-iterator-helpers/Iterator.prototype.map');
+const some = require('es-iterator-helpers/Iterator.prototype.some');
+const flatMap = require('es-iterator-helpers/Iterator.prototype.flatMap');
+const fromEntries = require('object.fromentries');
+const entries = require('object.entries');
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getText = require('../util/eslint').getText;
+
+/** @typedef {import('eslint').Rule.RuleModule} RuleModule */
+
+/** @typedef {import('../../types/rules/jsx-no-literals').Config} Config */
+/** @typedef {import('../../types/rules/jsx-no-literals').RawConfig} RawConfig */
+/** @typedef {import('../../types/rules/jsx-no-literals').ResolvedConfig} ResolvedConfig */
+/** @typedef {import('../../types/rules/jsx-no-literals').OverrideConfig} OverrideConfig */
+/** @typedef {import('../../types/rules/jsx-no-literals').ElementConfig} ElementConfig */
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/**
+ * @param {unknown} value
+ * @returns {string | unknown}
+ */
+function trimIfString(value) {
+  return typeof value === 'string' ? value.trim() : value;
+}
+
+const reOverridableElement = /^[A-Z][\w.]*$/;
+const reIsWhiteSpace = /^[\s]+$/;
+const jsxElementTypes = new Set(['JSXElement', 'JSXFragment']);
+const standardJSXNodeParentTypes = new Set(['JSXAttribute', 'JSXElement', 'JSXExpressionContainer', 'JSXFragment']);
+
+const messages = {
+  invalidPropValue: 'Invalid prop value: "{{text}}"',
+  invalidPropValueInElement: 'Invalid prop value: "{{text}}" in {{element}}',
+  noStringsInAttributes: 'Strings not allowed in attributes: "{{text}}"',
+  noStringsInAttributesInElement: 'Strings not allowed in attributes: "{{text}}" in {{element}}',
+  noStringsInJSX: 'Strings not allowed in JSX files: "{{text}}"',
+  noStringsInJSXInElement: 'Strings not allowed in JSX files: "{{text}}" in {{element}}',
+  literalNotInJSXExpression: 'Missing JSX expression container around literal string: "{{text}}"',
+  literalNotInJSXExpressionInElement: 'Missing JSX expression container around literal string: "{{text}}" in {{element}}',
+};
+
+/** @type {Exclude<RuleModule['meta']['schema'], unknown[] | false>['properties']} */
+const commonPropertiesSchema = {
+  noStrings: {
+    type: 'boolean',
+  },
+  allowedStrings: {
+    type: 'array',
+    uniqueItems: true,
+    items: {
+      type: 'string',
+    },
+  },
+  ignoreProps: {
+    type: 'boolean',
+  },
+  noAttributeStrings: {
+    type: 'boolean',
+  },
+};
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * Normalizes the element portion of the config
+ * @param {RawConfig} config
+ * @returns {ElementConfig}
+ */
+function normalizeElementConfig(config) {
+  return {
+    type: 'element',
+    noStrings: !!config.noStrings,
+    allowedStrings: config.allowedStrings
+      ? new Set(map(iterFrom(config.allowedStrings), trimIfString))
+      : new Set(),
+    ignoreProps: !!config.ignoreProps,
+    noAttributeStrings: !!config.noAttributeStrings,
+  };
+}
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * Normalizes the config and applies default values to all config options
+ * @param {RawConfig} config
+ * @returns {Config}
+ */
+function normalizeConfig(config) {
+  /** @type {Config} */
+  const normalizedConfig = Object.assign(normalizeElementConfig(config), {
+    elementOverrides: {},
+  });
+
+  if (config.elementOverrides) {
+    normalizedConfig.elementOverrides = fromEntries(
+      flatMap(
+        iterFrom(entries(config.elementOverrides)),
+        (entry) => {
+          const elementName = entry[0];
+          const rawElementConfig = entry[1];
+
+          if (!reOverridableElement.test(elementName)) {
+            return [];
+          }
+
+          return [[
+            elementName,
+            Object.assign(normalizeElementConfig(rawElementConfig), {
+              type: 'override',
+              name: elementName,
+              allowElement: !!rawElementConfig.allowElement,
+              applyToNestedElements: typeof rawElementConfig.applyToNestedElements === 'undefined' || !!rawElementConfig.applyToNestedElements,
+            }),
+          ]];
+        }
+      )
+    );
+  }
+
+  return normalizedConfig;
+}
+
+const elementOverrides = {
+  type: 'object',
+  patternProperties: {
+    [reOverridableElement.source]: {
+      type: 'object',
+      properties: Object.assign(
+        { applyToNestedElements: { type: 'boolean' } },
+        commonPropertiesSchema
+      ),
+
+    },
+  },
+};
+
+/** @type {RuleModule} */
+module.exports = {
+  meta: /** @type {RuleModule['meta']} */ ({
+    docs: {
+      description: 'Disallow usage of string literals in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-no-literals'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: Object.assign(
+        { elementOverrides },
+        commonPropertiesSchema
+      ),
+      additionalProperties: false,
+    }],
+  }),
+
+  create(context) {
+    /** @type {RawConfig} */
+    const rawConfig = (context.options.length && context.options[0]) || {};
+    const config = normalizeConfig(rawConfig);
+
+    const hasElementOverrides = Object.keys(config.elementOverrides).length > 0;
+
+    /** @type {Map<string, string>} */
+    const renamedImportMap = new Map();
+
+    /**
+     * Determines if the given expression is a require statement. Supports
+     * nested MemberExpresions. ie `require('foo').nested.property`
+     * @param {ASTNode} node
+     * @returns {boolean}
+     */
+    function isRequireStatement(node) {
+      if (node.type === 'CallExpression') {
+        if (node.callee.type === 'Identifier') {
+          return node.callee.name === 'require';
+        }
+      }
+      if (node.type === 'MemberExpression') {
+        return isRequireStatement(node.object);
+      }
+
+      return false;
+    }
+
+    /** @typedef {{ name: string, compoundName?: string }} ElementNameFragment */
+
+    /**
+     * Gets the name of the given JSX element. Supports nested
+     * JSXMemeberExpressions. ie `<Namesapce.Component.SubComponent />`
+     * @param {ASTNode} node
+     * @returns {ElementNameFragment | undefined}
+     */
+    function getJSXElementName(node) {
+      if (node.openingElement.name.type === 'JSXIdentifier') {
+        const name = node.openingElement.name.name;
+        return {
+          name: renamedImportMap.get(name) || name,
+          compoundName: undefined,
+        };
+      }
+
+      /** @type {string[]} */
+      const nameFragments = [];
+
+      if (node.openingElement.name.type === 'JSXMemberExpression') {
+        /** @type {ASTNode} */
+        let current = node.openingElement.name;
+        while (current.type === 'JSXMemberExpression') {
+          if (current.property.type === 'JSXIdentifier') {
+            nameFragments.unshift(current.property.name);
+          }
+
+          current = current.object;
+        }
+
+        if (current.type === 'JSXIdentifier') {
+          nameFragments.unshift(current.name);
+
+          const rootFragment = nameFragments[0];
+          if (rootFragment) {
+            const rootFragmentRenamed = renamedImportMap.get(rootFragment);
+            if (rootFragmentRenamed) {
+              nameFragments[0] = rootFragmentRenamed;
+            }
+          }
+
+          const nameFragment = nameFragments[nameFragments.length - 1];
+          if (nameFragment) {
+            return {
+              name: nameFragment,
+              compoundName: nameFragments.join('.'),
+            };
+          }
+        }
+      }
+    }
+
+    /**
+     * Gets all JSXElement ancestor nodes for the given node
+     * @param {ASTNode} node
+     * @returns {ASTNode[]}
+     */
+    function getJSXElementAncestors(node) {
+      /** @type {ASTNode[]} */
+      const ancestors = [];
+
+      let current = node;
+      while (current) {
+        if (current.type === 'JSXElement') {
+          ancestors.push(current);
+        }
+
+        current = current.parent;
+      }
+
+      return ancestors;
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @returns {ASTNode}
+     */
+    function getParentIgnoringBinaryExpressions(node) {
+      let current = node;
+      while (current.parent.type === 'BinaryExpression') {
+        current = current.parent;
+      }
+      return current.parent;
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @returns {{ parent: ASTNode, grandParent: ASTNode }}
+     */
+    function getParentAndGrandParent(node) {
+      const parent = getParentIgnoringBinaryExpressions(node);
+      return {
+        parent,
+        grandParent: parent.parent,
+      };
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @returns {boolean}
+     */
+    function hasJSXElementParentOrGrandParent(node) {
+      const ancestors = getParentAndGrandParent(node);
+      return some(iterFrom([ancestors.parent, ancestors.grandParent]), (parent) => jsxElementTypes.has(parent.type));
+    }
+
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * Determines whether a given node's value and its immediate parent are
+     * viable text nodes that can/should be reported on
+     * @param {ASTNode} node
+     * @param {ResolvedConfig} resolvedConfig
+     * @returns {boolean}
+     */
+    function isViableTextNode(node, resolvedConfig) {
+      const textValues = iterFrom([trimIfString(node.raw), trimIfString(node.value)]);
+      if (some(textValues, (value) => resolvedConfig.allowedStrings.has(value))) {
+        return false;
+      }
+
+      const parent = getParentIgnoringBinaryExpressions(node);
+
+      let isStandardJSXNode = false;
+      if (typeof node.value === 'string' && !reIsWhiteSpace.test(node.value) && standardJSXNodeParentTypes.has(parent.type)) {
+        if (resolvedConfig.noAttributeStrings) {
+          isStandardJSXNode = parent.type === 'JSXAttribute' || parent.type === 'JSXElement';
+        } else {
+          isStandardJSXNode = parent.type !== 'JSXAttribute';
+        }
+      }
+
+      if (resolvedConfig.noStrings) {
+        return isStandardJSXNode;
+      }
+
+      return isStandardJSXNode && parent.type !== 'JSXExpressionContainer';
+    }
+
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * Gets an override config for a given node. For any given node, we also
+     * need to traverse the ancestor tree to determine if an ancestor's config
+     * will also apply to the current node.
+     * @param {ASTNode} node
+     * @returns {OverrideConfig | undefined}
+     */
+    function getOverrideConfig(node) {
+      if (!hasElementOverrides) {
+        return;
+      }
+
+      const allAncestorElements = getJSXElementAncestors(node);
+      if (!allAncestorElements.length) {
+        return;
+      }
+
+      for (const ancestorElement of allAncestorElements) {
+        const isClosestJSXAncestor = ancestorElement === allAncestorElements[0];
+
+        const ancestor = getJSXElementName(ancestorElement);
+        if (ancestor) {
+          if (ancestor.name) {
+            const ancestorElements = config.elementOverrides[ancestor.name];
+            const ancestorConfig = ancestor.compoundName
+              ? config.elementOverrides[ancestor.compoundName] || ancestorElements
+              : ancestorElements;
+
+            if (ancestorConfig) {
+              if (isClosestJSXAncestor || ancestorConfig.applyToNestedElements) {
+                return ancestorConfig;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * @param {ResolvedConfig} resolvedConfig
+     * @returns {boolean}
+     */
+    function shouldAllowElement(resolvedConfig) {
+      return resolvedConfig.type === 'override' && 'allowElement' in resolvedConfig && !!resolvedConfig.allowElement;
+    }
+
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * @param {boolean} ancestorIsJSXElement
+     * @param {ResolvedConfig} resolvedConfig
+     * @returns {string}
+     */
+    function defaultMessageId(ancestorIsJSXElement, resolvedConfig) {
+      if (resolvedConfig.noAttributeStrings && !ancestorIsJSXElement) {
+        return resolvedConfig.type === 'override' ? 'noStringsInAttributesInElement' : 'noStringsInAttributes';
+      }
+
+      if (resolvedConfig.noStrings) {
+        return resolvedConfig.type === 'override' ? 'noStringsInJSXInElement' : 'noStringsInJSX';
+      }
+
+      return resolvedConfig.type === 'override' ? 'literalNotInJSXExpressionInElement' : 'literalNotInJSXExpression';
+    }
+
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * @param {ASTNode} node
+     * @param {string} messageId
+     * @param {ResolvedConfig} resolvedConfig
+     */
+    function reportLiteralNode(node, messageId, resolvedConfig) {
+      report(context, messages[messageId], messageId, {
+        node,
+        data: {
+          text: getText(context, node).trim(),
+          element: resolvedConfig.type === 'override' && 'name' in resolvedConfig ? resolvedConfig.name : undefined,
+        },
+      });
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return Object.assign(hasElementOverrides ? {
+      // Get renamed import local names mapped to their imported name
+      ImportDeclaration(node) {
+        node.specifiers
+          .filter((s) => s.type === 'ImportSpecifier')
+          .forEach((specifier) => {
+            renamedImportMap.set(
+              (specifier.local || specifier.imported).name,
+              specifier.imported.name
+            );
+          });
+      },
+
+      // Get renamed destructured local names mapped to their imported name
+      VariableDeclaration(node) {
+        node.declarations
+          .filter((d) => (
+            d.type === 'VariableDeclarator'
+            && isRequireStatement(d.init)
+            && d.id.type === 'ObjectPattern'
+          ))
+          .forEach((declaration) => {
+            declaration.id.properties
+              .filter((property) => (
+                property.type === 'Property'
+                && property.key.type === 'Identifier'
+                && property.value.type === 'Identifier'
+              ))
+              .forEach((property) => {
+                renamedImportMap.set(property.value.name, property.key.name);
+              });
+          });
+      },
+    } : false, {
+      Literal(node) {
+        const resolvedConfig = getOverrideConfig(node) || config;
+
+        const hasJSXParentOrGrandParent = hasJSXElementParentOrGrandParent(node);
+        if (hasJSXParentOrGrandParent && shouldAllowElement(resolvedConfig)) {
+          return;
+        }
+
+        if (isViableTextNode(node, resolvedConfig)) {
+          if (hasJSXParentOrGrandParent || !config.ignoreProps) {
+            reportLiteralNode(node, defaultMessageId(hasJSXParentOrGrandParent, resolvedConfig), resolvedConfig);
+          }
+        }
+      },
+
+      JSXAttribute(node) {
+        const isLiteralString = node.value && node.value.type === 'Literal'
+          && typeof node.value.value === 'string';
+        const isStringLiteral = node.value && node.value.type === 'StringLiteral';
+
+        if (isLiteralString || isStringLiteral) {
+          const resolvedConfig = getOverrideConfig(node) || config;
+
+          if (
+            resolvedConfig.noStrings
+            && !resolvedConfig.ignoreProps
+            && !resolvedConfig.allowedStrings.has(node.value.value)
+          ) {
+            const messageId = resolvedConfig.type === 'override' ? 'invalidPropValueInElement' : 'invalidPropValue';
+            reportLiteralNode(node, messageId, resolvedConfig);
+          }
+        }
+      },
+
+      JSXText(node) {
+        const resolvedConfig = getOverrideConfig(node) || config;
+
+        if (shouldAllowElement(resolvedConfig)) {
+          return;
+        }
+
+        if (isViableTextNode(node, resolvedConfig)) {
+          const hasJSXParendOrGrantParent = hasJSXElementParentOrGrandParent(node);
+          reportLiteralNode(node, defaultMessageId(hasJSXParendOrGrantParent, resolvedConfig), resolvedConfig);
+        }
+      },
+
+      TemplateLiteral(node) {
+        const ancestors = getParentAndGrandParent(node);
+        const isParentJSXExpressionCont = ancestors.parent.type === 'JSXExpressionContainer';
+        const isParentJSXElement = ancestors.grandParent.type === 'JSXElement';
+
+        if (isParentJSXExpressionCont) {
+          const resolvedConfig = getOverrideConfig(node) || config;
+
+          if (
+            resolvedConfig.noStrings
+            && (isParentJSXElement || !resolvedConfig.ignoreProps)
+          ) {
+            reportLiteralNode(node, defaultMessageId(isParentJSXElement, resolvedConfig), resolvedConfig);
+          }
+        }
+      },
+    });
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-script-url.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-script-url.d.ts","sourceRoot":"","sources":["jsx-no-script-url.js"],"names":[],"mappings":"wBA6CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-script-url.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,143 @@
+/**
+ * @fileoverview Prevent usage of `javascript:` URLs
+ * @author Sergei Startsev
+ */
+
+'use strict';
+
+const includes = require('array-includes');
+const docsUrl = require('../util/docsUrl');
+const linkComponentsUtil = require('../util/linkComponents');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+// https://github.com/facebook/react/blob/d0ebde77f6d1232cefc0da184d731943d78e86f2/packages/react-dom/src/shared/sanitizeURL.js#L30
+/* eslint-disable-next-line max-len, no-control-regex */
+const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
+
+function hasJavaScriptProtocol(attr) {
+  return attr.value && attr.value.type === 'Literal'
+    && isJavaScriptProtocol.test(attr.value.value);
+}
+
+function shouldVerifyProp(node, config) {
+  const name = node.name && node.name.name;
+  const parentName = node.parent.name && node.parent.name.name;
+
+  if (!name || !parentName || !config.has(parentName)) return false;
+
+  const attributes = config.get(parentName);
+  return includes(attributes, name);
+}
+
+function parseLegacyOption(config, option) {
+  option.forEach((opt) => {
+    config.set(opt.name, opt.props);
+  });
+}
+
+const messages = {
+  noScriptURL: 'A future version of React will block javascript: URLs as a security precaution. Use event handlers instead if you can. If you need to generate unsafe HTML, try using dangerouslySetInnerHTML instead.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of `javascript:` URLs',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('jsx-no-script-url'),
+    },
+
+    messages,
+
+    schema: {
+      anyOf: [
+        {
+          type: 'array',
+          items: [
+            {
+              type: 'array',
+              uniqueItems: true,
+              items: {
+                type: 'object',
+                properties: {
+                  name: {
+                    type: 'string',
+                  },
+                  props: {
+                    type: 'array',
+                    items: {
+                      type: 'string',
+                      uniqueItems: true,
+                    },
+                  },
+                },
+                required: ['name', 'props'],
+                additionalProperties: false,
+              },
+            },
+            {
+              type: 'object',
+              properties: {
+                includeFromSettings: {
+                  type: 'boolean',
+                },
+              },
+              additionalItems: false,
+            },
+          ],
+          additionalItems: false,
+        },
+        {
+          type: 'array',
+          items: [
+            {
+              type: 'object',
+              properties: {
+                includeFromSettings: {
+                  type: 'boolean',
+                },
+              },
+              additionalItems: false,
+            },
+          ],
+          additionalItems: false,
+        },
+      ],
+    },
+  },
+
+  create(context) {
+    const options = context.options;
+    const hasLegacyOption = Array.isArray(options[0]);
+    const legacyOptions = hasLegacyOption ? options[0] : [];
+    // eslint-disable-next-line no-nested-ternary
+    const objectOption = (hasLegacyOption && options.length > 1)
+      ? options[1]
+      : (options.length > 0
+        ? options[0]
+        : {
+          includeFromSettings: false,
+        }
+      );
+    const includeFromSettings = objectOption.includeFromSettings;
+
+    const linkComponents = linkComponentsUtil.getLinkComponents(includeFromSettings ? context : {});
+    parseLegacyOption(linkComponents, legacyOptions);
+
+    return {
+      JSXAttribute(node) {
+        if (shouldVerifyProp(node, linkComponents) && hasJavaScriptProtocol(node)) {
+          report(context, messages.noScriptURL, 'noScriptURL', {
+            node,
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-target-blank.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-target-blank.d.ts","sourceRoot":"","sources":["jsx-no-target-blank.js"],"names":[],"mappings":"wBAgIW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-target-blank.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,285 @@
+/**
+ * @fileoverview Forbid target='_blank' attribute
+ * @author Kevin Miller
+ */
+
+'use strict';
+
+const includes = require('array-includes');
+const docsUrl = require('../util/docsUrl');
+const linkComponentsUtil = require('../util/linkComponents');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function findLastIndex(arr, condition) {
+  for (let i = arr.length - 1; i >= 0; i -= 1) {
+    if (condition(arr[i])) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+function attributeValuePossiblyBlank(attribute) {
+  if (!attribute || !attribute.value) {
+    return false;
+  }
+  const value = attribute.value;
+  if (value.type === 'Literal') {
+    return typeof value.value === 'string' && value.value.toLowerCase() === '_blank';
+  }
+  if (value.type === 'JSXExpressionContainer') {
+    const expr = value.expression;
+    if (expr.type === 'Literal') {
+      return typeof expr.value === 'string' && expr.value.toLowerCase() === '_blank';
+    }
+    if (expr.type === 'ConditionalExpression') {
+      if (expr.alternate.type === 'Literal' && expr.alternate.value && expr.alternate.value.toLowerCase() === '_blank') {
+        return true;
+      }
+      if (expr.consequent.type === 'Literal' && expr.consequent.value && expr.consequent.value.toLowerCase() === '_blank') {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+function hasExternalLink(node, linkAttributes, warnOnSpreadAttributes, spreadAttributeIndex) {
+  const linkIndex = findLastIndex(node.attributes, (attr) => attr.name && includes(linkAttributes, attr.name.name));
+  const foundExternalLink = linkIndex !== -1 && ((attr) => attr.value && attr.value.type === 'Literal' && /^(?:\w+:|\/\/)/.test(attr.value.value))(
+    node.attributes[linkIndex]);
+  return foundExternalLink || (warnOnSpreadAttributes && linkIndex < spreadAttributeIndex);
+}
+
+function hasDynamicLink(node, linkAttributes) {
+  const dynamicLinkIndex = findLastIndex(node.attributes, (attr) => attr.name
+    && includes(linkAttributes, attr.name.name)
+    && attr.value
+    && attr.value.type === 'JSXExpressionContainer');
+  if (dynamicLinkIndex !== -1) {
+    return true;
+  }
+}
+
+/**
+ * Get the string(s) from a value
+ * @param {ASTNode} value The AST node being checked.
+ * @param {ASTNode} targetValue The AST node being checked.
+ * @returns {string | string[] | null} The string value, or null if not a string.
+ */
+function getStringFromValue(value, targetValue) {
+  if (value) {
+    if (value.type === 'Literal') {
+      return value.value;
+    }
+    if (value.type === 'JSXExpressionContainer') {
+      if (value.expression.type === 'TemplateLiteral') {
+        return value.expression.quasis[0].value.cooked;
+      }
+      const expr = value.expression;
+      if (expr && expr.type === 'ConditionalExpression') {
+        const relValues = [expr.consequent.value, expr.alternate.value];
+        if (targetValue.type === 'JSXExpressionContainer' && targetValue.expression && targetValue.expression.type === 'ConditionalExpression') {
+          const targetTestCond = targetValue.expression.test.name;
+          const relTestCond = value.expression.test.name;
+          if (targetTestCond === relTestCond) {
+            const targetBlankIndex = [targetValue.expression.consequent.value, targetValue.expression.alternate.value].indexOf('_blank');
+            return relValues[targetBlankIndex];
+          }
+        }
+        return relValues;
+      }
+      return expr.value;
+    }
+  }
+  return null;
+}
+
+function hasSecureRel(node, allowReferrer, warnOnSpreadAttributes, spreadAttributeIndex) {
+  const relIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'rel'));
+  const targetIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'target'));
+  if (relIndex === -1 || (warnOnSpreadAttributes && relIndex < spreadAttributeIndex)) {
+    return false;
+  }
+
+  const relAttribute = node.attributes[relIndex];
+  const targetAttributeValue = node.attributes[targetIndex] && node.attributes[targetIndex].value;
+  const value = getStringFromValue(relAttribute.value, targetAttributeValue);
+  return [].concat(value).every((item) => {
+    const tags = typeof item === 'string' ? item.toLowerCase().split(' ') : false;
+    const noreferrer = tags && tags.indexOf('noreferrer') >= 0;
+    if (noreferrer) {
+      return true;
+    }
+    const noopener = tags && tags.indexOf('noopener') >= 0;
+    return allowReferrer && noopener;
+  });
+}
+
+const messages = {
+  noTargetBlankWithoutNoreferrer: 'Using target="_blank" without rel="noreferrer" (which implies rel="noopener") is a security risk in older browsers: see https://mathiasbynens.github.io/rel-noopener/#recommendations',
+  noTargetBlankWithoutNoopener: 'Using target="_blank" without rel="noreferrer" or rel="noopener" (the former implies the latter and is preferred due to wider support) is a security risk: see https://mathiasbynens.github.io/rel-noopener/#recommendations',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    fixable: 'code',
+    docs: {
+      description: 'Disallow `target="_blank"` attribute without `rel="noreferrer"`',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('jsx-no-target-blank'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowReferrer: {
+          type: 'boolean',
+        },
+        enforceDynamicLinks: {
+          enum: ['always', 'never'],
+        },
+        warnOnSpreadAttributes: {
+          type: 'boolean',
+        },
+        links: {
+          type: 'boolean',
+          default: true,
+        },
+        forms: {
+          type: 'boolean',
+          default: false,
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = Object.assign(
+      {
+        allowReferrer: false,
+        warnOnSpreadAttributes: false,
+        links: true,
+        forms: false,
+      },
+      context.options[0]
+    );
+    const allowReferrer = configuration.allowReferrer;
+    const warnOnSpreadAttributes = configuration.warnOnSpreadAttributes;
+    const enforceDynamicLinks = configuration.enforceDynamicLinks || 'always';
+    const linkComponents = linkComponentsUtil.getLinkComponents(context);
+    const formComponents = linkComponentsUtil.getFormComponents(context);
+
+    return {
+      JSXOpeningElement(node) {
+        const targetIndex = findLastIndex(node.attributes, (attr) => attr.name && attr.name.name === 'target');
+        const spreadAttributeIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXSpreadAttribute'));
+
+        if (linkComponents.has(node.name.name)) {
+          if (!attributeValuePossiblyBlank(node.attributes[targetIndex])) {
+            const hasSpread = spreadAttributeIndex >= 0;
+
+            if (warnOnSpreadAttributes && hasSpread) {
+              // continue to check below
+            } else if ((hasSpread && targetIndex < spreadAttributeIndex) || !hasSpread || !warnOnSpreadAttributes) {
+              return;
+            }
+          }
+
+          const linkAttributes = linkComponents.get(node.name.name);
+          const hasDangerousLink = hasExternalLink(node, linkAttributes, warnOnSpreadAttributes, spreadAttributeIndex)
+            || (enforceDynamicLinks === 'always' && hasDynamicLink(node, linkAttributes));
+          if (hasDangerousLink && !hasSecureRel(node, allowReferrer, warnOnSpreadAttributes, spreadAttributeIndex)) {
+            const messageId = allowReferrer ? 'noTargetBlankWithoutNoopener' : 'noTargetBlankWithoutNoreferrer';
+            const relValue = allowReferrer ? 'noopener' : 'noreferrer';
+            report(context, messages[messageId], messageId, {
+              node,
+              fix(fixer) {
+                // eslint 5 uses `node.attributes`; eslint 6+ uses `node.parent.attributes`
+                const nodeWithAttrs = node.parent.attributes ? node.parent : node;
+                // eslint 5 does not provide a `name` property on JSXSpreadElements
+                const relAttribute = nodeWithAttrs.attributes.find((attr) => attr.name && attr.name.name === 'rel');
+
+                if (targetIndex < spreadAttributeIndex || (spreadAttributeIndex >= 0 && !relAttribute)) {
+                  return null;
+                }
+
+                if (!relAttribute) {
+                  return fixer.insertTextAfter(nodeWithAttrs.attributes.slice(-1)[0], ` rel="${relValue}"`);
+                }
+
+                if (!relAttribute.value) {
+                  return fixer.insertTextAfter(relAttribute, `="${relValue}"`);
+                }
+
+                if (relAttribute.value.type === 'Literal') {
+                  const parts = relAttribute.value.value
+                    .split('noreferrer')
+                    .filter(Boolean);
+                  return fixer.replaceText(relAttribute.value, `"${parts.concat('noreferrer').join(' ')}"`);
+                }
+
+                if (relAttribute.value.type === 'JSXExpressionContainer') {
+                  if (relAttribute.value.expression.type === 'Literal') {
+                    if (typeof relAttribute.value.expression.value === 'string') {
+                      const parts = relAttribute.value.expression.value
+                        .split('noreferrer')
+                        .filter(Boolean);
+                      return fixer.replaceText(relAttribute.value.expression, `"${parts.concat('noreferrer').join(' ')}"`);
+                    }
+
+                    // for undefined, boolean, number, symbol, bigint, and null
+                    return fixer.replaceText(relAttribute.value, '"noreferrer"');
+                  }
+                }
+
+                return null;
+              },
+            });
+          }
+        }
+        if (formComponents.has(node.name.name)) {
+          if (!attributeValuePossiblyBlank(node.attributes[targetIndex])) {
+            const hasSpread = spreadAttributeIndex >= 0;
+
+            if (warnOnSpreadAttributes && hasSpread) {
+              // continue to check below
+            } else if (
+              (hasSpread && targetIndex < spreadAttributeIndex)
+              || !hasSpread
+              || !warnOnSpreadAttributes
+            ) {
+              return;
+            }
+          }
+
+          if (!configuration.forms || hasSecureRel(node)) {
+            return;
+          }
+
+          const formAttributes = formComponents.get(node.name.name);
+
+          if (
+            hasExternalLink(node, formAttributes)
+            || (enforceDynamicLinks === 'always' && hasDynamicLink(node, formAttributes))
+          ) {
+            const messageId = allowReferrer ? 'noTargetBlankWithoutNoopener' : 'noTargetBlankWithoutNoreferrer';
+            report(context, messages[messageId], messageId, {
+              node,
+            });
+          }
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-undef.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-undef.d.ts","sourceRoot":"","sources":["jsx-no-undef.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,117 @@
+/**
+ * @fileoverview Disallow undeclared variables in JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const eslintUtil = require('../util/eslint');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  undefined: '\'{{identifier}}\' is not defined.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow undeclared variables in JSX',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('jsx-no-undef'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowGlobals: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const config = context.options[0] || {};
+    const allowGlobals = config.allowGlobals || false;
+
+    /**
+     * Compare an identifier with the variables declared in the scope
+     * @param {ASTNode} node - Identifier or JSXIdentifier node
+     * @returns {void}
+     */
+    function checkIdentifierInJSX(node) {
+      let scope = eslintUtil.getScope(context, node);
+      const sourceCode = eslintUtil.getSourceCode(context);
+      const sourceType = sourceCode.ast.sourceType;
+      const scopeUpperBound = !allowGlobals && sourceType === 'module' ? 'module' : 'global';
+      let variables = scope.variables;
+      let i;
+      let len;
+
+      // Ignore 'this' keyword (also maked as JSXIdentifier when used in JSX)
+      if (node.name === 'this') {
+        return;
+      }
+
+      while (scope.type !== scopeUpperBound && scope.type !== 'global') {
+        scope = scope.upper;
+        variables = scope.variables.concat(variables);
+      }
+      if (scope.childScopes.length) {
+        variables = scope.childScopes[0].variables.concat(variables);
+        // Temporary fix for babel-eslint
+        if (scope.childScopes[0].childScopes.length) {
+          variables = scope.childScopes[0].childScopes[0].variables.concat(variables);
+        }
+      }
+
+      for (i = 0, len = variables.length; i < len; i++) {
+        if (variables[i].name === node.name) {
+          return;
+        }
+      }
+
+      report(context, messages.undefined, 'undefined', {
+        node,
+        data: {
+          identifier: node.name,
+        },
+      });
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        switch (node.name.type) {
+          case 'JSXIdentifier':
+            if (jsxUtil.isDOMComponent(node)) {
+              return;
+            }
+            node = node.name;
+            break;
+          case 'JSXMemberExpression':
+            node = node.name;
+            do {
+              node = node.object;
+            } while (node && node.type !== 'JSXIdentifier');
+            break;
+          case 'JSXNamespacedName':
+            return;
+          default:
+            break;
+        }
+        checkIdentifierInJSX(node);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-no-useless-fragment.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-no-useless-fragment.d.ts","sourceRoot":"","sources":["jsx-no-useless-fragment.js"],"names":[],"mappings":"wBAsFW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-useless-fragment.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,259 @@
+/**
+ * @fileoverview Disallow useless fragments
+ */
+
+'use strict';
+
+const arrayIncludes = require('array-includes');
+
+const pragmaUtil = require('../util/pragma');
+const astUtil = require('../util/ast');
+const jsxUtil = require('../util/jsx');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getText = require('../util/eslint').getText;
+
+function isJSXText(node) {
+  return !!node && (node.type === 'JSXText' || node.type === 'Literal');
+}
+
+/**
+ * @param {string} text
+ * @returns {boolean}
+ */
+function isOnlyWhitespace(text) {
+  return text.trim().length === 0;
+}
+
+/**
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function isNonspaceJSXTextOrJSXCurly(node) {
+  return (isJSXText(node) && !isOnlyWhitespace(node.raw)) || node.type === 'JSXExpressionContainer';
+}
+
+/**
+ * Somehow fragment like this is useful: <Foo content={<>ee eeee eeee ...</>} />
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function isFragmentWithOnlyTextAndIsNotChild(node) {
+  return node.children.length === 1
+    && isJSXText(node.children[0])
+    && !(node.parent.type === 'JSXElement' || node.parent.type === 'JSXFragment');
+}
+
+/**
+ * @param {string} text
+ * @returns {string}
+ */
+function trimLikeReact(text) {
+  const leadingSpaces = /^\s*/.exec(text)[0];
+  const trailingSpaces = /\s*$/.exec(text)[0];
+
+  const start = arrayIncludes(leadingSpaces, '\n') ? leadingSpaces.length : 0;
+  const end = arrayIncludes(trailingSpaces, '\n') ? text.length - trailingSpaces.length : text.length;
+
+  return text.slice(start, end);
+}
+
+/**
+ * Test if node is like `<Fragment key={_}>_</Fragment>`
+ * @param {JSXElement} node
+ * @returns {boolean}
+ */
+function isKeyedElement(node) {
+  return node.type === 'JSXElement'
+    && node.openingElement.attributes
+    && node.openingElement.attributes.some(jsxUtil.isJSXAttributeKey);
+}
+
+/**
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function containsCallExpression(node) {
+  return node
+    && node.type === 'JSXExpressionContainer'
+    && astUtil.isCallExpression(node.expression);
+}
+
+const messages = {
+  NeedsMoreChildren: 'Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all.',
+  ChildOfHtmlElement: 'Passing a fragment to an HTML element is useless.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    fixable: 'code',
+    docs: {
+      description: 'Disallow unnecessary fragments',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('jsx-no-useless-fragment'),
+    },
+    messages,
+    schema: [{
+      type: 'object',
+      properties: {
+        allowExpressions: {
+          type: 'boolean',
+        },
+      },
+    }],
+  },
+
+  create(context) {
+    const config = context.options[0] || {};
+    const allowExpressions = config.allowExpressions || false;
+
+    const reactPragma = pragmaUtil.getFromContext(context);
+    const fragmentPragma = pragmaUtil.getFragmentFromContext(context);
+
+    /**
+     * Test whether a node is an padding spaces trimmed by react runtime.
+     * @param {ASTNode} node
+     * @returns {boolean}
+     */
+    function isPaddingSpaces(node) {
+      return isJSXText(node)
+        && isOnlyWhitespace(node.raw)
+        && arrayIncludes(node.raw, '\n');
+    }
+
+    function isFragmentWithSingleExpression(node) {
+      const children = node && node.children.filter((child) => !isPaddingSpaces(child));
+      return (
+        children
+        && children.length === 1
+        && children[0].type === 'JSXExpressionContainer'
+      );
+    }
+
+    /**
+     * Test whether a JSXElement has less than two children, excluding paddings spaces.
+     * @param {JSXElement|JSXFragment} node
+     * @returns {boolean}
+     */
+    function hasLessThanTwoChildren(node) {
+      if (!node || !node.children) {
+        return true;
+      }
+
+      /** @type {ASTNode[]} */
+      const nonPaddingChildren = node.children.filter(
+        (child) => !isPaddingSpaces(child)
+      );
+
+      if (nonPaddingChildren.length < 2) {
+        return !containsCallExpression(nonPaddingChildren[0]);
+      }
+    }
+
+    /**
+     * @param {JSXElement|JSXFragment} node
+     * @returns {boolean}
+     */
+    function isChildOfHtmlElement(node) {
+      return node.parent.type === 'JSXElement'
+        && node.parent.openingElement.name.type === 'JSXIdentifier'
+        && /^[a-z]+$/.test(node.parent.openingElement.name.name);
+    }
+
+    /**
+     * @param {JSXElement|JSXFragment} node
+     * @return {boolean}
+     */
+    function isChildOfComponentElement(node) {
+      return node.parent.type === 'JSXElement'
+        && !isChildOfHtmlElement(node)
+        && !jsxUtil.isFragment(node.parent, reactPragma, fragmentPragma);
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @returns {boolean}
+     */
+    function canFix(node) {
+      // Not safe to fix fragments without a jsx parent.
+      if (!(node.parent.type === 'JSXElement' || node.parent.type === 'JSXFragment')) {
+        // const a = <></>
+        if (node.children.length === 0) {
+          return false;
+        }
+
+        // const a = <>cat {meow}</>
+        if (node.children.some(isNonspaceJSXTextOrJSXCurly)) {
+          return false;
+        }
+      }
+
+      // Not safe to fix `<Eeee><>foo</></Eeee>` because `Eeee` might require its children be a ReactElement.
+      if (isChildOfComponentElement(node)) {
+        return false;
+      }
+
+      // old TS parser can't handle this one
+      if (node.type === 'JSXFragment' && (!node.openingFragment || !node.closingFragment)) {
+        return false;
+      }
+
+      return true;
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @returns {Function | undefined}
+     */
+    function getFix(node) {
+      if (!canFix(node)) {
+        return undefined;
+      }
+
+      return function fix(fixer) {
+        const opener = node.type === 'JSXFragment' ? node.openingFragment : node.openingElement;
+        const closer = node.type === 'JSXFragment' ? node.closingFragment : node.closingElement;
+
+        const childrenText = opener.selfClosing ? '' : getText(context).slice(opener.range[1], closer.range[0]);
+
+        return fixer.replaceText(node, trimLikeReact(childrenText));
+      };
+    }
+
+    function checkNode(node) {
+      if (isKeyedElement(node)) {
+        return;
+      }
+
+      if (
+        hasLessThanTwoChildren(node)
+        && !isFragmentWithOnlyTextAndIsNotChild(node)
+        && !(allowExpressions && isFragmentWithSingleExpression(node))
+      ) {
+        report(context, messages.NeedsMoreChildren, 'NeedsMoreChildren', {
+          node,
+          fix: getFix(node),
+        });
+      }
+
+      if (isChildOfHtmlElement(node)) {
+        report(context, messages.ChildOfHtmlElement, 'ChildOfHtmlElement', {
+          node,
+          fix: getFix(node),
+        });
+      }
+    }
+
+    return {
+      JSXElement(node) {
+        if (jsxUtil.isFragment(node, reactPragma, fragmentPragma)) {
+          checkNode(node);
+        }
+      },
+      JSXFragment: checkNode,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-one-expression-per-line.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-one-expression-per-line.d.ts","sourceRoot":"","sources":["jsx-one-expression-per-line.js"],"names":[],"mappings":"wBA2BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-one-expression-per-line.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,249 @@
+/**
+ * @fileoverview Limit to one expression per line in JSX
+ * @author Mark Ivan Allen <Vydia.com>
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const eslintUtil = require('../util/eslint');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const optionDefaults = {
+  allow: 'none',
+};
+
+const messages = {
+  moveToNewLine: '`{{descriptor}}` must be placed on a new line',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Require one JSX element per line',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-one-expression-per-line'),
+    },
+    fixable: 'whitespace',
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          allow: {
+            enum: ['none', 'literal', 'single-child', 'non-jsx'],
+          },
+        },
+        default: optionDefaults,
+        additionalProperties: false,
+      },
+    ],
+  },
+
+  create(context) {
+    const options = Object.assign({}, optionDefaults, context.options[0]);
+
+    function nodeKey(node) {
+      return `${node.loc.start.line},${node.loc.start.column}`;
+    }
+
+    /**
+     * @param {ASTNode} n
+     * @returns {string}
+     */
+    function nodeDescriptor(n) {
+      return n.openingElement ? n.openingElement.name.name : getText(context, n).replace(/\n/g, '');
+    }
+
+    function handleJSX(node) {
+      const children = node.children;
+
+      if (!children || !children.length) {
+        return;
+      }
+
+      if (
+        options.allow === 'non-jsx'
+        && !children.find((child) => (child.type === 'JSXFragment' || child.type === 'JSXElement'))
+      ) {
+        return;
+      }
+
+      const openingElement = node.openingElement || node.openingFragment;
+      const closingElement = node.closingElement || node.closingFragment;
+      const openingElementStartLine = openingElement.loc.start.line;
+      const openingElementEndLine = openingElement.loc.end.line;
+      const closingElementStartLine = closingElement.loc.start.line;
+      const closingElementEndLine = closingElement.loc.end.line;
+
+      if (children.length === 1) {
+        const child = children[0];
+        if (
+          openingElementStartLine === openingElementEndLine
+          && openingElementEndLine === closingElementStartLine
+          && closingElementStartLine === closingElementEndLine
+          && closingElementEndLine === child.loc.start.line
+          && child.loc.start.line === child.loc.end.line
+        ) {
+          if (
+            options.allow === 'single-child'
+            || (options.allow === 'literal' && (child.type === 'Literal' || child.type === 'JSXText'))
+          ) {
+            return;
+          }
+        }
+      }
+
+      const childrenGroupedByLine = {};
+      const fixDetailsByNode = {};
+
+      children.forEach((child) => {
+        let countNewLinesBeforeContent = 0;
+        let countNewLinesAfterContent = 0;
+
+        if (child.type === 'Literal' || child.type === 'JSXText') {
+          if (jsxUtil.isWhiteSpaces(child.raw)) {
+            return;
+          }
+
+          countNewLinesBeforeContent = (child.raw.match(/^\s*\n/g) || []).length;
+          countNewLinesAfterContent = (child.raw.match(/\n\s*$/g) || []).length;
+        }
+
+        const startLine = child.loc.start.line + countNewLinesBeforeContent;
+        const endLine = child.loc.end.line - countNewLinesAfterContent;
+
+        if (startLine === endLine) {
+          if (!childrenGroupedByLine[startLine]) {
+            childrenGroupedByLine[startLine] = [];
+          }
+          childrenGroupedByLine[startLine].push(child);
+        } else {
+          if (!childrenGroupedByLine[startLine]) {
+            childrenGroupedByLine[startLine] = [];
+          }
+          childrenGroupedByLine[startLine].push(child);
+          if (!childrenGroupedByLine[endLine]) {
+            childrenGroupedByLine[endLine] = [];
+          }
+          childrenGroupedByLine[endLine].push(child);
+        }
+      });
+
+      Object.keys(childrenGroupedByLine).forEach((_line) => {
+        const line = parseInt(_line, 10);
+        const firstIndex = 0;
+        const lastIndex = childrenGroupedByLine[line].length - 1;
+
+        childrenGroupedByLine[line].forEach((child, i) => {
+          let prevChild;
+          let nextChild;
+
+          if (i === firstIndex) {
+            if (line === openingElementEndLine) {
+              prevChild = openingElement;
+            }
+          } else {
+            prevChild = childrenGroupedByLine[line][i - 1];
+          }
+
+          if (i === lastIndex) {
+            if (line === closingElementStartLine) {
+              nextChild = closingElement;
+            }
+          } else {
+            // We don't need to append a trailing because the next child will prepend a leading.
+            // nextChild = childrenGroupedByLine[line][i + 1];
+          }
+
+          function spaceBetweenPrev() {
+            return ((prevChild.type === 'Literal' || prevChild.type === 'JSXText') && / $/.test(prevChild.raw))
+              || ((child.type === 'Literal' || child.type === 'JSXText') && /^ /.test(child.raw))
+              || getSourceCode(context).isSpaceBetweenTokens(prevChild, child);
+          }
+
+          function spaceBetweenNext() {
+            return ((nextChild.type === 'Literal' || nextChild.type === 'JSXText') && /^ /.test(nextChild.raw))
+              || ((child.type === 'Literal' || child.type === 'JSXText') && / $/.test(child.raw))
+              || getSourceCode(context).isSpaceBetweenTokens(child, nextChild);
+          }
+
+          if (!prevChild && !nextChild) {
+            return;
+          }
+
+          const source = getText(context, child);
+          const leadingSpace = !!(prevChild && spaceBetweenPrev());
+          const trailingSpace = !!(nextChild && spaceBetweenNext());
+          const leadingNewLine = !!prevChild;
+          const trailingNewLine = !!nextChild;
+
+          const key = nodeKey(child);
+
+          if (!fixDetailsByNode[key]) {
+            fixDetailsByNode[key] = {
+              node: child,
+              source,
+              descriptor: nodeDescriptor(child),
+            };
+          }
+
+          if (leadingSpace) {
+            fixDetailsByNode[key].leadingSpace = true;
+          }
+          if (leadingNewLine) {
+            fixDetailsByNode[key].leadingNewLine = true;
+          }
+          if (trailingNewLine) {
+            fixDetailsByNode[key].trailingNewLine = true;
+          }
+          if (trailingSpace) {
+            fixDetailsByNode[key].trailingSpace = true;
+          }
+        });
+      });
+
+      Object.keys(fixDetailsByNode).forEach((key) => {
+        const details = fixDetailsByNode[key];
+
+        const nodeToReport = details.node;
+        const descriptor = details.descriptor;
+        const source = details.source.replace(/(^ +| +(?=\n)*$)/g, '');
+
+        const leadingSpaceString = details.leadingSpace ? '\n{\' \'}' : '';
+        const trailingSpaceString = details.trailingSpace ? '{\' \'}\n' : '';
+        const leadingNewLineString = details.leadingNewLine ? '\n' : '';
+        const trailingNewLineString = details.trailingNewLine ? '\n' : '';
+
+        const replaceText = `${leadingSpaceString}${leadingNewLineString}${source}${trailingNewLineString}${trailingSpaceString}`;
+
+        report(context, messages.moveToNewLine, 'moveToNewLine', {
+          node: nodeToReport,
+          data: {
+            descriptor,
+          },
+          fix(fixer) {
+            return fixer.replaceText(nodeToReport, replaceText);
+          },
+        });
+      });
+    }
+
+    return {
+      JSXElement: handleJSX,
+      JSXFragment: handleJSX,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-pascal-case.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-pascal-case.d.ts","sourceRoot":"","sources":["jsx-pascal-case.js"],"names":[],"mappings":"wBA8EW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,164 @@
+/**
+ * @fileoverview Enforce PascalCase for user-defined JSX components
+ * @author Jake Marsh
+ */
+
+'use strict';
+
+const elementType = require('jsx-ast-utils/elementType');
+const minimatch = require('minimatch');
+const docsUrl = require('../util/docsUrl');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+
+function testDigit(char) {
+  const charCode = char.charCodeAt(0);
+  return charCode >= 48 && charCode <= 57;
+}
+
+function testUpperCase(char) {
+  const upperCase = char.toUpperCase();
+  return char === upperCase && upperCase !== char.toLowerCase();
+}
+
+function testLowerCase(char) {
+  const lowerCase = char.toLowerCase();
+  return char === lowerCase && lowerCase !== char.toUpperCase();
+}
+
+function testPascalCase(name) {
+  if (!testUpperCase(name.charAt(0))) {
+    return false;
+  }
+  const anyNonAlphaNumeric = Array.prototype.some.call(
+    name.slice(1),
+    (char) => char.toLowerCase() === char.toUpperCase() && !testDigit(char)
+  );
+  if (anyNonAlphaNumeric) {
+    return false;
+  }
+  return Array.prototype.some.call(
+    name.slice(1),
+    (char) => testLowerCase(char) || testDigit(char)
+  );
+}
+
+function testAllCaps(name) {
+  const firstChar = name.charAt(0);
+  if (!(testUpperCase(firstChar) || testDigit(firstChar))) {
+    return false;
+  }
+  for (let i = 1; i < name.length - 1; i += 1) {
+    const char = name.charAt(i);
+    if (!(testUpperCase(char) || testDigit(char) || char === '_')) {
+      return false;
+    }
+  }
+  const lastChar = name.charAt(name.length - 1);
+  if (!(testUpperCase(lastChar) || testDigit(lastChar))) {
+    return false;
+  }
+  return true;
+}
+
+function ignoreCheck(ignore, name) {
+  return ignore.some(
+    (entry) => name === entry || minimatch(name, entry, { noglobstar: true })
+  );
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  usePascalCase: 'Imported JSX component {{name}} must be in PascalCase',
+  usePascalOrSnakeCase: 'Imported JSX component {{name}} must be in PascalCase or SCREAMING_SNAKE_CASE',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce PascalCase for user-defined JSX components',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-pascal-case'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowAllCaps: {
+          type: 'boolean',
+        },
+        allowLeadingUnderscore: {
+          type: 'boolean',
+        },
+        allowNamespace: {
+          type: 'boolean',
+        },
+        ignore: {
+          items: [
+            {
+              type: 'string',
+            },
+          ],
+          minItems: 0,
+          type: 'array',
+          uniqueItems: true,
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const allowAllCaps = configuration.allowAllCaps || false;
+    const allowLeadingUnderscore = configuration.allowLeadingUnderscore || false;
+    const allowNamespace = configuration.allowNamespace || false;
+    const ignore = configuration.ignore || [];
+
+    return {
+      JSXOpeningElement(node) {
+        const isCompatTag = jsxUtil.isDOMComponent(node);
+        if (isCompatTag) return undefined;
+
+        const name = elementType(node);
+        let checkNames = [name];
+        let index = 0;
+
+        if (name.lastIndexOf(':') > -1) {
+          checkNames = name.split(':');
+        } else if (name.lastIndexOf('.') > -1) {
+          checkNames = name.split('.');
+        }
+
+        do {
+          const splitName = checkNames[index];
+          if (splitName.length === 1) return undefined;
+          const isIgnored = ignoreCheck(ignore, splitName);
+
+          const checkName = allowLeadingUnderscore && splitName.startsWith('_') ? splitName.slice(1) : splitName;
+          const isPascalCase = testPascalCase(checkName);
+          const isAllowedAllCaps = allowAllCaps && testAllCaps(checkName);
+
+          if (!isPascalCase && !isAllowedAllCaps && !isIgnored) {
+            const messageId = allowAllCaps ? 'usePascalOrSnakeCase' : 'usePascalCase';
+            report(context, messages[messageId], messageId, {
+              node,
+              data: {
+                name: splitName,
+              },
+            });
+            break;
+          }
+          index += 1;
+        } while (index < checkNames.length && !allowNamespace);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-props-no-multi-spaces.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-props-no-multi-spaces.d.ts","sourceRoot":"","sources":["jsx-props-no-multi-spaces.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-multi-spaces.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,144 @@
+/**
+ * @fileoverview Disallow multiple spaces between inline JSX props
+ * @author Adrian Moennich
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const eslintUtil = require('../util/eslint');
+const report = require('../util/report');
+const propsUtil = require('../util/props');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noLineGap: 'Expected no line gap between “{{prop1}}” and “{{prop2}}”',
+  onlyOneSpace: 'Expected only one space between “{{prop1}}” and “{{prop2}}”',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow multiple spaces between inline JSX props',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-props-no-multi-spaces'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    const sourceCode = getSourceCode(context);
+
+    function getPropName(propNode) {
+      switch (propNode.type) {
+        case 'JSXSpreadAttribute':
+          return getText(context, propNode.argument);
+        case 'JSXIdentifier':
+          return propNode.name;
+        case 'JSXMemberExpression':
+          return `${getPropName(propNode.object)}.${propNode.property.name}`;
+        default:
+          return propNode.name
+            ? propNode.name.name
+            : `${getText(context, propNode.object)}.${propNode.property.name}`; // needed for typescript-eslint parser
+      }
+    }
+
+    // First and second must be adjacent nodes
+    function hasEmptyLines(first, second) {
+      const comments = sourceCode.getCommentsBefore ? sourceCode.getCommentsBefore(second) : [];
+      const nodes = [].concat(first, comments, second);
+
+      for (let i = 1; i < nodes.length; i += 1) {
+        const prev = nodes[i - 1];
+        const curr = nodes[i];
+        if (curr.loc.start.line - prev.loc.end.line >= 2) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    function checkSpacing(prev, node) {
+      if (hasEmptyLines(prev, node)) {
+        report(context, messages.noLineGap, 'noLineGap', {
+          node,
+          data: {
+            prop1: getPropName(prev),
+            prop2: getPropName(node),
+          },
+        });
+      }
+
+      if (prev.loc.end.line !== node.loc.end.line) {
+        return;
+      }
+
+      const between = getSourceCode(context).text.slice(prev.range[1], node.range[0]);
+
+      if (between !== ' ') {
+        report(context, messages.onlyOneSpace, 'onlyOneSpace', {
+          node,
+          data: {
+            prop1: getPropName(prev),
+            prop2: getPropName(node),
+          },
+          fix(fixer) {
+            return fixer.replaceTextRange([prev.range[1], node.range[0]], ' ');
+          },
+        });
+      }
+    }
+
+    function containsGenericType(node) {
+      const nodeTypeArguments = propsUtil.getTypeArguments(node);
+      if (typeof nodeTypeArguments === 'undefined') {
+        return false;
+      }
+
+      return nodeTypeArguments.type === 'TSTypeParameterInstantiation';
+    }
+
+    function getGenericNode(node) {
+      const name = node.name;
+      if (containsGenericType(node)) {
+        const nodeTypeArguments = propsUtil.getTypeArguments(node);
+
+        return Object.assign(
+          {},
+          node,
+          {
+            range: [
+              name.range[0],
+              nodeTypeArguments.range[1],
+            ],
+          }
+        );
+      }
+
+      return name;
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        node.attributes.reduce((prev, prop) => {
+          checkSpacing(prev, prop);
+          return prop;
+        }, getGenericNode(node));
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-props-no-spread-multi.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-props-no-spread-multi.d.ts","sourceRoot":"","sources":["jsx-props-no-spread-multi.js"],"names":[],"mappings":"wBAkBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spread-multi.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,54 @@
+/**
+ * @fileoverview Prevent JSX prop spreading the same expression multiple times
+ * @author Simon Schick
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noMultiSpreading: 'Spreading the same expression multiple times is forbidden',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow JSX prop spreading the same identifier multiple times',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('jsx-props-no-spread-multi'),
+    },
+    messages,
+  },
+
+  create(context) {
+    return {
+      JSXOpeningElement(node) {
+        const spreads = node.attributes.filter(
+          (attr) => attr.type === 'JSXSpreadAttribute'
+          && attr.argument.type === 'Identifier'
+        );
+        if (spreads.length < 2) {
+          return;
+        }
+        // We detect duplicate expressions by their identifier
+        const identifierNames = new Set();
+        spreads.forEach((spread) => {
+          if (identifierNames.has(spread.argument.name)) {
+            report(context, messages.noMultiSpreading, 'noMultiSpreading', {
+              node: spread,
+            });
+          }
+          identifierNames.add(spread.argument.name);
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-props-no-spreading.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-props-no-spreading.d.ts","sourceRoot":"","sources":["jsx-props-no-spreading.js"],"names":[],"mappings":"wBAwCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-props-no-spreading.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,145 @@
+/**
+ * @fileoverview Prevent JSX prop spreading
+ * @author Ashish Gambhir
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const OPTIONS = { ignore: 'ignore', enforce: 'enforce' };
+const DEFAULTS = {
+  html: OPTIONS.enforce,
+  custom: OPTIONS.enforce,
+  explicitSpread: OPTIONS.enforce,
+  exceptions: [],
+};
+
+const isException = (tag, allExceptions) => allExceptions.indexOf(tag) !== -1;
+const isProperty = (property) => property.type === 'Property';
+const getTagNameFromMemberExpression = (node) => {
+  if (node.property.parent) {
+    return `${node.property.parent.object.name}.${node.property.name}`;
+  }
+  // for eslint 3
+  return `${node.object.name}.${node.property.name}`;
+};
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noSpreading: 'Prop spreading is forbidden',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow JSX prop spreading',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('jsx-props-no-spreading'),
+    },
+
+    messages,
+
+    schema: [{
+      allOf: [{
+        type: 'object',
+        properties: {
+          html: {
+            enum: [OPTIONS.enforce, OPTIONS.ignore],
+          },
+          custom: {
+            enum: [OPTIONS.enforce, OPTIONS.ignore],
+          },
+          explicitSpread: {
+            enum: [OPTIONS.enforce, OPTIONS.ignore],
+          },
+          exceptions: {
+            type: 'array',
+            items: {
+              type: 'string',
+              uniqueItems: true,
+            },
+          },
+        },
+      }, {
+        not: {
+          type: 'object',
+          required: ['html', 'custom'],
+          properties: {
+            html: {
+              enum: [OPTIONS.ignore],
+            },
+            custom: {
+              enum: [OPTIONS.ignore],
+            },
+            exceptions: {
+              type: 'array',
+              minItems: 0,
+              maxItems: 0,
+            },
+          },
+        },
+      }],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const ignoreHtmlTags = (configuration.html || DEFAULTS.html) === OPTIONS.ignore;
+    const ignoreCustomTags = (configuration.custom || DEFAULTS.custom) === OPTIONS.ignore;
+    const ignoreExplicitSpread = (configuration.explicitSpread || DEFAULTS.explicitSpread) === OPTIONS.ignore;
+    const exceptions = configuration.exceptions || DEFAULTS.exceptions;
+    return {
+      JSXSpreadAttribute(node) {
+        const jsxOpeningElement = node.parent.name;
+        const type = jsxOpeningElement.type;
+
+        let tagName;
+        if (type === 'JSXIdentifier') {
+          tagName = jsxOpeningElement.name;
+        } else if (type === 'JSXMemberExpression') {
+          tagName = getTagNameFromMemberExpression(jsxOpeningElement);
+        } else {
+          tagName = undefined;
+        }
+
+        const isHTMLTag = tagName && tagName[0] !== tagName[0].toUpperCase();
+        const isCustomTag = tagName && (tagName[0] === tagName[0].toUpperCase() || tagName.includes('.'));
+        if (
+          isHTMLTag
+          && ((ignoreHtmlTags && !isException(tagName, exceptions))
+          || (!ignoreHtmlTags && isException(tagName, exceptions)))
+        ) {
+          return;
+        }
+        if (
+          isCustomTag
+          && ((ignoreCustomTags && !isException(tagName, exceptions))
+          || (!ignoreCustomTags && isException(tagName, exceptions)))
+        ) {
+          return;
+        }
+        if (
+          ignoreExplicitSpread
+          && node.argument.type === 'ObjectExpression'
+          && node.argument.properties.every(isProperty)
+        ) {
+          return;
+        }
+        report(context, messages.noSpreading, 'noSpreading', {
+          node,
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-sort-default-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-sort-default-props.d.ts","sourceRoot":"","sources":["jsx-sort-default-props.js"],"names":[],"mappings":"wBA2BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-default-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,195 @@
+/**
+ * @fileoverview Enforce default props alphabetical sorting
+ * @author Vladimir Kattsov
+ * @deprecated
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const log = require('../util/log');
+const eslintUtil = require('../util/eslint');
+
+const getFirstTokens = eslintUtil.getFirstTokens;
+const getText = eslintUtil.getText;
+
+let isWarnedForDeprecation = false;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  propsNotSorted: 'Default prop types declarations should be sorted alphabetically',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    deprecated: true,
+    replacedBy: ['sort-default-props'],
+    docs: {
+      description: 'Enforce defaultProps declarations alphabetical sorting',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-sort-default-props'),
+    },
+    // fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignoreCase: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const ignoreCase = configuration.ignoreCase || false;
+
+    /**
+     * Get properties name
+     * @param {Object} node - Property.
+     * @returns {string} Property name.
+     */
+    function getPropertyName(node) {
+      if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
+        return node.key.name;
+      }
+      if (node.type === 'MemberExpression') {
+        return node.property.name;
+      // Special case for class properties
+      // (babel-eslint@5 does not expose property name so we have to rely on tokens)
+      }
+      if (node.type === 'ClassProperty') {
+        const tokens = getFirstTokens(context, node, 2);
+        return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
+      }
+      return '';
+    }
+
+    /**
+     * Checks if the Identifier node passed in looks like a defaultProps declaration.
+     * @param   {ASTNode}  node The node to check. Must be an Identifier node.
+     * @returns {boolean}       `true` if the node is a defaultProps declaration, `false` if not
+     */
+    function isDefaultPropsDeclaration(node) {
+      const propName = getPropertyName(node);
+      return (propName === 'defaultProps' || propName === 'getDefaultProps');
+    }
+
+    function getKey(node) {
+      return getText(context, node.key || node.argument);
+    }
+
+    /**
+     * Find a variable by name in the current scope.
+     * @param  {ASTNode} node The node to look for.
+     * @param  {string} name Name of the variable to look for.
+     * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+     */
+    function findVariableByName(node, name) {
+      const variable = variableUtil
+        .getVariableFromContext(context, node, name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+        return null;
+      }
+
+      if (variable.defs[0].node.type === 'TypeAlias') {
+        return variable.defs[0].node.right;
+      }
+
+      return variable.defs[0].node.init;
+    }
+
+    /**
+     * Checks if defaultProps declarations are sorted
+     * @param {Array} declarations The array of AST nodes being checked.
+     * @returns {void}
+     */
+    function checkSorted(declarations) {
+      // function fix(fixer) {
+      //   return propTypesSortUtil.fixPropTypesSort(context, fixer, declarations, ignoreCase);
+      // }
+
+      declarations.reduce((prev, curr, idx, decls) => {
+        if (/Spread(?:Property|Element)$/.test(curr.type)) {
+          return decls[idx + 1];
+        }
+
+        let prevPropName = getKey(prev);
+        let currentPropName = getKey(curr);
+
+        if (ignoreCase) {
+          prevPropName = prevPropName.toLowerCase();
+          currentPropName = currentPropName.toLowerCase();
+        }
+
+        if (currentPropName < prevPropName) {
+          report(context, messages.propsNotSorted, 'propsNotSorted', {
+            node: curr,
+            // fix
+          });
+
+          return prev;
+        }
+
+        return curr;
+      }, declarations[0]);
+    }
+
+    function checkNode(node) {
+      if (!node) {
+        return;
+      }
+      if (node.type === 'ObjectExpression') {
+        checkSorted(node.properties);
+      } else if (node.type === 'Identifier') {
+        const propTypesObject = findVariableByName(node, node.name);
+        if (propTypesObject && propTypesObject.properties) {
+          checkSorted(propTypesObject.properties);
+        }
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public API
+    // --------------------------------------------------------------------------
+
+    return {
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!isDefaultPropsDeclaration(node)) {
+          return;
+        }
+
+        checkNode(node.value);
+      },
+
+      MemberExpression(node) {
+        if (!isDefaultPropsDeclaration(node)) {
+          return;
+        }
+
+        checkNode('right' in node.parent && node.parent.right);
+      },
+
+      Program() {
+        if (isWarnedForDeprecation) {
+          return;
+        }
+
+        log('The react/jsx-sort-default-props rule is deprecated. It has been renamed to `react/sort-default-props`.');
+        isWarnedForDeprecation = true;
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-sort-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-sort-props.d.ts","sourceRoot":"","sources":["jsx-sort-props.js"],"names":[],"mappings":"wBAoVW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,531 @@
+/**
+ * @fileoverview Enforce props alphabetical sorting
+ * @author Ilya Volodin, Yannick Croissant
+ */
+
+'use strict';
+
+const propName = require('jsx-ast-utils/propName');
+const includes = require('array-includes');
+const toSorted = require('array.prototype.tosorted');
+
+const docsUrl = require('../util/docsUrl');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+const propTypesSortUtil = require('../util/propTypesSort');
+const eslintUtil = require('../util/eslint');
+
+const getText = eslintUtil.getText;
+const getSourceCode = eslintUtil.getSourceCode;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function isMultilineProp(node) {
+  return node.loc.start.line !== node.loc.end.line;
+}
+
+const messages = {
+  noUnreservedProps: 'A customized reserved first list must only contain a subset of React reserved props. Remove: {{unreservedWords}}',
+  listIsEmpty: 'A customized reserved first list must not be empty',
+  listReservedPropsFirst: 'Reserved props must be listed before all other props',
+  listCallbacksLast: 'Callbacks must be listed after all other props',
+  listShorthandFirst: 'Shorthand props must be listed before all other props',
+  listShorthandLast: 'Shorthand props must be listed after all other props',
+  listMultilineFirst: 'Multiline props must be listed before all other props',
+  listMultilineLast: 'Multiline props must be listed after all other props',
+  sortPropsByAlpha: 'Props should be sorted alphabetically',
+};
+
+const RESERVED_PROPS_LIST = [
+  'children',
+  'dangerouslySetInnerHTML',
+  'key',
+  'ref',
+];
+
+function isReservedPropName(name, list) {
+  return list.indexOf(name) >= 0;
+}
+
+let attributeMap;
+// attributeMap = { end: endrange, hasComment: true||false if comment in between nodes exists, it needs to be sorted to end }
+
+function shouldSortToEnd(node) {
+  const attr = attributeMap.get(node);
+  return !!attr && !!attr.hasComment;
+}
+
+function contextCompare(a, b, options) {
+  let aProp = propName(a);
+  let bProp = propName(b);
+
+  const aSortToEnd = shouldSortToEnd(a);
+  const bSortToEnd = shouldSortToEnd(b);
+  if (aSortToEnd && !bSortToEnd) {
+    return 1;
+  }
+  if (!aSortToEnd && bSortToEnd) {
+    return -1;
+  }
+
+  if (options.reservedFirst) {
+    const aIsReserved = isReservedPropName(aProp, options.reservedList);
+    const bIsReserved = isReservedPropName(bProp, options.reservedList);
+    if (aIsReserved && !bIsReserved) {
+      return -1;
+    }
+    if (!aIsReserved && bIsReserved) {
+      return 1;
+    }
+  }
+
+  if (options.callbacksLast) {
+    const aIsCallback = propTypesSortUtil.isCallbackPropName(aProp);
+    const bIsCallback = propTypesSortUtil.isCallbackPropName(bProp);
+    if (aIsCallback && !bIsCallback) {
+      return 1;
+    }
+    if (!aIsCallback && bIsCallback) {
+      return -1;
+    }
+  }
+
+  if (options.shorthandFirst || options.shorthandLast) {
+    const shorthandSign = options.shorthandFirst ? -1 : 1;
+    if (!a.value && b.value) {
+      return shorthandSign;
+    }
+    if (a.value && !b.value) {
+      return -shorthandSign;
+    }
+  }
+
+  if (options.multiline !== 'ignore') {
+    const multilineSign = options.multiline === 'first' ? -1 : 1;
+    const aIsMultiline = isMultilineProp(a);
+    const bIsMultiline = isMultilineProp(b);
+    if (aIsMultiline && !bIsMultiline) {
+      return multilineSign;
+    }
+    if (!aIsMultiline && bIsMultiline) {
+      return -multilineSign;
+    }
+  }
+
+  if (options.noSortAlphabetically) {
+    return 0;
+  }
+
+  const actualLocale = options.locale === 'auto' ? undefined : options.locale;
+
+  if (options.ignoreCase) {
+    aProp = aProp.toLowerCase();
+    bProp = bProp.toLowerCase();
+    return aProp.localeCompare(bProp, actualLocale);
+  }
+  if (aProp === bProp) {
+    return 0;
+  }
+  if (options.locale === 'auto') {
+    return aProp < bProp ? -1 : 1;
+  }
+  return aProp.localeCompare(bProp, actualLocale);
+}
+
+/**
+ * Create an array of arrays where each subarray is composed of attributes
+ * that are considered sortable.
+ * @param {Array<JSXSpreadAttribute|JSXAttribute>} attributes
+ * @param {Object} context The context of the rule
+ * @return {Array<Array<JSXAttribute>>}
+ */
+function getGroupsOfSortableAttributes(attributes, context) {
+  const sourceCode = getSourceCode(context);
+
+  const sortableAttributeGroups = [];
+  let groupCount = 0;
+  function addtoSortableAttributeGroups(attribute) {
+    sortableAttributeGroups[groupCount - 1].push(attribute);
+  }
+
+  for (let i = 0; i < attributes.length; i++) {
+    const attribute = attributes[i];
+    const nextAttribute = attributes[i + 1];
+    const attributeline = attribute.loc.start.line;
+    let comment = [];
+    try {
+      comment = sourceCode.getCommentsAfter(attribute);
+    } catch (e) { /**/ }
+    const lastAttr = attributes[i - 1];
+    const attrIsSpread = attribute.type === 'JSXSpreadAttribute';
+
+    // If we have no groups or if the last attribute was JSXSpreadAttribute
+    // then we start a new group. Append attributes to the group until we
+    // come across another JSXSpreadAttribute or exhaust the array.
+    if (
+      !lastAttr
+      || (lastAttr.type === 'JSXSpreadAttribute' && !attrIsSpread)
+    ) {
+      groupCount += 1;
+      sortableAttributeGroups[groupCount - 1] = [];
+    }
+    if (!attrIsSpread) {
+      if (comment.length === 0) {
+        attributeMap.set(attribute, { end: attribute.range[1], hasComment: false });
+        addtoSortableAttributeGroups(attribute);
+      } else {
+        const firstComment = comment[0];
+        const commentline = firstComment.loc.start.line;
+        if (comment.length === 1) {
+          if (attributeline + 1 === commentline && nextAttribute) {
+            attributeMap.set(attribute, { end: nextAttribute.range[1], hasComment: true });
+            addtoSortableAttributeGroups(attribute);
+            i += 1;
+          } else if (attributeline === commentline) {
+            if (firstComment.type === 'Block' && nextAttribute) {
+              attributeMap.set(attribute, { end: nextAttribute.range[1], hasComment: true });
+              i += 1;
+            } else if (firstComment.type === 'Block') {
+              attributeMap.set(attribute, { end: firstComment.range[1], hasComment: true });
+            } else {
+              attributeMap.set(attribute, { end: firstComment.range[1], hasComment: false });
+            }
+            addtoSortableAttributeGroups(attribute);
+          }
+        } else if (comment.length > 1 && attributeline + 1 === comment[1].loc.start.line && nextAttribute) {
+          const commentNextAttribute = sourceCode.getCommentsAfter(nextAttribute);
+          attributeMap.set(attribute, { end: nextAttribute.range[1], hasComment: true });
+          if (
+            commentNextAttribute.length === 1
+            && nextAttribute.loc.start.line === commentNextAttribute[0].loc.start.line
+          ) {
+            attributeMap.set(attribute, { end: commentNextAttribute[0].range[1], hasComment: true });
+          }
+          addtoSortableAttributeGroups(attribute);
+          i += 1;
+        }
+      }
+    }
+  }
+  return sortableAttributeGroups;
+}
+
+function generateFixerFunction(node, context, reservedList) {
+  const attributes = node.attributes.slice(0);
+  const configuration = context.options[0] || {};
+  const ignoreCase = configuration.ignoreCase || false;
+  const callbacksLast = configuration.callbacksLast || false;
+  const shorthandFirst = configuration.shorthandFirst || false;
+  const shorthandLast = configuration.shorthandLast || false;
+  const multiline = configuration.multiline || 'ignore';
+  const noSortAlphabetically = configuration.noSortAlphabetically || false;
+  const reservedFirst = configuration.reservedFirst || false;
+  const locale = configuration.locale || 'auto';
+
+  // Sort props according to the context. Only supports ignoreCase.
+  // Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
+  // we only consider groups of sortable attributes.
+  const options = {
+    ignoreCase,
+    callbacksLast,
+    shorthandFirst,
+    shorthandLast,
+    multiline,
+    noSortAlphabetically,
+    reservedFirst,
+    reservedList,
+    locale,
+  };
+  const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context);
+  const sortedAttributeGroups = sortableAttributeGroups
+    .slice(0)
+    .map((group) => toSorted(group, (a, b) => contextCompare(a, b, options)));
+
+  return function fixFunction(fixer) {
+    const fixers = [];
+    let source = getText(context);
+
+    sortableAttributeGroups.forEach((sortableGroup, ii) => {
+      sortableGroup.forEach((attr, jj) => {
+        const sortedAttr = sortedAttributeGroups[ii][jj];
+        const sortedAttrText = source.slice(sortedAttr.range[0], attributeMap.get(sortedAttr).end);
+        fixers.push({
+          range: [attr.range[0], attributeMap.get(attr).end],
+          text: sortedAttrText,
+        });
+      });
+    });
+
+    fixers.sort((a, b) => b.range[0] - a.range[0]);
+
+    const firstFixer = fixers[0];
+    const lastFixer = fixers[fixers.length - 1];
+    const rangeStart = lastFixer ? lastFixer.range[0] : 0;
+    const rangeEnd = firstFixer ? firstFixer.range[1] : -0;
+
+    fixers.forEach((fix) => {
+      source = `${source.slice(0, fix.range[0])}${fix.text}${source.slice(fix.range[1])}`;
+    });
+
+    return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd));
+  };
+}
+
+/**
+ * Checks if the `reservedFirst` option is valid
+ * @param {Object} context The context of the rule
+ * @param {boolean | string[]} reservedFirst The `reservedFirst` option
+ * @return {Function | undefined} If an error is detected, a function to generate the error message, otherwise, `undefined`
+ */
+// eslint-disable-next-line consistent-return
+function validateReservedFirstConfig(context, reservedFirst) {
+  if (reservedFirst) {
+    if (Array.isArray(reservedFirst)) {
+      // Only allow a subset of reserved words in customized lists
+      const nonReservedWords = reservedFirst.filter((word) => !isReservedPropName(
+        word,
+        RESERVED_PROPS_LIST
+      ));
+
+      if (reservedFirst.length === 0) {
+        return function Report(decl) {
+          report(context, messages.listIsEmpty, 'listIsEmpty', {
+            node: decl,
+          });
+        };
+      }
+      if (nonReservedWords.length > 0) {
+        return function Report(decl) {
+          report(context, messages.noUnreservedProps, 'noUnreservedProps', {
+            node: decl,
+            data: {
+              unreservedWords: nonReservedWords.toString(),
+            },
+          });
+        };
+      }
+    }
+  }
+}
+
+const reportedNodeAttributes = new WeakMap();
+/**
+ * Check if the current node attribute has already been reported with the same error type
+ * if that's the case then we don't report a new error
+ * otherwise we report the error
+ * @param {Object} nodeAttribute The node attribute to be reported
+ * @param {string} errorType The error type to be reported
+ * @param {Object} node The parent node for the node attribute
+ * @param {Object} context The context of the rule
+ * @param {Array<String>} reservedList The list of reserved props
+ */
+function reportNodeAttribute(nodeAttribute, errorType, node, context, reservedList) {
+  const errors = reportedNodeAttributes.get(nodeAttribute) || [];
+
+  if (includes(errors, errorType)) {
+    return;
+  }
+
+  errors.push(errorType);
+
+  reportedNodeAttributes.set(nodeAttribute, errors);
+
+  report(context, messages[errorType], errorType, {
+    node: nodeAttribute.name,
+    fix: generateFixerFunction(node, context, reservedList),
+  });
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce props alphabetical sorting',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-sort-props'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        // Whether callbacks (prefixed with "on") should be listed at the very end,
+        // after all other props. Supersedes shorthandLast.
+        callbacksLast: {
+          type: 'boolean',
+        },
+        // Whether shorthand properties (without a value) should be listed first
+        shorthandFirst: {
+          type: 'boolean',
+        },
+        // Whether shorthand properties (without a value) should be listed last
+        shorthandLast: {
+          type: 'boolean',
+        },
+        // Whether multiline properties should be listed first or last
+        multiline: {
+          enum: ['ignore', 'first', 'last'],
+          default: 'ignore',
+        },
+        ignoreCase: {
+          type: 'boolean',
+        },
+        // Whether alphabetical sorting should be enforced
+        noSortAlphabetically: {
+          type: 'boolean',
+        },
+        reservedFirst: {
+          type: ['array', 'boolean'],
+        },
+        locale: {
+          type: 'string',
+          default: 'auto',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const ignoreCase = configuration.ignoreCase || false;
+    const callbacksLast = configuration.callbacksLast || false;
+    const shorthandFirst = configuration.shorthandFirst || false;
+    const shorthandLast = configuration.shorthandLast || false;
+    const multiline = configuration.multiline || 'ignore';
+    const noSortAlphabetically = configuration.noSortAlphabetically || false;
+    const reservedFirst = configuration.reservedFirst || false;
+    const reservedFirstError = validateReservedFirstConfig(context, reservedFirst);
+    const reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST;
+    const locale = configuration.locale || 'auto';
+
+    return {
+      Program() {
+        attributeMap = new WeakMap();
+      },
+
+      JSXOpeningElement(node) {
+        // `dangerouslySetInnerHTML` is only "reserved" on DOM components
+        const nodeReservedList = reservedFirst && !jsxUtil.isDOMComponent(node) ? reservedList.filter((prop) => prop !== 'dangerouslySetInnerHTML') : reservedList;
+
+        node.attributes.reduce((memo, decl, idx, attrs) => {
+          if (decl.type === 'JSXSpreadAttribute') {
+            return attrs[idx + 1];
+          }
+
+          let previousPropName = propName(memo);
+          let currentPropName = propName(decl);
+          const previousValue = memo.value;
+          const currentValue = decl.value;
+          const previousIsCallback = propTypesSortUtil.isCallbackPropName(previousPropName);
+          const currentIsCallback = propTypesSortUtil.isCallbackPropName(currentPropName);
+
+          if (ignoreCase) {
+            previousPropName = previousPropName.toLowerCase();
+            currentPropName = currentPropName.toLowerCase();
+          }
+
+          if (reservedFirst) {
+            if (reservedFirstError) {
+              reservedFirstError(decl);
+              return memo;
+            }
+
+            const previousIsReserved = isReservedPropName(previousPropName, nodeReservedList);
+            const currentIsReserved = isReservedPropName(currentPropName, nodeReservedList);
+
+            if (previousIsReserved && !currentIsReserved) {
+              return decl;
+            }
+            if (!previousIsReserved && currentIsReserved) {
+              reportNodeAttribute(decl, 'listReservedPropsFirst', node, context, nodeReservedList);
+
+              return memo;
+            }
+          }
+
+          if (callbacksLast) {
+            if (!previousIsCallback && currentIsCallback) {
+              // Entering the callback prop section
+              return decl;
+            }
+            if (previousIsCallback && !currentIsCallback) {
+              // Encountered a non-callback prop after a callback prop
+              reportNodeAttribute(memo, 'listCallbacksLast', node, context, nodeReservedList);
+
+              return memo;
+            }
+          }
+
+          if (shorthandFirst) {
+            if (currentValue && !previousValue) {
+              return decl;
+            }
+            if (!currentValue && previousValue) {
+              reportNodeAttribute(decl, 'listShorthandFirst', node, context, nodeReservedList);
+
+              return memo;
+            }
+          }
+
+          if (shorthandLast) {
+            if (!currentValue && previousValue) {
+              return decl;
+            }
+            if (currentValue && !previousValue) {
+              reportNodeAttribute(memo, 'listShorthandLast', node, context, nodeReservedList);
+
+              return memo;
+            }
+          }
+
+          const previousIsMultiline = isMultilineProp(memo);
+          const currentIsMultiline = isMultilineProp(decl);
+          if (multiline === 'first') {
+            if (previousIsMultiline && !currentIsMultiline) {
+              // Exiting the multiline prop section
+              return decl;
+            }
+            if (!previousIsMultiline && currentIsMultiline) {
+              // Encountered a non-multiline prop before a multiline prop
+              reportNodeAttribute(decl, 'listMultilineFirst', node, context, nodeReservedList);
+
+              return memo;
+            }
+          } else if (multiline === 'last') {
+            if (!previousIsMultiline && currentIsMultiline) {
+              // Entering the multiline prop section
+              return decl;
+            }
+            if (previousIsMultiline && !currentIsMultiline) {
+              // Encountered a non-multiline prop after a multiline prop
+              reportNodeAttribute(memo, 'listMultilineLast', node, context, nodeReservedList);
+
+              return memo;
+            }
+          }
+
+          if (
+            !noSortAlphabetically
+            && (
+              (ignoreCase || locale !== 'auto')
+                ? previousPropName.localeCompare(currentPropName, locale === 'auto' ? undefined : locale) > 0
+                : previousPropName > currentPropName
+            )
+          ) {
+            reportNodeAttribute(decl, 'sortPropsByAlpha', node, context, nodeReservedList);
+
+            return memo;
+          }
+
+          return decl;
+        }, node.attributes[0]);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-space-before-closing.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-space-before-closing.d.ts","sourceRoot":"","sources":["jsx-space-before-closing.js"],"names":[],"mappings":"wBAyBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-space-before-closing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,98 @@
+/**
+ * @fileoverview Validate spacing before closing bracket in JSX.
+ * @author ryym
+ * @deprecated
+ */
+
+'use strict';
+
+const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket');
+const docsUrl = require('../util/docsUrl');
+const log = require('../util/log');
+const report = require('../util/report');
+const getSourceCode = require('../util/eslint').getSourceCode;
+
+let isWarnedForDeprecation = false;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noSpaceBeforeClose: 'A space is forbidden before closing bracket',
+  needSpaceBeforeClose: 'A space is required before closing bracket',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    deprecated: true,
+    replacedBy: ['jsx-tag-spacing'],
+    docs: {
+      description: 'Enforce spacing before closing bracket in JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-space-before-closing'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      enum: ['always', 'never'],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || 'always';
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      JSXOpeningElement(node) {
+        if (!node.selfClosing) {
+          return;
+        }
+
+        const sourceCode = getSourceCode(context);
+
+        const leftToken = getTokenBeforeClosingBracket(node);
+        const closingSlash = /** @type {import('eslint').AST.Token} */ (sourceCode.getTokenAfter(leftToken));
+
+        if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
+          return;
+        }
+
+        if (configuration === 'always' && !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
+          report(context, messages.needSpaceBeforeClose, 'needSpaceBeforeClose', {
+            loc: closingSlash.loc.start,
+            fix(fixer) {
+              return fixer.insertTextBefore(closingSlash, ' ');
+            },
+          });
+        } else if (configuration === 'never' && sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
+          report(context, messages.noSpaceBeforeClose, 'noSpaceBeforeClose', {
+            loc: closingSlash.loc.start,
+            fix(fixer) {
+              const previousToken = sourceCode.getTokenBefore(closingSlash);
+              return fixer.removeRange([previousToken.range[1], closingSlash.range[0]]);
+            },
+          });
+        }
+      },
+
+      Program() {
+        if (isWarnedForDeprecation) {
+          return;
+        }
+
+        log('The react/jsx-space-before-closing rule is deprecated. '
+            + 'Please use the react/jsx-tag-spacing rule with the '
+            + '"beforeSelfClosing" option instead.');
+        isWarnedForDeprecation = true;
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-tag-spacing.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-tag-spacing.d.ts","sourceRoot":"","sources":["jsx-tag-spacing.js"],"names":[],"mappings":"wBAqQW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-tag-spacing.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,328 @@
+/**
+ * @fileoverview Validates whitespace in and around the JSX opening and closing brackets
+ * @author Diogo Franco (Kovensky)
+ */
+
+'use strict';
+
+const getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getFirstTokens = eslintUtil.getFirstTokens;
+const getSourceCode = eslintUtil.getSourceCode;
+
+const messages = {
+  selfCloseSlashNoSpace: 'Whitespace is forbidden between `/` and `>`; write `/>`',
+  selfCloseSlashNeedSpace: 'Whitespace is required between `/` and `>`; write `/ >`',
+  closeSlashNoSpace: 'Whitespace is forbidden between `<` and `/`; write `</`',
+  closeSlashNeedSpace: 'Whitespace is required between `<` and `/`; write `< /`',
+  beforeSelfCloseNoSpace: 'A space is forbidden before closing bracket',
+  beforeSelfCloseNeedSpace: 'A space is required before closing bracket',
+  beforeSelfCloseNeedNewline: 'A newline is required before closing bracket',
+  afterOpenNoSpace: 'A space is forbidden after opening bracket',
+  afterOpenNeedSpace: 'A space is required after opening bracket',
+  beforeCloseNoSpace: 'A space is forbidden before closing bracket',
+  beforeCloseNeedSpace: 'Whitespace is required before closing bracket',
+  beforeCloseNeedNewline: 'A newline is required before closing bracket',
+};
+
+// ------------------------------------------------------------------------------
+// Validators
+// ------------------------------------------------------------------------------
+
+function validateClosingSlash(context, node, option) {
+  const sourceCode = getSourceCode(context);
+
+  let adjacent;
+
+  if (node.selfClosing) {
+    const lastTokens = sourceCode.getLastTokens(node, 2);
+
+    adjacent = !sourceCode.isSpaceBetweenTokens(lastTokens[0], lastTokens[1]);
+
+    if (option === 'never') {
+      if (!adjacent) {
+        report(context, messages.selfCloseSlashNoSpace, 'selfCloseSlashNoSpace', {
+          node,
+          loc: {
+            start: lastTokens[0].loc.start,
+            end: lastTokens[1].loc.end,
+          },
+          fix(fixer) {
+            return fixer.removeRange([lastTokens[0].range[1], lastTokens[1].range[0]]);
+          },
+        });
+      }
+    } else if (option === 'always' && adjacent) {
+      report(context, messages.selfCloseSlashNeedSpace, 'selfCloseSlashNeedSpace', {
+        node,
+        loc: {
+          start: lastTokens[0].loc.start,
+          end: lastTokens[1].loc.end,
+        },
+        fix(fixer) {
+          return fixer.insertTextBefore(lastTokens[1], ' ');
+        },
+      });
+    }
+  } else {
+    const firstTokens = getFirstTokens(context, node, 2);
+
+    adjacent = !sourceCode.isSpaceBetweenTokens(firstTokens[0], firstTokens[1]);
+
+    if (option === 'never') {
+      if (!adjacent) {
+        report(context, messages.closeSlashNoSpace, 'closeSlashNoSpace', {
+          node,
+          loc: {
+            start: firstTokens[0].loc.start,
+            end: firstTokens[1].loc.end,
+          },
+          fix(fixer) {
+            return fixer.removeRange([firstTokens[0].range[1], firstTokens[1].range[0]]);
+          },
+        });
+      }
+    } else if (option === 'always' && adjacent) {
+      report(context, messages.closeSlashNeedSpace, 'closeSlashNeedSpace', {
+        node,
+        loc: {
+          start: firstTokens[0].loc.start,
+          end: firstTokens[1].loc.end,
+        },
+        fix(fixer) {
+          return fixer.insertTextBefore(firstTokens[1], ' ');
+        },
+      });
+    }
+  }
+}
+
+function validateBeforeSelfClosing(context, node, option) {
+  const sourceCode = getSourceCode(context);
+  const leftToken = getTokenBeforeClosingBracket(node);
+  const closingSlash = sourceCode.getTokenAfter(leftToken);
+
+  if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
+    if (leftToken.loc.end.line === closingSlash.loc.start.line) {
+      report(context, messages.beforeSelfCloseNeedNewline, 'beforeSelfCloseNeedNewline', {
+        node,
+        loc: leftToken.loc.end,
+        fix(fixer) {
+          return fixer.insertTextBefore(closingSlash, '\n');
+        },
+      });
+      return;
+    }
+  }
+
+  if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
+    return;
+  }
+
+  const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash);
+
+  if ((option === 'always' || option === 'proportional-always') && adjacent) {
+    report(context, messages.beforeSelfCloseNeedSpace, 'beforeSelfCloseNeedSpace', {
+      node,
+      loc: closingSlash.loc.start,
+      fix(fixer) {
+        return fixer.insertTextBefore(closingSlash, ' ');
+      },
+    });
+  } else if (option === 'never' && !adjacent) {
+    report(context, messages.beforeSelfCloseNoSpace, 'beforeSelfCloseNoSpace', {
+      node,
+      loc: closingSlash.loc.start,
+      fix(fixer) {
+        const previousToken = sourceCode.getTokenBefore(closingSlash);
+        return fixer.removeRange([previousToken.range[1], closingSlash.range[0]]);
+      },
+    });
+  }
+}
+
+function validateAfterOpening(context, node, option) {
+  const sourceCode = getSourceCode(context);
+  const openingToken = sourceCode.getTokenBefore(node.name);
+
+  if (option === 'allow-multiline') {
+    if (openingToken.loc.start.line !== node.name.loc.start.line) {
+      return;
+    }
+  }
+
+  const adjacent = !sourceCode.isSpaceBetweenTokens(openingToken, node.name);
+
+  if (option === 'never' || option === 'allow-multiline') {
+    if (!adjacent) {
+      report(context, messages.afterOpenNoSpace, 'afterOpenNoSpace', {
+        node,
+        loc: {
+          start: openingToken.loc.start,
+          end: node.name.loc.start,
+        },
+        fix(fixer) {
+          return fixer.removeRange([openingToken.range[1], node.name.range[0]]);
+        },
+      });
+    }
+  } else if (option === 'always' && adjacent) {
+    report(context, messages.afterOpenNeedSpace, 'afterOpenNeedSpace', {
+      node,
+      loc: {
+        start: openingToken.loc.start,
+        end: node.name.loc.start,
+      },
+      fix(fixer) {
+        return fixer.insertTextBefore(node.name, ' ');
+      },
+    });
+  }
+}
+
+function validateBeforeClosing(context, node, option) {
+  // Don't enforce this rule for self closing tags
+  if (!node.selfClosing) {
+    const sourceCode = getSourceCode(context);
+    const leftToken = option === 'proportional-always'
+      ? getTokenBeforeClosingBracket(node)
+      : sourceCode.getLastTokens(node, 2)[0];
+    const closingToken = sourceCode.getTokenAfter(leftToken);
+
+    if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
+      if (leftToken.loc.end.line === closingToken.loc.start.line) {
+        report(context, messages.beforeCloseNeedNewline, 'beforeCloseNeedNewline', {
+          node,
+          loc: leftToken.loc.end,
+          fix(fixer) {
+            return fixer.insertTextBefore(closingToken, '\n');
+          },
+        });
+        return;
+      }
+    }
+
+    if (leftToken.loc.start.line !== closingToken.loc.start.line) {
+      return;
+    }
+
+    const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingToken);
+
+    if (option === 'never' && !adjacent) {
+      report(context, messages.beforeCloseNoSpace, 'beforeCloseNoSpace', {
+        node,
+        loc: {
+          start: leftToken.loc.end,
+          end: closingToken.loc.start,
+        },
+        fix(fixer) {
+          return fixer.removeRange([leftToken.range[1], closingToken.range[0]]);
+        },
+      });
+    } else if (option === 'always' && adjacent) {
+      report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
+        node,
+        loc: {
+          start: leftToken.loc.end,
+          end: closingToken.loc.start,
+        },
+        fix(fixer) {
+          return fixer.insertTextBefore(closingToken, ' ');
+        },
+      });
+    } else if (option === 'proportional-always' && node.type === 'JSXOpeningElement' && adjacent !== (node.loc.start.line === node.loc.end.line)) {
+      report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
+        node,
+        loc: {
+          start: leftToken.loc.end,
+          end: closingToken.loc.start,
+        },
+        fix(fixer) {
+          return fixer.insertTextBefore(closingToken, ' ');
+        },
+      });
+    }
+  }
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const optionDefaults = {
+  closingSlash: 'never',
+  beforeSelfClosing: 'always',
+  afterOpening: 'never',
+  beforeClosing: 'allow',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce whitespace in and around the JSX opening and closing brackets',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-tag-spacing'),
+    },
+    fixable: 'whitespace',
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          closingSlash: {
+            enum: ['always', 'never', 'allow'],
+          },
+          beforeSelfClosing: {
+            enum: ['always', 'proportional-always', 'never', 'allow'],
+          },
+          afterOpening: {
+            enum: ['always', 'allow-multiline', 'never', 'allow'],
+          },
+          beforeClosing: {
+            enum: ['always', 'proportional-always', 'never', 'allow'],
+          },
+        },
+        default: optionDefaults,
+        additionalProperties: false,
+      },
+    ],
+  },
+  create(context) {
+    const options = Object.assign({}, optionDefaults, context.options[0]);
+
+    return {
+      JSXOpeningElement(node) {
+        if (options.closingSlash !== 'allow' && node.selfClosing) {
+          validateClosingSlash(context, node, options.closingSlash);
+        }
+        if (options.afterOpening !== 'allow') {
+          validateAfterOpening(context, node, options.afterOpening);
+        }
+        if (options.beforeSelfClosing !== 'allow' && node.selfClosing) {
+          validateBeforeSelfClosing(context, node, options.beforeSelfClosing);
+        }
+        if (options.beforeClosing !== 'allow') {
+          validateBeforeClosing(context, node, options.beforeClosing);
+        }
+      },
+      JSXClosingElement(node) {
+        if (options.afterOpening !== 'allow') {
+          validateAfterOpening(context, node, options.afterOpening);
+        }
+        if (options.closingSlash !== 'allow') {
+          validateClosingSlash(context, node, options.closingSlash);
+        }
+        if (options.beforeClosing !== 'allow') {
+          validateBeforeClosing(context, node, options.beforeClosing);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-uses-react.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-uses-react.d.ts","sourceRoot":"","sources":["jsx-uses-react.js"],"names":[],"mappings":"wBAeW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,52 @@
+/**
+ * @fileoverview Prevent React to be marked as unused
+ * @author Glen Mailer
+ */
+
+'use strict';
+
+const pragmaUtil = require('../util/pragma');
+const docsUrl = require('../util/docsUrl');
+const markVariableAsUsed = require('../util/eslint').markVariableAsUsed;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  // eslint-disable-next-line eslint-plugin/prefer-message-ids -- https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/292
+  meta: {
+    docs: {
+      description: 'Disallow React to be incorrectly marked as unused',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('jsx-uses-react'),
+    },
+    schema: [],
+  },
+
+  create(context) {
+    const pragma = pragmaUtil.getFromContext(context);
+    const fragment = pragmaUtil.getFragmentFromContext(context);
+
+    /**
+     * @param {ASTNode} node
+     * @returns {void}
+     */
+    function handleOpeningElement(node) {
+      markVariableAsUsed(pragma, node, context);
+    }
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      JSXOpeningElement: handleOpeningElement,
+      JSXOpeningFragment: handleOpeningElement,
+      JSXFragment(node) {
+        markVariableAsUsed(fragment, node, context);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-uses-vars.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-uses-vars.d.ts","sourceRoot":"","sources":["jsx-uses-vars.js"],"names":[],"mappings":"wBAiBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,62 @@
+/**
+ * @fileoverview Prevent variables used in JSX to be marked as unused
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const markVariableAsUsed = require('../util/eslint').markVariableAsUsed;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const isTagNameRe = /^[a-z]/;
+const isTagName = (name) => isTagNameRe.test(name);
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  // eslint-disable-next-line eslint-plugin/prefer-message-ids -- https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/292
+  meta: {
+    docs: {
+      description: 'Disallow variables used in JSX to be incorrectly marked as unused',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('jsx-uses-vars'),
+    },
+    schema: [],
+  },
+
+  create(context) {
+    return {
+      JSXOpeningElement(node) {
+        let name;
+        if (node.name.namespace) {
+          // <Foo:Bar>
+          return;
+        }
+        if (node.name.name) {
+          // <Foo>
+          name = node.name.name;
+          // Exclude lowercase tag names like <div>
+          if (isTagName(name)) {
+            return;
+          }
+        } else if (node.name.object) {
+          // <Foo...Bar>
+          let parent = node.name.object;
+          while (parent.object) {
+            parent = parent.object;
+          }
+          name = parent.name;
+        } else {
+          return;
+        }
+
+        markVariableAsUsed(name, node, context);
+      },
+
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=jsx-wrap-multilines.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx-wrap-multilines.d.ts","sourceRoot":"","sources":["jsx-wrap-multilines.js"],"names":[],"mappings":"wBAyCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/jsx-wrap-multilines.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,275 @@
+/**
+ * @fileoverview Prevent missing parentheses around multilines JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const has = require('hasown');
+const docsUrl = require('../util/docsUrl');
+const eslintUtil = require('../util/eslint');
+const jsxUtil = require('../util/jsx');
+const reportC = require('../util/report');
+const isParenthesized = require('../util/ast').isParenthesized;
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = {
+  declaration: 'parens',
+  assignment: 'parens',
+  return: 'parens',
+  arrow: 'parens',
+  condition: 'ignore',
+  logical: 'ignore',
+  prop: 'ignore',
+};
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  missingParens: 'Missing parentheses around multilines JSX',
+  extraParens: 'Expected no parentheses around multilines JSX',
+  parensOnNewLines: 'Parentheses around JSX should be on separate lines',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow missing parentheses around multiline JSX',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-wrap-multilines'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      // true/false are for backwards compatibility
+      properties: {
+        declaration: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+        assignment: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+        return: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+        arrow: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+        condition: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+        logical: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+        prop: {
+          enum: [true, false, 'ignore', 'parens', 'parens-new-line', 'never'],
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    function getOption(type) {
+      const userOptions = context.options[0] || {};
+      if (has(userOptions, type)) {
+        return userOptions[type];
+      }
+      return DEFAULTS[type];
+    }
+
+    function isEnabled(type) {
+      const option = getOption(type);
+      return option && option !== 'ignore';
+    }
+
+    function needsOpeningNewLine(node) {
+      const previousToken = getSourceCode(context).getTokenBefore(node);
+
+      if (!isParenthesized(context, node)) {
+        return false;
+      }
+
+      if (previousToken.loc.end.line === node.loc.start.line) {
+        return true;
+      }
+
+      return false;
+    }
+
+    function needsClosingNewLine(node) {
+      const nextToken = getSourceCode(context).getTokenAfter(node);
+
+      if (!isParenthesized(context, node)) {
+        return false;
+      }
+
+      if (node.loc.end.line === nextToken.loc.end.line) {
+        return true;
+      }
+
+      return false;
+    }
+
+    function isMultilines(node) {
+      return node.loc.start.line !== node.loc.end.line;
+    }
+
+    function report(node, messageId, fix) {
+      reportC(context, messages[messageId], messageId, {
+        node,
+        fix,
+      });
+    }
+
+    function trimTokenBeforeNewline(node, tokenBefore) {
+      // if the token before the jsx is a bracket or curly brace
+      // we don't want a space between the opening parentheses and the multiline jsx
+      const isBracket = tokenBefore.value === '{' || tokenBefore.value === '[';
+      return `${tokenBefore.value.trim()}${isBracket ? '' : ' '}`;
+    }
+
+    function check(node, type) {
+      if (!node || !jsxUtil.isJSX(node)) {
+        return;
+      }
+
+      const sourceCode = getSourceCode(context);
+      const option = getOption(type);
+
+      if ((option === true || option === 'parens') && !isParenthesized(context, node) && isMultilines(node)) {
+        report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(${getText(context, node)})`));
+      }
+
+      if (option === 'parens-new-line' && isMultilines(node)) {
+        if (!isParenthesized(context, node)) {
+          const tokenBefore = sourceCode.getTokenBefore(node, { includeComments: true });
+          const tokenAfter = sourceCode.getTokenAfter(node, { includeComments: true });
+          const start = node.loc.start;
+          if (tokenBefore.loc.end.line < start.line) {
+            // Strip newline after operator if parens newline is specified
+            report(
+              node,
+              'missingParens',
+              (fixer) => fixer.replaceTextRange(
+                [tokenBefore.range[0], tokenAfter && (tokenAfter.value === ';' || tokenAfter.value === '}') ? tokenAfter.range[0] : node.range[1]],
+                `${trimTokenBeforeNewline(node, tokenBefore)}(\n${start.column > 0 ? ' '.repeat(start.column) : ''}${getText(context, node)}\n${start.column > 0 ? ' '.repeat(start.column - 2) : ''})`
+              )
+            );
+          } else {
+            report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(\n${getText(context, node)}\n)`));
+          }
+        } else {
+          const needsOpening = needsOpeningNewLine(node);
+          const needsClosing = needsClosingNewLine(node);
+          if (needsOpening || needsClosing) {
+            report(node, 'parensOnNewLines', (fixer) => {
+              const text = getText(context, node);
+              let fixed = text;
+              if (needsOpening) {
+                fixed = `\n${fixed}`;
+              }
+              if (needsClosing) {
+                fixed = `${fixed}\n`;
+              }
+              return fixer.replaceText(node, fixed);
+            });
+          }
+        }
+      }
+
+      if (option === 'never' && isParenthesized(context, node)) {
+        const tokenBefore = sourceCode.getTokenBefore(node);
+        const tokenAfter = sourceCode.getTokenAfter(node);
+        report(node, 'extraParens', (fixer) => fixer.replaceTextRange(
+          [tokenBefore.range[0], tokenAfter.range[1]],
+          getText(context, node)
+        ));
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+
+      VariableDeclarator(node) {
+        const type = 'declaration';
+        if (!isEnabled(type)) {
+          return;
+        }
+        if (!isEnabled('condition') && node.init && node.init.type === 'ConditionalExpression') {
+          check(node.init.consequent, type);
+          check(node.init.alternate, type);
+          return;
+        }
+        check(node.init, type);
+      },
+
+      AssignmentExpression(node) {
+        const type = 'assignment';
+        if (!isEnabled(type)) {
+          return;
+        }
+        if (!isEnabled('condition') && node.right.type === 'ConditionalExpression') {
+          check(node.right.consequent, type);
+          check(node.right.alternate, type);
+          return;
+        }
+        check(node.right, type);
+      },
+
+      ReturnStatement(node) {
+        const type = 'return';
+        if (isEnabled(type)) {
+          check(node.argument, type);
+        }
+      },
+
+      'ArrowFunctionExpression:exit': (node) => {
+        const arrowBody = node.body;
+        const type = 'arrow';
+
+        if (isEnabled(type) && arrowBody.type !== 'BlockStatement') {
+          check(arrowBody, type);
+        }
+      },
+
+      ConditionalExpression(node) {
+        const type = 'condition';
+        if (isEnabled(type)) {
+          check(node.consequent, type);
+          check(node.alternate, type);
+        }
+      },
+
+      LogicalExpression(node) {
+        const type = 'logical';
+        if (isEnabled(type)) {
+          check(node.right, type);
+        }
+      },
+
+      JSXAttribute(node) {
+        const type = 'prop';
+        if (isEnabled(type) && node.value && node.value.type === 'JSXExpressionContainer') {
+          check(node.value.expression, type);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-access-state-in-setstate.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-access-state-in-setstate.d.ts","sourceRoot":"","sources":["no-access-state-in-setstate.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-access-state-in-setstate.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,211 @@
+/**
+ * @fileoverview Prevent usage of this.state within setState
+ * @author Rolf Erik Lekang, Jørgen Aaberg
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const report = require('../util/report');
+const getScope = require('../util/eslint').getScope;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  useCallback: 'Use callback in setState when referencing the previous state.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow when this.state is accessed within setState',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('no-access-state-in-setstate'),
+    },
+
+    messages,
+  },
+
+  create(context) {
+    function isSetStateCall(node) {
+      return astUtil.isCallExpression(node)
+        && node.callee.property
+        && node.callee.property.name === 'setState'
+        && node.callee.object.type === 'ThisExpression';
+    }
+
+    function isFirstArgumentInSetStateCall(current, node) {
+      if (!isSetStateCall(current)) {
+        return false;
+      }
+      while (node && node.parent !== current) {
+        node = node.parent;
+      }
+      return current.arguments[0] === node;
+    }
+
+    /**
+     * @param {ASTNode} node
+     * @returns {boolean}
+     */
+    function isClassComponent(node) {
+      return !!(
+        componentUtil.getParentES6Component(context, node)
+        || componentUtil.getParentES5Component(context, node)
+      );
+    }
+
+    // The methods array contains all methods or functions that are using this.state
+    // or that are calling another method or function using this.state
+    const methods = [];
+    // The vars array contains all variables that contains this.state
+    const vars = [];
+    return {
+      CallExpression(node) {
+        if (!isClassComponent(node)) {
+          return;
+        }
+        // Appends all the methods that are calling another
+        // method containing this.state to the methods array
+        methods.forEach((method) => {
+          if ('name' in node.callee && node.callee.name === method.methodName) {
+            let current = node.parent;
+            while (current.type !== 'Program') {
+              if (current.type === 'MethodDefinition') {
+                methods.push({
+                  methodName: 'name' in current.key ? current.key.name : undefined,
+                  node: method.node,
+                });
+                break;
+              }
+              current = current.parent;
+            }
+          }
+        });
+
+        // Finding all CallExpressions that is inside a setState
+        // to further check if they contains this.state
+        let current = node.parent;
+        while (current.type !== 'Program') {
+          if (isFirstArgumentInSetStateCall(current, node)) {
+            const methodName = 'name' in node.callee ? node.callee.name : undefined;
+            methods.forEach((method) => {
+              if (method.methodName === methodName) {
+                report(context, messages.useCallback, 'useCallback', {
+                  node: method.node,
+                });
+              }
+            });
+
+            break;
+          }
+          current = current.parent;
+        }
+      },
+
+      MemberExpression(node) {
+        if (
+          'name' in node.property
+          && node.property.name === 'state'
+          && node.object.type === 'ThisExpression'
+          && isClassComponent(node)
+        ) {
+          /** @type {import('eslint').Rule.Node} */
+          let current = node;
+          while (current.type !== 'Program') {
+            // Reporting if this.state is directly within this.setState
+            if (isFirstArgumentInSetStateCall(current, node)) {
+              report(context, messages.useCallback, 'useCallback', {
+                node,
+              });
+              break;
+            }
+
+            // Storing all functions and methods that contains this.state
+            if (current.type === 'MethodDefinition') {
+              methods.push({
+                methodName: 'name' in current.key ? current.key.name : undefined,
+                node,
+              });
+              break;
+            } else if (
+              current.type === 'FunctionExpression'
+              && 'key' in current.parent
+              && current.parent.key
+            ) {
+              methods.push({
+                methodName: 'name' in current.parent.key ? current.parent.key.name : undefined,
+                node,
+              });
+              break;
+            }
+
+            // Storing all variables containing this.state
+            if (current.type === 'VariableDeclarator') {
+              vars.push({
+                node,
+                scope: getScope(context, node),
+                variableName: 'name' in current.id ? current.id.name : undefined,
+              });
+              break;
+            }
+
+            current = current.parent;
+          }
+        }
+      },
+
+      Identifier(node) {
+        // Checks if the identifier is a variable within an object
+        /** @type {import('eslint').Rule.Node} */
+        let current = node;
+        while (current.parent.type === 'BinaryExpression') {
+          current = current.parent;
+        }
+        if (
+          ('value' in current.parent && current.parent.value === current)
+          || ('object' in current.parent && current.parent.object === current)
+        ) {
+          while (current.type !== 'Program') {
+            if (isFirstArgumentInSetStateCall(current, node)) {
+              vars
+                .filter((v) => v.scope === getScope(context, node) && v.variableName === node.name)
+                .forEach((v) => {
+                  report(context, messages.useCallback, 'useCallback', {
+                    node: v.node,
+                  });
+                });
+            }
+            current = current.parent;
+          }
+        }
+      },
+
+      ObjectPattern(node) {
+        const isDerivedFromThis = 'init' in node.parent && node.parent.init && node.parent.init.type === 'ThisExpression';
+        node.properties.forEach((property) => {
+          if (
+            property
+            && 'key' in property
+            && property.key
+            && 'name' in property.key
+            && property.key.name === 'state'
+            && isDerivedFromThis
+          ) {
+            vars.push({
+              node: property.key,
+              scope: getScope(context, node),
+              variableName: property.key.name,
+            });
+          }
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-adjacent-inline-elements.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-adjacent-inline-elements.d.ts","sourceRoot":"","sources":["no-adjacent-inline-elements.js"],"names":[],"mappings":"wBA+EW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-adjacent-inline-elements.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,127 @@
+/**
+ * @fileoverview Prevent adjacent inline elements not separated by whitespace.
+ * @author Sean Hayes
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+const astUtil = require('../util/ast');
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+// https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
+const inlineNames = [
+  'a',
+  'b',
+  'big',
+  'i',
+  'small',
+  'tt',
+  'abbr',
+  'acronym',
+  'cite',
+  'code',
+  'dfn',
+  'em',
+  'kbd',
+  'strong',
+  'samp',
+  'time',
+  'var',
+  'bdo',
+  'br',
+  'img',
+  'map',
+  'object',
+  'q',
+  'script',
+  'span',
+  'sub',
+  'sup',
+  'button',
+  'input',
+  'label',
+  'select',
+  'textarea',
+];
+// Note: raw &nbsp; will be transformed into \u00a0.
+const whitespaceRegex = /(?:^\s|\s$)/;
+
+function isInline(node) {
+  if (node.type === 'Literal') {
+    // Regular whitespace will be removed.
+    const value = node.value;
+    // To properly separate inline elements, each end of the literal will need
+    // whitespace.
+    return !whitespaceRegex.test(value);
+  }
+  if (node.type === 'JSXElement' && inlineNames.indexOf(node.openingElement.name.name) > -1) {
+    return true;
+  }
+  if (astUtil.isCallExpression(node) && inlineNames.indexOf(node.arguments[0].value) > -1) {
+    return true;
+  }
+  return false;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  inlineElement: 'Child elements which render as inline HTML elements should be separated by a space or wrapped in block level elements.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow adjacent inline elements not separated by whitespace.',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-adjacent-inline-elements'),
+    },
+    schema: [],
+
+    messages,
+  },
+  create(context) {
+    function validate(node, children) {
+      let currentIsInline = false;
+      let previousIsInline = false;
+      if (!children) {
+        return;
+      }
+      for (let i = 0; i < children.length; i++) {
+        currentIsInline = isInline(children[i]);
+        if (previousIsInline && currentIsInline) {
+          report(context, messages.inlineElement, 'inlineElement', {
+            node,
+          });
+          return;
+        }
+        previousIsInline = currentIsInline;
+      }
+    }
+    return {
+      JSXElement(node) {
+        validate(node, node.children);
+      },
+      CallExpression(node) {
+        if (!isCreateElement(context, node)) {
+          return;
+        }
+        if (node.arguments.length < 2 || !node.arguments[2]) {
+          return;
+        }
+        const children = 'elements' in node.arguments[2] ? node.arguments[2].elements : undefined;
+        validate(node, children);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-array-index-key.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-array-index-key.d.ts","sourceRoot":"","sources":["no-array-index-key.js"],"names":[],"mappings":"wBA2CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-array-index-key.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,293 @@
+/**
+ * @fileoverview Prevent usage of Array index in keys
+ * @author Joe Lencioni
+ */
+
+'use strict';
+
+const has = require('hasown');
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const pragma = require('../util/pragma');
+const report = require('../util/report');
+const variableUtil = require('../util/variable');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function isCreateCloneElement(node, context) {
+  if (!node) {
+    return false;
+  }
+
+  if (node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression') {
+    return node.object
+      && node.object.name === pragma.getFromContext(context)
+      && ['createElement', 'cloneElement'].indexOf(node.property.name) !== -1;
+  }
+
+  if (node.type === 'Identifier') {
+    const variable = variableUtil.findVariableByName(context, node, node.name);
+    if (variable && variable.type === 'ImportSpecifier') {
+      return variable.parent.source.value === 'react';
+    }
+  }
+
+  return false;
+}
+
+const messages = {
+  noArrayIndex: 'Do not use Array index in keys',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of Array index in keys',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-array-index-key'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+    const indexParamNames = [];
+    const iteratorFunctionsToIndexParamPosition = {
+      every: 1,
+      filter: 1,
+      find: 1,
+      findIndex: 1,
+      flatMap: 1,
+      forEach: 1,
+      map: 1,
+      reduce: 2,
+      reduceRight: 2,
+      some: 1,
+    };
+
+    function isArrayIndex(node) {
+      return node.type === 'Identifier'
+        && indexParamNames.indexOf(node.name) !== -1;
+    }
+
+    function isUsingReactChildren(node) {
+      const callee = node.callee;
+      if (
+        !callee
+        || !callee.property
+        || !callee.object
+      ) {
+        return null;
+      }
+
+      const isReactChildMethod = ['map', 'forEach'].indexOf(callee.property.name) > -1;
+      if (!isReactChildMethod) {
+        return null;
+      }
+
+      const obj = callee.object;
+      if (obj && obj.name === 'Children') {
+        return true;
+      }
+      if (obj && obj.object && obj.object.name === pragma.getFromContext(context)) {
+        return true;
+      }
+
+      return false;
+    }
+
+    function getMapIndexParamName(node) {
+      const callee = node.callee;
+      if (callee.type !== 'MemberExpression' && callee.type !== 'OptionalMemberExpression') {
+        return null;
+      }
+      if (callee.property.type !== 'Identifier') {
+        return null;
+      }
+      if (!has(iteratorFunctionsToIndexParamPosition, callee.property.name)) {
+        return null;
+      }
+
+      const name = /** @type {keyof iteratorFunctionsToIndexParamPosition} */ (callee.property.name);
+
+      const callbackArg = isUsingReactChildren(node)
+        ? node.arguments[1]
+        : node.arguments[0];
+
+      if (!callbackArg) {
+        return null;
+      }
+
+      if (!astUtil.isFunctionLikeExpression(callbackArg)) {
+        return null;
+      }
+
+      const params = callbackArg.params;
+
+      const indexParamPosition = iteratorFunctionsToIndexParamPosition[name];
+      if (params.length < indexParamPosition + 1) {
+        return null;
+      }
+
+      return params[indexParamPosition].name;
+    }
+
+    function getIdentifiersFromBinaryExpression(side) {
+      if (side.type === 'Identifier') {
+        return side;
+      }
+
+      if (side.type === 'BinaryExpression') {
+        // recurse
+        const left = getIdentifiersFromBinaryExpression(side.left);
+        const right = getIdentifiersFromBinaryExpression(side.right);
+        return [].concat(left, right).filter(Boolean);
+      }
+
+      return null;
+    }
+
+    function checkPropValue(node) {
+      if (isArrayIndex(node)) {
+        // key={bar}
+        report(context, messages.noArrayIndex, 'noArrayIndex', {
+          node,
+        });
+        return;
+      }
+
+      if (node.type === 'TemplateLiteral') {
+        // key={`foo-${bar}`}
+        node.expressions.filter(isArrayIndex).forEach(() => {
+          report(context, messages.noArrayIndex, 'noArrayIndex', {
+            node,
+          });
+        });
+
+        return;
+      }
+
+      if (node.type === 'BinaryExpression') {
+        // key={'foo' + bar}
+        const identifiers = getIdentifiersFromBinaryExpression(node);
+
+        identifiers.filter(isArrayIndex).forEach(() => {
+          report(context, messages.noArrayIndex, 'noArrayIndex', {
+            node,
+          });
+        });
+
+        return;
+      }
+
+      if (
+        astUtil.isCallExpression(node)
+        && node.callee
+        && node.callee.type === 'MemberExpression'
+        && node.callee.object
+        && isArrayIndex(node.callee.object)
+        && node.callee.property
+        && node.callee.property.type === 'Identifier'
+        && node.callee.property.name === 'toString'
+      ) {
+        // key={bar.toString()}
+        report(context, messages.noArrayIndex, 'noArrayIndex', {
+          node,
+        });
+        return;
+      }
+
+      if (
+        astUtil.isCallExpression(node)
+        && node.callee
+        && node.callee.type === 'Identifier'
+        && node.callee.name === 'String'
+        && Array.isArray(node.arguments)
+        && node.arguments.length > 0
+        && isArrayIndex(node.arguments[0])
+      ) {
+        // key={String(bar)}
+        report(context, messages.noArrayIndex, 'noArrayIndex', {
+          node: node.arguments[0],
+        });
+      }
+    }
+
+    function popIndex(node) {
+      const mapIndexParamName = getMapIndexParamName(node);
+      if (!mapIndexParamName) {
+        return;
+      }
+
+      indexParamNames.pop();
+    }
+
+    return {
+      'CallExpression, OptionalCallExpression'(node) {
+        if (isCreateCloneElement(node.callee, context) && node.arguments.length > 1) {
+          // React.createElement
+          if (!indexParamNames.length) {
+            return;
+          }
+
+          const props = node.arguments[1];
+
+          if (props.type !== 'ObjectExpression') {
+            return;
+          }
+
+          props.properties.forEach((prop) => {
+            if (!prop.key || prop.key.name !== 'key') {
+              // { ...foo }
+              // { foo: bar }
+              return;
+            }
+
+            checkPropValue(prop.value);
+          });
+
+          return;
+        }
+
+        const mapIndexParamName = getMapIndexParamName(node);
+        if (!mapIndexParamName) {
+          return;
+        }
+
+        indexParamNames.push(mapIndexParamName);
+      },
+
+      JSXAttribute(node) {
+        if (node.name.name !== 'key') {
+          // foo={bar}
+          return;
+        }
+
+        if (!indexParamNames.length) {
+          // Not inside a call expression that we think has an index param.
+          return;
+        }
+
+        const value = node.value;
+        if (!value || value.type !== 'JSXExpressionContainer') {
+          // key='foo' or just simply 'key'
+          return;
+        }
+
+        checkPropValue(value.expression);
+      },
+
+      'CallExpression:exit': popIndex,
+      'OptionalCallExpression:exit': popIndex,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-arrow-function-lifecycle.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-arrow-function-lifecycle.d.ts","sourceRoot":"","sources":["no-arrow-function-lifecycle.js"],"names":[],"mappings":"wBAsCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-arrow-function-lifecycle.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,149 @@
+/**
+ * @fileoverview Lifecycle methods should be methods on the prototype, not class fields
+ * @author Tan Nguyen
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const lifecycleMethods = require('../util/lifecycleMethods');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+function getRuleText(node) {
+  const params = node.value.params.map((p) => p.name);
+
+  if (node.type === 'Property') {
+    return `: function(${params.join(', ')}) `;
+  }
+
+  if (node.type === 'ClassProperty' || node.type === 'PropertyDefinition') {
+    return `(${params.join(', ')}) `;
+  }
+
+  return null;
+}
+
+const messages = {
+  lifecycle: '{{propertyName}} is a React lifecycle method, and should not be an arrow function or in a class field. Use an instance method instead.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Lifecycle methods should be methods on the prototype, not class fields',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-arrow-function-lifecycle'),
+    },
+    messages,
+    schema: [],
+    fixable: 'code',
+  },
+
+  create: Components.detect((context, components) => {
+    /**
+     * @param {Array} properties list of component properties
+     */
+    function reportNoArrowFunctionLifecycle(properties) {
+      properties.forEach((node) => {
+        if (!node || !node.value) {
+          return;
+        }
+
+        const propertyName = astUtil.getPropertyName(node);
+        const nodeType = node.value.type;
+        const isLifecycleMethod = (
+          node.static && !componentUtil.isES5Component(node, context)
+            ? lifecycleMethods.static
+            : lifecycleMethods.instance
+        ).indexOf(propertyName) > -1;
+
+        if (nodeType === 'ArrowFunctionExpression' && isLifecycleMethod) {
+          const body = node.value.body;
+          const isBlockBody = body.type === 'BlockStatement';
+          const sourceCode = getSourceCode(context);
+
+          let nextComment = [];
+          let previousComment = [];
+          let bodyRange;
+          if (!isBlockBody) {
+            const previousToken = sourceCode.getTokenBefore(body);
+
+            if (sourceCode.getCommentsBefore) {
+              // eslint >=4.x
+              previousComment = sourceCode.getCommentsBefore(body);
+            } else {
+              // eslint 3.x
+              const potentialComment = sourceCode.getTokenBefore(body, { includeComments: true });
+              previousComment = previousToken === potentialComment ? [] : [potentialComment];
+            }
+
+            if (sourceCode.getCommentsAfter) {
+              // eslint >=4.x
+              nextComment = sourceCode.getCommentsAfter(body);
+            } else {
+              // eslint 3.x
+              const potentialComment = sourceCode.getTokenAfter(body, { includeComments: true });
+              const nextToken = sourceCode.getTokenAfter(body);
+              nextComment = nextToken === potentialComment ? [] : [potentialComment];
+            }
+            bodyRange = [
+              (previousComment.length > 0 ? previousComment[0] : body).range[0],
+              (nextComment.length > 0 ? nextComment[nextComment.length - 1] : body).range[1]
+                + (node.value.body.type === 'ObjectExpression' ? 1 : 0), // to account for a wrapped end paren
+            ];
+          }
+          const headRange = [
+            node.key.range[1],
+            (previousComment.length > 0 ? previousComment[0] : body).range[0],
+          ];
+          const hasSemi = node.value.expression && getText(context, node).slice(node.value.range[1] - node.range[0]) === ';';
+
+          report(
+            context,
+            messages.lifecycle,
+            'lifecycle',
+            {
+              node,
+              data: {
+                propertyName,
+              },
+              fix(fixer) {
+                if (!sourceCode.getCommentsAfter) {
+                  // eslint 3.x
+                  return isBlockBody && fixer.replaceTextRange(headRange, getRuleText(node));
+                }
+                return [].concat(
+                  fixer.replaceTextRange(headRange, getRuleText(node)),
+                  isBlockBody ? [] : fixer.replaceTextRange(
+                    [bodyRange[0], bodyRange[1] + (hasSemi ? 1 : 0)],
+                    `{ return ${previousComment.map((x) => getText(context, x)).join('')}${getText(context, body)}${nextComment.map((x) => getText(context, x)).join('')}; }`
+                  )
+                );
+              },
+            }
+          );
+        }
+      });
+    }
+
+    return {
+      'Program:exit'() {
+        values(components.list()).forEach((component) => {
+          const properties = astUtil.getComponentProperties(component.node);
+          reportNoArrowFunctionLifecycle(properties);
+        });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-children-prop.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-children-prop.d.ts","sourceRoot":"","sources":["no-children-prop.js"],"names":[],"mappings":"wBAuCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-children-prop.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,125 @@
+/**
+ * @fileoverview Prevent passing of children as props
+ * @author Benjamin Stepp
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * Checks if the node is a createElement call with a props literal.
+ * @param {ASTNode} node - The AST node being checked.
+ * @param {Context} context - The AST node being checked.
+ * @returns {boolean} - True if node is a createElement call with a props
+ * object literal, False if not.
+*/
+function isCreateElementWithProps(node, context) {
+  return isCreateElement(context, node)
+    && node.arguments.length > 1
+    && node.arguments[1].type === 'ObjectExpression';
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  nestChildren: 'Do not pass children as props. Instead, nest children between the opening and closing tags.',
+  passChildrenAsArgs: 'Do not pass children as props. Instead, pass them as additional arguments to React.createElement.',
+  nestFunction: 'Do not nest a function between the opening and closing tags. Instead, pass it as a prop.',
+  passFunctionAsArgs: 'Do not pass a function as an additional argument to React.createElement. Instead, pass it as a prop.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow passing of children as props',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('no-children-prop'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowFunctions: {
+          type: 'boolean',
+          default: false,
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+  create(context) {
+    const configuration = context.options[0] || {};
+
+    function isFunction(node) {
+      return configuration.allowFunctions && (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression');
+    }
+
+    return {
+      JSXAttribute(node) {
+        if (node.name.name !== 'children') {
+          return;
+        }
+
+        const value = node.value;
+        if (value && value.type === 'JSXExpressionContainer' && isFunction(value.expression)) {
+          return;
+        }
+
+        report(context, messages.nestChildren, 'nestChildren', {
+          node,
+        });
+      },
+      CallExpression(node) {
+        if (!isCreateElementWithProps(node, context)) {
+          return;
+        }
+
+        const props = 'properties' in node.arguments[1] ? node.arguments[1].properties : undefined;
+        const childrenProp = props.find((prop) => (
+          'key' in prop
+          && prop.key
+          && 'name' in prop.key
+          && prop.key.name === 'children'
+        ));
+
+        if (childrenProp) {
+          if ('value' in childrenProp && childrenProp.value && !isFunction(childrenProp.value)) {
+            report(context, messages.passChildrenAsArgs, 'passChildrenAsArgs', {
+              node,
+            });
+          }
+        } else if (node.arguments.length === 3) {
+          const children = node.arguments[2];
+          if (isFunction(children)) {
+            report(context, messages.passFunctionAsArgs, 'passFunctionAsArgs', {
+              node,
+            });
+          }
+        }
+      },
+      JSXElement(node) {
+        const children = node.children;
+        if (children && children.length === 1 && children[0].type === 'JSXExpressionContainer') {
+          if (isFunction(children[0].expression)) {
+            report(context, messages.nestFunction, 'nestFunction', {
+              node,
+            });
+          }
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-danger-with-children.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-danger-with-children.d.ts","sourceRoot":"","sources":["no-danger-with-children.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-danger-with-children.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,157 @@
+/**
+ * @fileoverview Report when a DOM element is using both children and dangerouslySetInnerHTML
+ * @author David Petersen
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const jsxUtil = require('../util/jsx');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+const messages = {
+  dangerWithChildren: 'Only set one of `children` or `props.dangerouslySetInnerHTML`',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow when a DOM element is using both children and dangerouslySetInnerHTML',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('no-danger-with-children'),
+    },
+
+    messages,
+
+    schema: [], // no options
+  },
+  create(context) {
+    function findSpreadVariable(node, name) {
+      return variableUtil.getVariableFromContext(context, node, name);
+    }
+    /**
+     * Takes a ObjectExpression and returns the value of the prop if it has it
+     * @param {object} node - ObjectExpression node
+     * @param {string} propName - name of the prop to look for
+     * @param {string[]} seenProps
+     * @returns {object | boolean}
+     */
+    function findObjectProp(node, propName, seenProps) {
+      if (!node.properties) {
+        return false;
+      }
+      return node.properties.find((prop) => {
+        if (prop.type === 'Property') {
+          return prop.key.name === propName;
+        }
+        if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') {
+          const variable = findSpreadVariable(node, prop.argument.name);
+          if (variable && variable.defs.length && variable.defs[0].node.init) {
+            if (seenProps.indexOf(prop.argument.name) > -1) {
+              return false;
+            }
+            const newSeenProps = seenProps.concat(prop.argument.name || []);
+            return findObjectProp(variable.defs[0].node.init, propName, newSeenProps);
+          }
+        }
+        return false;
+      });
+    }
+
+    /**
+     * Takes a JSXElement and returns the value of the prop if it has it
+     * @param {object} node - JSXElement node
+     * @param {string} propName - name of the prop to look for
+     * @returns {object | boolean}
+     */
+    function findJsxProp(node, propName) {
+      const attributes = node.openingElement.attributes;
+      return attributes.find((attribute) => {
+        if (attribute.type === 'JSXSpreadAttribute') {
+          const variable = findSpreadVariable(node, attribute.argument.name);
+          if (variable && variable.defs.length && variable.defs[0].node.init) {
+            return findObjectProp(variable.defs[0].node.init, propName, []);
+          }
+        }
+        return attribute.name && attribute.name.name === propName;
+      });
+    }
+
+    /**
+     * Checks to see if a node is a line break
+     * @param {ASTNode} node The AST node being checked
+     * @returns {boolean} True if node is a line break, false if not
+     */
+    function isLineBreak(node) {
+      const isLiteral = node.type === 'Literal' || node.type === 'JSXText';
+      const isMultiline = node.loc.start.line !== node.loc.end.line;
+      const isWhiteSpaces = jsxUtil.isWhiteSpaces(node.value);
+
+      return isLiteral && isMultiline && isWhiteSpaces;
+    }
+
+    return {
+      JSXElement(node) {
+        let hasChildren = false;
+
+        if (node.children.length && !isLineBreak(node.children[0])) {
+          hasChildren = true;
+        } else if (findJsxProp(node, 'children')) {
+          hasChildren = true;
+        }
+
+        if (
+          node.openingElement.attributes
+          && hasChildren
+          && findJsxProp(node, 'dangerouslySetInnerHTML')
+        ) {
+          report(context, messages.dangerWithChildren, 'dangerWithChildren', {
+            node,
+          });
+        }
+      },
+      CallExpression(node) {
+        if (
+          node.callee
+          && node.callee.type === 'MemberExpression'
+          && 'name' in node.callee.property
+          && node.callee.property.name === 'createElement'
+          && node.arguments.length > 1
+        ) {
+          let hasChildren = false;
+
+          let props = node.arguments[1];
+
+          if (props.type === 'Identifier') {
+            const variable = variableUtil.getVariableFromContext(context, node, props.name);
+            if (variable && variable.defs.length && variable.defs[0].node.init) {
+              props = variable.defs[0].node.init;
+            }
+          }
+
+          const dangerously = findObjectProp(props, 'dangerouslySetInnerHTML', []);
+
+          if (node.arguments.length === 2) {
+            if (findObjectProp(props, 'children', [])) {
+              hasChildren = true;
+            }
+          } else {
+            hasChildren = true;
+          }
+
+          if (dangerously && hasChildren) {
+            report(context, messages.dangerWithChildren, 'dangerWithChildren', {
+              node,
+            });
+          }
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-danger.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-danger.d.ts","sourceRoot":"","sources":["no-danger.js"],"names":[],"mappings":"wBA8CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-danger.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,96 @@
+/**
+ * @fileoverview Prevent usage of dangerous JSX props
+ * @author Scott Andrews
+ */
+
+'use strict';
+
+const has = require('hasown');
+const fromEntries = require('object.fromentries/polyfill')();
+const minimatch = require('minimatch');
+
+const docsUrl = require('../util/docsUrl');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DANGEROUS_PROPERTY_NAMES = [
+  'dangerouslySetInnerHTML',
+];
+
+const DANGEROUS_PROPERTIES = fromEntries(DANGEROUS_PROPERTY_NAMES.map((prop) => [prop, prop]));
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * Checks if a JSX attribute is dangerous.
+ * @param {string} name - Name of the attribute to check.
+ * @returns {boolean} Whether or not the attribute is dangerous.
+ */
+function isDangerous(name) {
+  return has(DANGEROUS_PROPERTIES, name);
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  dangerousProp: 'Dangerous property \'{{name}}\' found',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of dangerous JSX properties',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-danger'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        customComponentNames: {
+          items: {
+            type: 'string',
+          },
+          minItems: 0,
+          type: 'array',
+          uniqueItems: true,
+        },
+      },
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const customComponentNames = configuration.customComponentNames || [];
+
+    return {
+      JSXAttribute(node) {
+        const nodeName = node.parent.name;
+        const functionName = nodeName.name || `${nodeName.object.name}.${nodeName.property.name}`;
+
+        const enableCheckingCustomComponent = customComponentNames.some((name) => minimatch(functionName, name));
+
+        if ((enableCheckingCustomComponent || jsxUtil.isDOMComponent(node.parent)) && isDangerous(node.name.name)) {
+          report(context, messages.dangerousProp, 'dangerousProp', {
+            node,
+            data: {
+              name: node.name.name,
+            },
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-deprecated.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-deprecated.d.ts","sourceRoot":"","sources":["no-deprecated.js"],"names":[],"mappings":"wBAqHW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-deprecated.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,271 @@
+/**
+ * @fileoverview Prevent usage of deprecated methods
+ * @author Yannick Croissant
+ * @author Scott Feeney
+ * @author Sergei Startsev
+ */
+
+'use strict';
+
+const entries = require('object.entries');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const pragmaUtil = require('../util/pragma');
+const testReactVersion = require('../util/version').testReactVersion;
+const report = require('../util/report');
+const getText = require('../util/eslint').getText;
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const MODULES = {
+  react: ['React'],
+  'react-addons-perf': ['ReactPerf', 'Perf'],
+  'react-dom': ['ReactDOM'],
+  'react-dom/server': ['ReactDOMServer'],
+};
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function getDeprecated(pragma) {
+  const deprecated = {};
+  // 0.12.0
+  deprecated[`${pragma}.renderComponent`] = ['0.12.0', `${pragma}.render`];
+  deprecated[`${pragma}.renderComponentToString`] = ['0.12.0', `${pragma}.renderToString`];
+  deprecated[`${pragma}.renderComponentToStaticMarkup`] = ['0.12.0', `${pragma}.renderToStaticMarkup`];
+  deprecated[`${pragma}.isValidComponent`] = ['0.12.0', `${pragma}.isValidElement`];
+  deprecated[`${pragma}.PropTypes.component`] = ['0.12.0', `${pragma}.PropTypes.element`];
+  deprecated[`${pragma}.PropTypes.renderable`] = ['0.12.0', `${pragma}.PropTypes.node`];
+  deprecated[`${pragma}.isValidClass`] = ['0.12.0'];
+  deprecated['this.transferPropsTo'] = ['0.12.0', 'spread operator ({...})'];
+  // 0.13.0
+  deprecated[`${pragma}.addons.classSet`] = ['0.13.0', 'the npm module classnames'];
+  deprecated[`${pragma}.addons.cloneWithProps`] = ['0.13.0', `${pragma}.cloneElement`];
+  // 0.14.0
+  deprecated[`${pragma}.render`] = ['0.14.0', 'ReactDOM.render'];
+  deprecated[`${pragma}.unmountComponentAtNode`] = ['0.14.0', 'ReactDOM.unmountComponentAtNode'];
+  deprecated[`${pragma}.findDOMNode`] = ['0.14.0', 'ReactDOM.findDOMNode'];
+  deprecated[`${pragma}.renderToString`] = ['0.14.0', 'ReactDOMServer.renderToString'];
+  deprecated[`${pragma}.renderToStaticMarkup`] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup'];
+  // 15.0.0
+  deprecated[`${pragma}.addons.LinkedStateMixin`] = ['15.0.0'];
+  deprecated['ReactPerf.printDOM'] = ['15.0.0', 'ReactPerf.printOperations'];
+  deprecated['Perf.printDOM'] = ['15.0.0', 'Perf.printOperations'];
+  deprecated['ReactPerf.getMeasurementsSummaryMap'] = ['15.0.0', 'ReactPerf.getWasted'];
+  deprecated['Perf.getMeasurementsSummaryMap'] = ['15.0.0', 'Perf.getWasted'];
+  // 15.5.0
+  deprecated[`${pragma}.createClass`] = ['15.5.0', 'the npm module create-react-class'];
+  deprecated[`${pragma}.addons.TestUtils`] = ['15.5.0', 'ReactDOM.TestUtils'];
+  deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types'];
+  // 15.6.0
+  deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories'];
+  // 16.9.0
+  // For now the following life-cycle methods are just legacy, not deprecated:
+  // `componentWillMount`, `componentWillReceiveProps`, `componentWillUpdate`
+  // https://github.com/yannickcr/eslint-plugin-react/pull/1750#issuecomment-425975934
+  deprecated.componentWillMount = [
+    '16.9.0',
+    'UNSAFE_componentWillMount',
+    'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount. '
+    + 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
+  ];
+  deprecated.componentWillReceiveProps = [
+    '16.9.0',
+    'UNSAFE_componentWillReceiveProps',
+    'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops. '
+    + 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
+  ];
+  deprecated.componentWillUpdate = [
+    '16.9.0',
+    'UNSAFE_componentWillUpdate',
+    'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate. '
+    + 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
+  ];
+  // 18.0.0
+  // https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#deprecations
+  deprecated['ReactDOM.render'] = [
+    '18.0.0',
+    'createRoot',
+    'https://reactjs.org/link/switch-to-createroot',
+  ];
+  deprecated['ReactDOM.hydrate'] = [
+    '18.0.0',
+    'hydrateRoot',
+    'https://reactjs.org/link/switch-to-createroot',
+  ];
+  deprecated['ReactDOM.unmountComponentAtNode'] = [
+    '18.0.0',
+    'root.unmount',
+    'https://reactjs.org/link/switch-to-createroot',
+  ];
+  deprecated['ReactDOMServer.renderToNodeStream'] = [
+    '18.0.0',
+    'renderToPipeableStream',
+    'https://reactjs.org/docs/react-dom-server.html#rendertonodestream',
+  ];
+
+  return deprecated;
+}
+
+const messages = {
+  deprecated: '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}{{refs}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of deprecated methods',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('no-deprecated'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    const pragma = pragmaUtil.getFromContext(context);
+    const deprecated = getDeprecated(pragma);
+
+    function isDeprecated(method) {
+      return (
+        deprecated
+        && deprecated[method]
+        && deprecated[method][0]
+        && testReactVersion(context, `>= ${deprecated[method][0]}`)
+      );
+    }
+
+    function checkDeprecation(node, methodName, methodNode) {
+      if (!isDeprecated(methodName)) {
+        return;
+      }
+      const version = deprecated[methodName][0];
+      const newMethod = deprecated[methodName][1];
+      const refs = deprecated[methodName][2];
+      report(context, messages.deprecated, 'deprecated', {
+        node: methodNode || node,
+        data: {
+          oldMethod: methodName,
+          version,
+          newMethod: newMethod ? `, use ${newMethod} instead` : '',
+          refs: refs ? `, see ${refs}` : '',
+        },
+      });
+    }
+
+    function getReactModuleName(node) {
+      let moduleName = false;
+      if (!node.init) {
+        return false;
+      }
+
+      entries(MODULES).some((entry) => {
+        const key = entry[0];
+        const moduleNames = entry[1];
+        if (
+          node.init.arguments
+          && node.init.arguments.length > 0
+          && node.init.arguments[0]
+          && key === node.init.arguments[0].value
+        ) {
+          moduleName = MODULES[key][0];
+        } else {
+          moduleName = moduleNames.find((name) => name === node.init.name);
+        }
+        return moduleName;
+      });
+
+      return moduleName;
+    }
+
+    /**
+     * Returns life cycle methods if available
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {Array} The array of methods.
+     */
+    function getLifeCycleMethods(node) {
+      const properties = astUtil.getComponentProperties(node);
+      return properties.map((property) => ({
+        name: astUtil.getPropertyName(property),
+        node: astUtil.getPropertyNameNode(property),
+      }));
+    }
+
+    /**
+     * Checks life cycle methods
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function checkLifeCycleMethods(node) {
+      if (
+        componentUtil.isES5Component(node, context)
+     || componentUtil.isES6Component(node, context)
+      ) {
+        const methods = getLifeCycleMethods(node);
+        methods.forEach((method) => checkDeprecation(node, method.name, method.node));
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      MemberExpression(node) {
+        checkDeprecation(node, getText(context, node));
+      },
+
+      ImportDeclaration(node) {
+        const isReactImport = typeof MODULES[node.source.value] !== 'undefined';
+        if (!isReactImport) {
+          return;
+        }
+        node.specifiers.filter(((s) => 'imported' in s && s.imported)).forEach((specifier) => {
+          // TODO, semver-major: remove `in` check as part of jsdoc->tsdoc migration
+          checkDeprecation(node, 'imported' in specifier && 'name' in specifier.imported && `${MODULES[node.source.value][0]}.${specifier.imported.name}`, specifier);
+        });
+      },
+
+      VariableDeclarator(node) {
+        const reactModuleName = getReactModuleName(node);
+        const isRequire = node.init
+          && 'callee' in node.init
+          && node.init.callee
+          && 'name' in node.init.callee
+          && node.init.callee.name === 'require';
+        const isReactRequire = node.init
+          && 'arguments' in node.init
+          && node.init.arguments
+          && node.init.arguments.length
+          && typeof MODULES['value' in node.init.arguments[0] ? node.init.arguments[0].value : undefined] !== 'undefined';
+        const isDestructuring = node.id && node.id.type === 'ObjectPattern';
+
+        if (
+          !(isDestructuring && reactModuleName)
+          && !(isDestructuring && isRequire && isReactRequire)
+        ) {
+          return;
+        }
+
+        ('properties' in node.id ? node.id.properties : undefined).filter((p) => p.type !== 'RestElement' && p.key).forEach((property) => {
+          checkDeprecation(
+            node,
+            'key' in property && 'name' in property.key && `${reactModuleName || pragma}.${property.key.name}`,
+            property
+          );
+        });
+      },
+
+      ClassDeclaration: checkLifeCycleMethods,
+      ClassExpression: checkLifeCycleMethods,
+      ObjectExpression: checkLifeCycleMethods,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-did-mount-set-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-did-mount-set-state.d.ts","sourceRoot":"","sources":["no-did-mount-set-state.js"],"names":[],"mappings":"wBASW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,11 @@
+/**
+ * @fileoverview Prevent usage of setState in componentDidMount
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule');
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = makeNoMethodSetStateRule('componentDidMount');
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-did-update-set-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-did-update-set-state.d.ts","sourceRoot":"","sources":["no-did-update-set-state.js"],"names":[],"mappings":"wBASW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,11 @@
+/**
+ * @fileoverview Prevent usage of setState in componentDidUpdate
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule');
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = makeNoMethodSetStateRule('componentDidUpdate');
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-direct-mutation-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-direct-mutation-state.d.ts","sourceRoot":"","sources":["no-direct-mutation-state.js"],"names":[],"mappings":"wBAuBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,155 @@
+/**
+ * @fileoverview Prevent direct mutation of this.state
+ * @author David Petersen
+ * @author Nicolas Fernandez <@burabure>
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noDirectMutation: 'Do not mutate state directly. Use setState().',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow direct mutation of this.state',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('no-direct-mutation-state'),
+    },
+
+    messages,
+  },
+
+  create: Components.detect((context, components, utils) => {
+    /**
+     * Checks if the component is valid
+     * @param {Object} component The component to process
+     * @returns {boolean} True if the component is valid, false if not.
+     */
+    function isValid(component) {
+      return !!component && !component.mutateSetState;
+    }
+
+    /**
+     * Reports undeclared proptypes for a given component
+     * @param {Object} component The component to process
+     */
+    function reportMutations(component) {
+      let mutation;
+      for (let i = 0, j = component.mutations.length; i < j; i++) {
+        mutation = component.mutations[i];
+        report(context, messages.noDirectMutation, 'noDirectMutation', {
+          node: mutation,
+        });
+      }
+    }
+
+    /**
+     * Walks through the MemberExpression to the top-most property.
+     * @param {Object} node The node to process
+     * @returns {Object} The outer-most MemberExpression
+     */
+    function getOuterMemberExpression(node) {
+      while (node.object && node.object.property) {
+        node = node.object;
+      }
+      return node;
+    }
+
+    /**
+     * Determine if we should currently ignore assignments in this component.
+     * @param {?Object} component The component to process
+     * @returns {boolean} True if we should skip assignment checks.
+     */
+    function shouldIgnoreComponent(component) {
+      return !component || (component.inConstructor && !component.inCallExpression);
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+    return {
+      MethodDefinition(node) {
+        if (node.kind === 'constructor') {
+          components.set(node, {
+            inConstructor: true,
+          });
+        }
+      },
+
+      CallExpression(node) {
+        components.set(node, {
+          inCallExpression: true,
+        });
+      },
+
+      AssignmentExpression(node) {
+        const component = components.get(utils.getParentComponent(node));
+        if (shouldIgnoreComponent(component) || !node.left || !node.left.object) {
+          return;
+        }
+        const item = getOuterMemberExpression(node.left);
+        if (componentUtil.isStateMemberExpression(item)) {
+          const mutations = (component && component.mutations) || [];
+          mutations.push(node.left.object);
+          components.set(node, {
+            mutateSetState: true,
+            mutations,
+          });
+        }
+      },
+
+      UpdateExpression(node) {
+        const component = components.get(utils.getParentComponent(node));
+        if (shouldIgnoreComponent(component) || node.argument.type !== 'MemberExpression') {
+          return;
+        }
+        const item = getOuterMemberExpression(node.argument);
+        if (componentUtil.isStateMemberExpression(item)) {
+          const mutations = (component && component.mutations) || [];
+          mutations.push(item);
+          components.set(node, {
+            mutateSetState: true,
+            mutations,
+          });
+        }
+      },
+
+      'CallExpression:exit'(node) {
+        components.set(node, {
+          inCallExpression: false,
+        });
+      },
+
+      'MethodDefinition:exit'(node) {
+        if (node.kind === 'constructor') {
+          components.set(node, {
+            inConstructor: false,
+          });
+        }
+      },
+
+      'Program:exit'() {
+        values(components.list())
+          .filter((component) => !isValid(component))
+          .forEach((component) => {
+            reportMutations(component);
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-find-dom-node.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-find-dom-node.d.ts","sourceRoot":"","sources":["no-find-dom-node.js"],"names":[],"mappings":"wBAkBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-find-dom-node.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,56 @@
+/**
+ * @fileoverview Prevent usage of findDOMNode
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noFindDOMNode: 'Do not use findDOMNode. It doesn’t work with function components and is deprecated in StrictMode. See https://reactjs.org/docs/react-dom.html#finddomnode',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of findDOMNode',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('no-find-dom-node'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    return {
+      CallExpression(node) {
+        const callee = node.callee;
+
+        const isFindDOMNode = ('name' in callee && callee.name === 'findDOMNode') || (
+          'property' in callee
+          && callee.property
+          && 'name' in callee.property
+          && callee.property.name === 'findDOMNode'
+        );
+
+        if (!isFindDOMNode) {
+          return;
+        }
+
+        report(context, messages.noFindDOMNode, 'noFindDOMNode', {
+          node: callee,
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-invalid-html-attribute.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-invalid-html-attribute.d.ts","sourceRoot":"","sources":["no-invalid-html-attribute.js"],"names":[],"mappings":"wBA+kBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-invalid-html-attribute.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,654 @@
+/**
+ * @fileoverview Check if tag attributes to have non-valid value
+ * @author Sebastian Malton
+ */
+
+'use strict';
+
+const matchAll = require('string.prototype.matchall');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const rel = new Map([
+  ['alternate', new Set(['link', 'area', 'a'])],
+  ['apple-touch-icon', new Set(['link'])],
+  ['apple-touch-startup-image', new Set(['link'])],
+  ['author', new Set(['link', 'area', 'a'])],
+  ['bookmark', new Set(['area', 'a'])],
+  ['canonical', new Set(['link'])],
+  ['dns-prefetch', new Set(['link'])],
+  ['external', new Set(['area', 'a', 'form'])],
+  ['help', new Set(['link', 'area', 'a', 'form'])],
+  ['icon', new Set(['link'])],
+  ['license', new Set(['link', 'area', 'a', 'form'])],
+  ['manifest', new Set(['link'])],
+  ['mask-icon', new Set(['link'])],
+  ['modulepreload', new Set(['link'])],
+  ['next', new Set(['link', 'area', 'a', 'form'])],
+  ['nofollow', new Set(['area', 'a', 'form'])],
+  ['noopener', new Set(['area', 'a', 'form'])],
+  ['noreferrer', new Set(['area', 'a', 'form'])],
+  ['opener', new Set(['area', 'a', 'form'])],
+  ['pingback', new Set(['link'])],
+  ['preconnect', new Set(['link'])],
+  ['prefetch', new Set(['link'])],
+  ['preload', new Set(['link'])],
+  ['prerender', new Set(['link'])],
+  ['prev', new Set(['link', 'area', 'a', 'form'])],
+  ['search', new Set(['link', 'area', 'a', 'form'])],
+  ['shortcut', new Set(['link'])], // generally allowed but needs pair with "icon"
+  ['shortcut\u0020icon', new Set(['link'])],
+  ['stylesheet', new Set(['link'])],
+  ['tag', new Set(['area', 'a'])],
+]);
+
+const pairs = new Map([
+  ['shortcut', new Set(['icon'])],
+]);
+
+/**
+ * Map between attributes and a mapping between valid values and a set of tags they are valid on
+ * @type {Map<string, Map<string, Set<string>>>}
+ */
+const VALID_VALUES = new Map([
+  ['rel', rel],
+]);
+
+/**
+ * Map between attributes and a mapping between pair-values and a set of values they are valid with
+ * @type {Map<string, Map<string, Set<string>>>}
+ */
+const VALID_PAIR_VALUES = new Map([
+  ['rel', pairs],
+]);
+
+/**
+ * The set of all possible HTML elements. Used for skipping custom types
+ * @type {Set<string>}
+ */
+const HTML_ELEMENTS = new Set([
+  'a',
+  'abbr',
+  'acronym',
+  'address',
+  'applet',
+  'area',
+  'article',
+  'aside',
+  'audio',
+  'b',
+  'base',
+  'basefont',
+  'bdi',
+  'bdo',
+  'bgsound',
+  'big',
+  'blink',
+  'blockquote',
+  'body',
+  'br',
+  'button',
+  'canvas',
+  'caption',
+  'center',
+  'cite',
+  'code',
+  'col',
+  'colgroup',
+  'content',
+  'data',
+  'datalist',
+  'dd',
+  'del',
+  'details',
+  'dfn',
+  'dialog',
+  'dir',
+  'div',
+  'dl',
+  'dt',
+  'em',
+  'embed',
+  'fieldset',
+  'figcaption',
+  'figure',
+  'font',
+  'footer',
+  'form',
+  'frame',
+  'frameset',
+  'h1',
+  'h2',
+  'h3',
+  'h4',
+  'h5',
+  'h6',
+  'head',
+  'header',
+  'hgroup',
+  'hr',
+  'html',
+  'i',
+  'iframe',
+  'image',
+  'img',
+  'input',
+  'ins',
+  'kbd',
+  'keygen',
+  'label',
+  'legend',
+  'li',
+  'link',
+  'main',
+  'map',
+  'mark',
+  'marquee',
+  'math',
+  'menu',
+  'menuitem',
+  'meta',
+  'meter',
+  'nav',
+  'nobr',
+  'noembed',
+  'noframes',
+  'noscript',
+  'object',
+  'ol',
+  'optgroup',
+  'option',
+  'output',
+  'p',
+  'param',
+  'picture',
+  'plaintext',
+  'portal',
+  'pre',
+  'progress',
+  'q',
+  'rb',
+  'rp',
+  'rt',
+  'rtc',
+  'ruby',
+  's',
+  'samp',
+  'script',
+  'section',
+  'select',
+  'shadow',
+  'slot',
+  'small',
+  'source',
+  'spacer',
+  'span',
+  'strike',
+  'strong',
+  'style',
+  'sub',
+  'summary',
+  'sup',
+  'svg',
+  'table',
+  'tbody',
+  'td',
+  'template',
+  'textarea',
+  'tfoot',
+  'th',
+  'thead',
+  'time',
+  'title',
+  'tr',
+  'track',
+  'tt',
+  'u',
+  'ul',
+  'var',
+  'video',
+  'wbr',
+  'xmp',
+]);
+
+/**
+* Map between attributes and set of tags that the attribute is valid on
+* @type {Map<string, Set<string>>}
+*/
+const COMPONENT_ATTRIBUTE_MAP = new Map([
+  ['rel', new Set(['link', 'a', 'area', 'form'])],
+]);
+
+/* eslint-disable eslint-plugin/no-unused-message-ids -- false positives, these messageIds are used */
+const messages = {
+  emptyIsMeaningless: 'An empty “{{attributeName}}” attribute is meaningless.',
+  neverValid: '“{{reportingValue}}” is never a valid “{{attributeName}}” attribute value.',
+  noEmpty: 'An empty “{{attributeName}}” attribute is meaningless.',
+  noMethod: 'The ”{{attributeName}}“ attribute cannot be a method.',
+  notAlone: '“{{reportingValue}}” must be directly followed by “{{missingValue}}”.',
+  notPaired: '“{{reportingValue}}” can not be directly followed by “{{secondValue}}” without “{{missingValue}}”.',
+  notValidFor: '“{{reportingValue}}” is not a valid “{{attributeName}}” attribute value for <{{elementName}}>.',
+  onlyMeaningfulFor: 'The ”{{attributeName}}“ attribute only has meaning on the tags: {{tagNames}}',
+  onlyStrings: '“{{attributeName}}” attribute only supports strings.',
+  spaceDelimited: '”{{attributeName}}“ attribute values should be space delimited.',
+  suggestRemoveDefault: '"remove {{attributeName}}"',
+  suggestRemoveEmpty: '"remove empty attribute {{attributeName}}"',
+  suggestRemoveInvalid: '“remove invalid attribute {{reportingValue}}”',
+  suggestRemoveWhitespaces: 'remove whitespaces in “{{attributeName}}”',
+  suggestRemoveNonString: 'remove non-string value in “{{attributeName}}”',
+};
+
+function splitIntoRangedParts(node, regex) {
+  const valueRangeStart = node.range[0] + 1; // the plus one is for the initial quote
+
+  return Array.from(matchAll(node.value, regex), (match) => {
+    const start = match.index + valueRangeStart;
+    const end = start + match[0].length;
+
+    return {
+      reportingValue: `${match[1]}`,
+      value: match[1],
+      range: [start, end],
+    };
+  });
+}
+
+function checkLiteralValueNode(context, attributeName, node, parentNode, parentNodeName) {
+  if (typeof node.value !== 'string') {
+    const data = { attributeName, reportingValue: node.value };
+
+    report(context, messages.onlyStrings, 'onlyStrings', {
+      node,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveNonString',
+        data,
+        fix(fixer) { return fixer.remove(parentNode); },
+      }],
+    });
+    return;
+  }
+
+  if (!node.value.trim()) {
+    const data = { attributeName, reportingValue: node.value };
+
+    report(context, messages.noEmpty, 'noEmpty', {
+      node,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveEmpty',
+        data,
+        fix(fixer) { return fixer.remove(node.parent); },
+      }],
+    });
+    return;
+  }
+
+  const singleAttributeParts = splitIntoRangedParts(node, /(\S+)/g);
+  singleAttributeParts.forEach((singlePart) => {
+    const allowedTags = VALID_VALUES.get(attributeName).get(singlePart.value);
+    const reportingValue = singlePart.reportingValue;
+
+    if (!allowedTags) {
+      const data = {
+        attributeName,
+        reportingValue,
+      };
+
+      const suggest = [{
+        messageId: 'suggestRemoveInvalid',
+        data,
+        fix(fixer) { return fixer.removeRange(singlePart.range); },
+      }];
+
+      report(context, messages.neverValid, 'neverValid', {
+        node,
+        data,
+        suggest,
+      });
+    } else if (!allowedTags.has(parentNodeName)) {
+      const data = {
+        attributeName,
+        reportingValue,
+        elementName: parentNodeName,
+      };
+
+      const suggest = [{
+        messageId: 'suggestRemoveInvalid',
+        data,
+        fix(fixer) { return fixer.removeRange(singlePart.range); },
+      }];
+
+      report(context, messages.notValidFor, 'notValidFor', {
+        node,
+        data,
+        suggest,
+      });
+    }
+  });
+
+  const allowedPairsForAttribute = VALID_PAIR_VALUES.get(attributeName);
+  if (allowedPairsForAttribute) {
+    const pairAttributeParts = splitIntoRangedParts(node, /(?=(\b\S+\s*\S+))/g);
+    pairAttributeParts.forEach((pairPart) => {
+      allowedPairsForAttribute.forEach((siblings, pairing) => {
+        const attributes = pairPart.reportingValue.split('\u0020');
+        const firstValue = attributes[0];
+        const secondValue = attributes[1];
+        if (firstValue === pairing) {
+          const lastValue = attributes[attributes.length - 1]; // in case of multiple white spaces
+          if (!siblings.has(lastValue)) {
+            const message = secondValue ? messages.notPaired : messages.notAlone;
+            const messageId = secondValue ? 'notPaired' : 'notAlone';
+            report(context, message, messageId, {
+              node,
+              data: {
+                reportingValue: firstValue,
+                secondValue,
+                missingValue: Array.from(siblings).join(', '),
+              },
+              suggest: false,
+            });
+          }
+        }
+      });
+    });
+  }
+
+  const whitespaceParts = splitIntoRangedParts(node, /(\s+)/g);
+  whitespaceParts.forEach((whitespacePart) => {
+    const data = { attributeName };
+
+    if (whitespacePart.range[0] === (node.range[0] + 1) || whitespacePart.range[1] === (node.range[1] - 1)) {
+      report(context, messages.spaceDelimited, 'spaceDelimited', {
+        node,
+        data,
+        suggest: [{
+          messageId: 'suggestRemoveWhitespaces',
+          data,
+          fix(fixer) { return fixer.removeRange(whitespacePart.range); },
+        }],
+      });
+    } else if (whitespacePart.value !== '\u0020') {
+      report(context, messages.spaceDelimited, 'spaceDelimited', {
+        node,
+        data,
+        suggest: [{
+          messageId: 'suggestRemoveWhitespaces',
+          data,
+          fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); },
+        }],
+      });
+    }
+  });
+}
+
+const DEFAULT_ATTRIBUTES = ['rel'];
+
+function checkAttribute(context, node) {
+  const attribute = node.name.name;
+
+  const parentNodeName = node.parent.name.name;
+  if (!COMPONENT_ATTRIBUTE_MAP.has(attribute) || !COMPONENT_ATTRIBUTE_MAP.get(attribute).has(parentNodeName)) {
+    const tagNames = Array.from(
+      COMPONENT_ATTRIBUTE_MAP.get(attribute).values(),
+      (tagName) => `"<${tagName}>"`
+    ).join(', ');
+    const data = {
+      attributeName: attribute,
+      tagNames,
+    };
+
+    report(context, messages.onlyMeaningfulFor, 'onlyMeaningfulFor', {
+      node: node.name,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveDefault',
+        data,
+        fix(fixer) { return fixer.remove(node); },
+      }],
+    });
+    return;
+  }
+
+  function fix(fixer) { return fixer.remove(node); }
+
+  if (!node.value) {
+    const data = { attributeName: attribute };
+
+    report(context, messages.emptyIsMeaningless, 'emptyIsMeaningless', {
+      node: node.name,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveEmpty',
+        data,
+        fix,
+      }],
+    });
+    return;
+  }
+
+  if (node.value.type === 'Literal') {
+    return checkLiteralValueNode(context, attribute, node.value, node, parentNodeName);
+  }
+
+  if (node.value.expression.type === 'Literal') {
+    return checkLiteralValueNode(context, attribute, node.value.expression, node, parentNodeName);
+  }
+
+  if (node.value.type !== 'JSXExpressionContainer') {
+    return;
+  }
+
+  if (node.value.expression.type === 'ObjectExpression') {
+    const data = { attributeName: attribute };
+
+    report(context, messages.onlyStrings, 'onlyStrings', {
+      node: node.value,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveDefault',
+        data,
+        fix,
+      }],
+    });
+  } else if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') {
+    const data = { attributeName: attribute };
+
+    report(context, messages.onlyStrings, 'onlyStrings', {
+      node: node.value,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveDefault',
+        data,
+        fix,
+      }],
+    });
+  }
+}
+
+function isValidCreateElement(node) {
+  return node.callee
+    && node.callee.type === 'MemberExpression'
+    && node.callee.object.name === 'React'
+    && node.callee.property.name === 'createElement'
+    && node.arguments.length > 0;
+}
+
+function checkPropValidValue(context, node, value, attribute) {
+  const validTags = VALID_VALUES.get(attribute);
+
+  if (value.type !== 'Literal') {
+    return; // cannot check non-literals
+  }
+
+  const validTagSet = validTags.get(value.value);
+  if (!validTagSet) {
+    const data = {
+      attributeName: attribute,
+      reportingValue: value.value,
+    };
+
+    report(context, messages.neverValid, 'neverValid', {
+      node: value,
+      data,
+      suggest: [{
+        messageId: 'suggestRemoveInvalid',
+        data,
+        fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); },
+      }],
+    });
+  } else if (!validTagSet.has(node.arguments[0].value)) {
+    report(context, messages.notValidFor, 'notValidFor', {
+      node: value,
+      data: {
+        attributeName: attribute,
+        reportingValue: value.raw,
+        elementName: node.arguments[0].value,
+      },
+      suggest: false,
+    });
+  }
+}
+
+/**
+ *
+ * @param {*} context
+ * @param {*} node
+ * @param {string} attribute
+ */
+function checkCreateProps(context, node, attribute) {
+  const propsArg = node.arguments[1];
+
+  if (!propsArg || propsArg.type !== 'ObjectExpression') {
+    return; // can't check variables, computed, or shorthands
+  }
+
+  for (const prop of propsArg.properties) {
+    if (!prop.key || prop.key.type !== 'Identifier') {
+      // eslint-disable-next-line no-continue
+      continue; // cannot check computed keys
+    }
+
+    if (prop.key.name !== attribute) {
+      // eslint-disable-next-line no-continue
+      continue; // ignore not this attribute
+    }
+
+    if (!COMPONENT_ATTRIBUTE_MAP.get(attribute).has(node.arguments[0].value)) {
+      const tagNames = Array.from(
+        COMPONENT_ATTRIBUTE_MAP.get(attribute).values(),
+        (tagName) => `"<${tagName}>"`
+      ).join(', ');
+
+      report(context, messages.onlyMeaningfulFor, 'onlyMeaningfulFor', {
+        node: prop.key,
+        data: {
+          attributeName: attribute,
+          tagNames,
+        },
+        suggest: false,
+      });
+
+      // eslint-disable-next-line no-continue
+      continue;
+    }
+
+    if (prop.method) {
+      report(context, messages.noMethod, 'noMethod', {
+        node: prop,
+        data: {
+          attributeName: attribute,
+        },
+        suggest: false,
+      });
+
+      // eslint-disable-next-line no-continue
+      continue;
+    }
+
+    if (prop.shorthand || prop.computed) {
+      // eslint-disable-next-line no-continue
+      continue; // cannot check these
+    }
+
+    if (prop.value.type === 'ArrayExpression') {
+      prop.value.elements.forEach((value) => {
+        checkPropValidValue(context, node, value, attribute);
+      });
+
+      // eslint-disable-next-line no-continue
+      continue;
+    }
+
+    checkPropValidValue(context, node, prop.value, attribute);
+  }
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of invalid attributes',
+      category: 'Possible Errors',
+      url: docsUrl('no-invalid-html-attribute'),
+    },
+    messages,
+    schema: [{
+      type: 'array',
+      uniqueItems: true,
+      items: {
+        enum: ['rel'],
+      },
+    }],
+    type: 'suggestion',
+    hasSuggestions: true, // eslint-disable-line eslint-plugin/require-meta-has-suggestions
+  },
+
+  create(context) {
+    return {
+      JSXAttribute(node) {
+        const attributes = new Set(context.options[0] || DEFAULT_ATTRIBUTES);
+
+        // ignore attributes that aren't configured to be checked
+        if (!attributes.has(node.name.name)) {
+          return;
+        }
+
+        // ignore non-HTML elements
+        if (!HTML_ELEMENTS.has(node.parent.name.name)) {
+          return;
+        }
+
+        checkAttribute(context, node);
+      },
+
+      CallExpression(node) {
+        if (!isValidCreateElement(node)) {
+          return;
+        }
+
+        const elemNameArg = node.arguments[0];
+
+        if (!elemNameArg || elemNameArg.type !== 'Literal') {
+          return; // can only check literals
+        }
+
+        // ignore non-HTML elements
+        if (typeof elemNameArg.value === 'string' && !HTML_ELEMENTS.has(elemNameArg.value)) {
+          return;
+        }
+
+        const attributes = new Set(context.options[0] || DEFAULT_ATTRIBUTES);
+
+        attributes.forEach((attribute) => {
+          checkCreateProps(context, node, attribute);
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-is-mounted.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-is-mounted.d.ts","sourceRoot":"","sources":["no-is-mounted.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview Prevent usage of isMounted
+ * @author Joe Lencioni
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const getAncestors = require('../util/eslint').getAncestors;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noIsMounted: 'Do not use isMounted',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of isMounted',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('no-is-mounted'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    return {
+      CallExpression(node) {
+        const callee = node.callee;
+        if (callee.type !== 'MemberExpression') {
+          return;
+        }
+        if (
+          callee.object.type !== 'ThisExpression'
+          || !('name' in callee.property)
+          || callee.property.name !== 'isMounted'
+        ) {
+          return;
+        }
+        const ancestors = getAncestors(context, node);
+        for (let i = 0, j = ancestors.length; i < j; i++) {
+          if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
+            report(context, messages.noIsMounted, 'noIsMounted', {
+              node: callee,
+            });
+            break;
+          }
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-multi-comp.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-multi-comp.d.ts","sourceRoot":"","sources":["no-multi-comp.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,81 @@
+/**
+ * @fileoverview Prevent multiple component definition per file
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  onlyOneComponent: 'Declare only one React component per file',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow multiple component definition per file',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('no-multi-comp'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignoreStateless: {
+          default: false,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const configuration = context.options[0] || {};
+    const ignoreStateless = configuration.ignoreStateless || false;
+
+    /**
+     * Checks if the component is ignored
+     * @param {Object} component The component being checked.
+     * @returns {boolean} True if the component is ignored, false if not.
+     */
+    function isIgnored(component) {
+      return (
+        ignoreStateless && (
+          /Function/.test(component.node.type)
+          || utils.isPragmaComponentWrapper(component.node)
+        )
+      );
+    }
+
+    return {
+      'Program:exit'() {
+        if (components.length() <= 1) {
+          return;
+        }
+
+        values(components.list())
+          .filter((component) => !isIgnored(component))
+          .slice(1)
+          .forEach((component) => {
+            report(context, messages.onlyOneComponent, 'onlyOneComponent', {
+              node: component.node,
+            });
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-namespace.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-namespace.d.ts","sourceRoot":"","sources":["no-namespace.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-namespace.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,62 @@
+/**
+ * @fileoverview Enforce that namespaces are not used in React elements
+ * @author Yacine Hmito
+ */
+
+'use strict';
+
+const elementType = require('jsx-ast-utils/elementType');
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noNamespace: 'React component {{name}} must not be in a namespace, as React does not support them',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce that namespaces are not used in React elements',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('no-namespace'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    return {
+      CallExpression(node) {
+        if (isCreateElement(context, node) && node.arguments.length > 0 && node.arguments[0].type === 'Literal') {
+          const name = node.arguments[0].value;
+          if (typeof name !== 'string' || name.indexOf(':') === -1) return undefined;
+          report(context, messages.noNamespace, 'noNamespace', {
+            node,
+            data: {
+              name,
+            },
+          });
+        }
+      },
+      JSXOpeningElement(node) {
+        const name = elementType(node);
+        if (typeof name !== 'string' || name.indexOf(':') === -1) return undefined;
+        report(context, messages.noNamespace, 'noNamespace', {
+          node,
+          data: {
+            name,
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-object-type-as-default-prop.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-object-type-as-default-prop.d.ts","sourceRoot":"","sources":["no-object-type-as-default-prop.js"],"names":[],"mappings":"wBAiFW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-object-type-as-default-prop.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,105 @@
+/**
+ * @fileoverview Prevent usage of referential-type variables as default param in functional component
+ * @author Chang Yan
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const report = require('../util/report');
+
+const FORBIDDEN_TYPES_MAP = {
+  ArrowFunctionExpression: 'arrow function',
+  FunctionExpression: 'function expression',
+  ObjectExpression: 'object literal',
+  ArrayExpression: 'array literal',
+  ClassExpression: 'class expression',
+  NewExpression: 'construction expression',
+  JSXElement: 'JSX element',
+};
+
+const FORBIDDEN_TYPES = new Set(Object.keys(FORBIDDEN_TYPES_MAP));
+const MESSAGE_ID = 'forbiddenTypeDefaultParam';
+
+const messages = {
+  [MESSAGE_ID]: '{{propName}} has a/an {{forbiddenType}} as default prop. This could lead to potential infinite render loop in React. Use a variable reference instead of {{forbiddenType}}.',
+};
+function hasUsedObjectDestructuringSyntax(params) {
+  return (
+    params != null
+    && params.length >= 1
+    && params[0].type === 'ObjectPattern'
+  );
+}
+
+function verifyDefaultPropsDestructuring(context, properties) {
+  // Loop through each of the default params
+  properties.filter((prop) => prop.type === 'Property' && prop.value.type === 'AssignmentPattern').forEach((prop) => {
+    const propName = prop.key.name;
+    const propDefaultValue = prop.value;
+
+    const propDefaultValueType = propDefaultValue.right.type;
+
+    if (
+      propDefaultValueType === 'Literal'
+      && propDefaultValue.right.regex != null
+    ) {
+      report(context, messages[MESSAGE_ID], MESSAGE_ID, {
+        node: propDefaultValue,
+        data: {
+          propName,
+          forbiddenType: 'regex literal',
+        },
+      });
+    } else if (
+      astUtil.isCallExpression(propDefaultValue.right)
+      && propDefaultValue.right.callee.type === 'Identifier'
+      && propDefaultValue.right.callee.name === 'Symbol'
+    ) {
+      report(context, messages[MESSAGE_ID], MESSAGE_ID, {
+        node: propDefaultValue,
+        data: {
+          propName,
+          forbiddenType: 'Symbol literal',
+        },
+      });
+    } else if (FORBIDDEN_TYPES.has(propDefaultValueType)) {
+      report(context, messages[MESSAGE_ID], MESSAGE_ID, {
+        node: propDefaultValue,
+        data: {
+          propName,
+          forbiddenType: FORBIDDEN_TYPES_MAP[propDefaultValueType],
+        },
+      });
+    }
+  });
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of referential-type variables as default param in functional component',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-object-type-as-default-prop'),
+    },
+    messages,
+  },
+  create: Components.detect((context, components) => ({
+    'Program:exit'() {
+      const list = components.list();
+      values(list)
+        .filter((component) => hasUsedObjectDestructuringSyntax(component.node.params))
+        .forEach((component) => {
+          const node = component.node;
+          const properties = node.params[0].properties;
+          verifyDefaultPropsDestructuring(context, properties);
+        });
+    },
+  })),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-redundant-should-component-update.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-redundant-should-component-update.d.ts","sourceRoot":"","sources":["no-redundant-should-component-update.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-redundant-should-component-update.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,88 @@
+/**
+ * @fileoverview Flag shouldComponentUpdate when extending PureComponent
+ */
+
+'use strict';
+
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noShouldCompUpdate: '{{component}} does not need shouldComponentUpdate when extending React.PureComponent.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of shouldComponentUpdate when extending React.PureComponent',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('no-redundant-should-component-update'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    /**
+     * Checks for shouldComponentUpdate property
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} Whether or not the property exists.
+     */
+    function hasShouldComponentUpdate(node) {
+      const properties = astUtil.getComponentProperties(node);
+      return properties.some((property) => {
+        const name = astUtil.getPropertyName(property);
+        return name === 'shouldComponentUpdate';
+      });
+    }
+
+    /**
+     * Get name of node if available
+     * @param {ASTNode} node The AST node being checked.
+     * @return {string} The name of the node
+     */
+    function getNodeName(node) {
+      if (node.id) {
+        return node.id.name;
+      }
+      if (node.parent && node.parent.id) {
+        return node.parent.id.name;
+      }
+      return '';
+    }
+
+    /**
+     * Checks for violation of rule
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function checkForViolation(node) {
+      if (componentUtil.isPureComponent(node, context)) {
+        const hasScu = hasShouldComponentUpdate(node);
+        if (hasScu) {
+          const className = getNodeName(node);
+          report(context, messages.noShouldCompUpdate, 'noShouldCompUpdate', {
+            node,
+            data: {
+              component: className,
+            },
+          });
+        }
+      }
+    }
+
+    return {
+      ClassDeclaration: checkForViolation,
+      ClassExpression: checkForViolation,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-render-return-value.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-render-return-value.d.ts","sourceRoot":"","sources":["no-render-return-value.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-render-return-value.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,82 @@
+/**
+ * @fileoverview Prevent usage of the return value of React.render
+ * @author Dustan Kasten
+ */
+
+'use strict';
+
+const testReactVersion = require('../util/version').testReactVersion;
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noReturnValue: 'Do not depend on the return value from {{node}}.render',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of the return value of ReactDOM.render',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('no-render-return-value'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    let calleeObjectName = /^ReactDOM$/;
+    if (testReactVersion(context, '>= 15.0.0')) {
+      calleeObjectName = /^ReactDOM$/;
+    } else if (testReactVersion(context, '^0.14.0')) {
+      calleeObjectName = /^React(DOM)?$/;
+    } else if (testReactVersion(context, '^0.13.0')) {
+      calleeObjectName = /^React$/;
+    }
+
+    return {
+      CallExpression(node) {
+        const callee = node.callee;
+        const parent = node.parent;
+        if (callee.type !== 'MemberExpression') {
+          return;
+        }
+
+        if (
+          callee.object.type !== 'Identifier'
+          || !calleeObjectName.test(callee.object.name)
+          || (!('name' in callee.property) || callee.property.name !== 'render')
+        ) {
+          return;
+        }
+
+        if (
+          parent.type === 'VariableDeclarator'
+          || parent.type === 'Property'
+          || parent.type === 'ReturnStatement'
+          || parent.type === 'ArrowFunctionExpression'
+          || parent.type === 'AssignmentExpression'
+        ) {
+          report(context, messages.noReturnValue, 'noReturnValue', {
+            node: callee,
+            data: {
+              node: callee.object.name,
+            },
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-set-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-set-state.d.ts","sourceRoot":"","sources":["no-set-state.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,88 @@
+/**
+ * @fileoverview Prevent usage of setState
+ * @author Mark Dalgleish
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noSetState: 'Do not use setState',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of setState',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('no-set-state'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    /**
+     * Checks if the component is valid
+     * @param {Object} component The component to process
+     * @returns {boolean} True if the component is valid, false if not.
+     */
+    function isValid(component) {
+      return !!component && !component.useSetState;
+    }
+
+    /**
+     * Reports usages of setState for a given component
+     * @param {Object} component The component to process
+     */
+    function reportSetStateUsages(component) {
+      for (let i = 0, j = component.setStateUsages.length; i < j; i++) {
+        const setStateUsage = component.setStateUsages[i];
+        report(context, messages.noSetState, 'noSetState', {
+          node: setStateUsage,
+        });
+      }
+    }
+
+    return {
+      CallExpression(node) {
+        const callee = node.callee;
+        if (
+          callee.type !== 'MemberExpression'
+          || callee.object.type !== 'ThisExpression'
+          || callee.property.name !== 'setState'
+        ) {
+          return;
+        }
+        const component = components.get(utils.getParentComponent(node));
+        const setStateUsages = (component && component.setStateUsages) || [];
+        setStateUsages.push(callee);
+        components.set(node, {
+          useSetState: true,
+          setStateUsages,
+        });
+      },
+
+      'Program:exit'() {
+        values(components.list())
+          .filter((component) => !isValid(component))
+          .forEach((component) => {
+            reportSetStateUsages(component);
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-string-refs.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-string-refs.d.ts","sourceRoot":"","sources":["no-string-refs.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-string-refs.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,117 @@
+/**
+ * @fileoverview Prevent string definitions for references and prevent referencing this.refs
+ * @author Tom Hastjarjanto
+ */
+
+'use strict';
+
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const testReactVersion = require('../util/version').testReactVersion;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  thisRefsDeprecated: 'Using this.refs is deprecated.',
+  stringInRefDeprecated: 'Using string literals in ref attributes is deprecated.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow using string references',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('no-string-refs'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        noTemplateLiterals: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const checkRefsUsage = testReactVersion(context, '< 18.3.0'); // `this.refs` is writable in React 18.3.0 and later, see https://github.com/facebook/react/pull/28867
+    const detectTemplateLiterals = context.options[0] ? context.options[0].noTemplateLiterals : false;
+    /**
+     * Checks if we are using refs
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if we are using refs, false if not.
+     */
+    function isRefsUsage(node) {
+      return !!(
+        (componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node))
+        && node.object.type === 'ThisExpression'
+        && node.property.name === 'refs'
+      );
+    }
+
+    /**
+     * Checks if we are using a ref attribute
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if we are using a ref attribute, false if not.
+     */
+    function isRefAttribute(node) {
+      return node.type === 'JSXAttribute'
+        && !!node.name
+        && node.name.name === 'ref';
+    }
+
+    /**
+     * Checks if a node contains a string value
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if the node contains a string value, false if not.
+     */
+    function containsStringLiteral(node) {
+      return !!node.value
+        && node.value.type === 'Literal'
+        && typeof node.value.value === 'string';
+    }
+
+    /**
+     * Checks if a node contains a string value within a jsx expression
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if the node contains a string value within a jsx expression, false if not.
+     */
+    function containsStringExpressionContainer(node) {
+      return !!node.value
+        && node.value.type === 'JSXExpressionContainer'
+        && node.value.expression
+        && ((node.value.expression.type === 'Literal' && typeof node.value.expression.value === 'string')
+        || (node.value.expression.type === 'TemplateLiteral' && detectTemplateLiterals));
+    }
+
+    return {
+      MemberExpression(node) {
+        if (checkRefsUsage && isRefsUsage(node)) {
+          report(context, messages.thisRefsDeprecated, 'thisRefsDeprecated', {
+            node,
+          });
+        }
+      },
+
+      JSXAttribute(node) {
+        if (
+          isRefAttribute(node)
+          && (containsStringLiteral(node) || containsStringExpressionContainer(node))
+        ) {
+          report(context, messages.stringInRefDeprecated, 'stringInRefDeprecated', {
+            node,
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-this-in-sfc.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-this-in-sfc.d.ts","sourceRoot":"","sources":["no-this-in-sfc.js"],"names":[],"mappings":"wBAkBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-this-in-sfc.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,47 @@
+/**
+ * @fileoverview Report "this" being used in stateless functional components.
+ */
+
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noThisInSFC: 'Stateless functional components should not use `this`',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow `this` from being used in stateless functional components',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('no-this-in-sfc'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create: Components.detect((context, components, utils) => ({
+    MemberExpression(node) {
+      if (node.object.type === 'ThisExpression') {
+        const component = components.get(utils.getParentStatelessComponent(node));
+        if (!component || (component.node && component.node.parent && component.node.parent.type === 'Property')) {
+          return;
+        }
+        report(context, messages.noThisInSFC, 'noThisInSFC', {
+          node,
+        });
+      }
+    },
+  })),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-typos.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-typos.d.ts","sourceRoot":"","sources":["no-typos.js"],"names":[],"mappings":"wBA+BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,260 @@
+/**
+ * @fileoverview Prevent common casing typos
+ */
+
+'use strict';
+
+const PROP_TYPES = Object.keys(require('prop-types'));
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const report = require('../util/report');
+const lifecycleMethods = require('../util/lifecycleMethods');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const STATIC_CLASS_PROPERTIES = ['propTypes', 'contextTypes', 'childContextTypes', 'defaultProps'];
+
+const messages = {
+  typoPropTypeChain: 'Typo in prop type chain qualifier: {{name}}',
+  typoPropType: 'Typo in declared prop type: {{name}}',
+  typoStaticClassProp: 'Typo in static class property declaration',
+  typoPropDeclaration: 'Typo in property declaration',
+  typoLifecycleMethod: 'Typo in component lifecycle method declaration: {{actual}} should be {{expected}}',
+  staticLifecycleMethod: 'Lifecycle method should be static: {{method}}',
+  noPropTypesBinding: '`\'prop-types\'` imported without a local `PropTypes` binding.',
+  noReactBinding: '`\'react\'` imported without a local `React` binding.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow common typos',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('no-typos'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    let propTypesPackageName = null;
+    let reactPackageName = null;
+
+    function checkValidPropTypeQualifier(node) {
+      if (node.name !== 'isRequired') {
+        report(context, messages.typoPropTypeChain, 'typoPropTypeChain', {
+          node,
+          data: { name: node.name },
+        });
+      }
+    }
+
+    function checkValidPropType(node) {
+      if (node.name && !PROP_TYPES.some((propTypeName) => propTypeName === node.name)) {
+        report(context, messages.typoPropType, 'typoPropType', {
+          node,
+          data: { name: node.name },
+        });
+      }
+    }
+
+    function isPropTypesPackage(node) {
+      return (
+        node.type === 'Identifier'
+        && node.name === propTypesPackageName
+      ) || (
+        node.type === 'MemberExpression'
+        && node.property.name === 'PropTypes'
+        && node.object.name === reactPackageName
+      );
+    }
+
+    /* eslint-disable no-use-before-define */
+
+    function checkValidCallExpression(node) {
+      const callee = node.callee;
+      if (callee.type === 'MemberExpression' && callee.property.name === 'shape') {
+        checkValidPropObject(node.arguments[0]);
+      } else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') {
+        const args = node.arguments[0];
+        if (args && args.type === 'ArrayExpression') {
+          args.elements.forEach((el) => {
+            checkValidProp(el);
+          });
+        }
+      }
+    }
+
+    function checkValidProp(node) {
+      if ((!propTypesPackageName && !reactPackageName) || !node) {
+        return;
+      }
+
+      if (node.type === 'MemberExpression') {
+        if (
+          node.object.type === 'MemberExpression'
+          && isPropTypesPackage(node.object.object)
+        ) { // PropTypes.myProp.isRequired
+          checkValidPropType(node.object.property);
+          checkValidPropTypeQualifier(node.property);
+        } else if (
+          isPropTypesPackage(node.object)
+          && node.property.name !== 'isRequired'
+        ) { // PropTypes.myProp
+          checkValidPropType(node.property);
+        } else if (astUtil.isCallExpression(node.object)) {
+          checkValidPropTypeQualifier(node.property);
+          checkValidCallExpression(node.object);
+        }
+      } else if (astUtil.isCallExpression(node)) {
+        checkValidCallExpression(node);
+      }
+    }
+
+    /* eslint-enable no-use-before-define */
+
+    function checkValidPropObject(node) {
+      if (node && node.type === 'ObjectExpression') {
+        node.properties.forEach((prop) => checkValidProp(prop.value));
+      }
+    }
+
+    function reportErrorIfPropertyCasingTypo(propertyValue, propertyKey, isClassProperty) {
+      const propertyName = propertyKey.name;
+      if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') {
+        checkValidPropObject(propertyValue);
+      }
+      STATIC_CLASS_PROPERTIES.forEach((CLASS_PROP) => {
+        if (propertyName && CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) {
+          const messageId = isClassProperty
+            ? 'typoStaticClassProp'
+            : 'typoPropDeclaration';
+          report(context, messages[messageId], messageId, {
+            node: propertyKey,
+          });
+        }
+      });
+    }
+
+    function reportErrorIfLifecycleMethodCasingTypo(node) {
+      const key = node.key;
+      let nodeKeyName = key.name;
+      if (key.type === 'Literal') {
+        nodeKeyName = key.value;
+      }
+      if (key.type === 'PrivateName' || (node.computed && typeof nodeKeyName !== 'string')) {
+        return;
+      }
+
+      lifecycleMethods.static.forEach((method) => {
+        if (!node.static && nodeKeyName && nodeKeyName.toLowerCase() === method.toLowerCase()) {
+          report(context, messages.staticLifecycleMethod, 'staticLifecycleMethod', {
+            node,
+            data: {
+              method: nodeKeyName,
+            },
+          });
+        }
+      });
+
+      lifecycleMethods.instance.concat(lifecycleMethods.static).forEach((method) => {
+        if (nodeKeyName && method.toLowerCase() === nodeKeyName.toLowerCase() && method !== nodeKeyName) {
+          report(context, messages.typoLifecycleMethod, 'typoLifecycleMethod', {
+            node,
+            data: { actual: nodeKeyName, expected: method },
+          });
+        }
+      });
+    }
+
+    return {
+      ImportDeclaration(node) {
+        if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
+          if (node.specifiers.length > 0) {
+            propTypesPackageName = node.specifiers[0].local.name;
+          } else {
+            report(context, messages.noPropTypesBinding, 'noPropTypesBinding', {
+              node,
+            });
+          }
+        } else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react"
+          if (node.specifiers.length > 0) {
+            reactPackageName = node.specifiers[0].local.name; // guard against accidental anonymous `import "react"`
+          } else {
+            report(context, messages.noReactBinding, 'noReactBinding', {
+              node,
+            });
+          }
+          if (node.specifiers.length >= 1) {
+            const propTypesSpecifier = node.specifiers.find((specifier) => (
+              specifier.imported
+              && specifier.imported.name === 'PropTypes'
+            ));
+            if (propTypesSpecifier) {
+              propTypesPackageName = propTypesSpecifier.local.name;
+            }
+          }
+        }
+      },
+
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!node.static || !componentUtil.isES6Component(node.parent.parent, context)) {
+          return;
+        }
+
+        reportErrorIfPropertyCasingTypo(node.value, node.key, true);
+      },
+
+      MemberExpression(node) {
+        const propertyName = node.property.name;
+
+        if (
+          !propertyName
+          || STATIC_CLASS_PROPERTIES.map((prop) => prop.toLocaleLowerCase()).indexOf(propertyName.toLowerCase()) === -1
+        ) {
+          return;
+        }
+
+        const relatedComponent = utils.getRelatedComponent(node);
+
+        if (
+          relatedComponent
+            && (componentUtil.isES6Component(relatedComponent.node, context) || (
+              relatedComponent.node.type !== 'ClassDeclaration' && utils.isReturningJSX(relatedComponent.node)))
+            && (node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right)
+        ) {
+          reportErrorIfPropertyCasingTypo(node.parent.right, node.property, true);
+        }
+      },
+
+      MethodDefinition(node) {
+        if (!componentUtil.isES6Component(node.parent.parent, context)) {
+          return;
+        }
+
+        reportErrorIfLifecycleMethodCasingTypo(node);
+      },
+
+      ObjectExpression(node) {
+        const component = componentUtil.isES5Component(node, context) && components.get(node);
+
+        if (!component) {
+          return;
+        }
+
+        node.properties.filter((property) => property.type !== 'SpreadElement').forEach((property) => {
+          reportErrorIfPropertyCasingTypo(property.value, property.key, false);
+          reportErrorIfLifecycleMethodCasingTypo(property);
+        });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unescaped-entities.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unescaped-entities.d.ts","sourceRoot":"","sources":["no-unescaped-entities.js"],"names":[],"mappings":"wBAwCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unescaped-entities.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,157 @@
+/**
+ * @fileoverview HTML special characters should be escaped.
+ * @author Patrick Hayes
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const getSourceCode = require('../util/eslint').getSourceCode;
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+const getMessageData = require('../util/message');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+// NOTE: '<' and '{' are also problematic characters, but they do not need
+// to be included here because it is a syntax error when these characters are
+// included accidentally.
+const DEFAULTS = [{
+  char: '>',
+  alternatives: ['&gt;'],
+}, {
+  char: '"',
+  alternatives: ['&quot;', '&ldquo;', '&#34;', '&rdquo;'],
+}, {
+  char: '\'',
+  alternatives: ['&apos;', '&lsquo;', '&#39;', '&rsquo;'],
+}, {
+  char: '}',
+  alternatives: ['&#125;'],
+}];
+
+const messages = {
+  unescapedEntity: 'HTML entity, `{{entity}}` , must be escaped.',
+  unescapedEntityAlts: '`{{entity}}` can be escaped with {{alts}}.',
+  replaceWithAlt: 'Replace with `{{alt}}`.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    hasSuggestions: true,
+    docs: {
+      description: 'Disallow unescaped HTML entities from appearing in markup',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('no-unescaped-entities'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbid: {
+          type: 'array',
+          items: {
+            anyOf: [{
+              type: 'string',
+            }, {
+              type: 'object',
+              properties: {
+                char: {
+                  type: 'string',
+                },
+                alternatives: {
+                  type: 'array',
+                  uniqueItems: true,
+                  items: {
+                    type: 'string',
+                  },
+                },
+              },
+            }],
+          },
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    function reportInvalidEntity(node) {
+      const configuration = context.options[0] || {};
+      const entities = configuration.forbid || DEFAULTS;
+
+      // HTML entities are already escaped in node.value (as well as node.raw),
+      // so pull the raw text from getSourceCode(context)
+      for (let i = node.loc.start.line; i <= node.loc.end.line; i++) {
+        let rawLine = getSourceCode(context).lines[i - 1];
+        let start = 0;
+        let end = rawLine.length;
+        if (i === node.loc.start.line) {
+          start = node.loc.start.column;
+        }
+        if (i === node.loc.end.line) {
+          end = node.loc.end.column;
+        }
+        rawLine = rawLine.slice(start, end);
+        for (let j = 0; j < entities.length; j++) {
+          for (let index = 0; index < rawLine.length; index++) {
+            const c = rawLine[index];
+            if (typeof entities[j] === 'string') {
+              if (c === entities[j]) {
+                report(context, messages.unescapedEntity, 'unescapedEntity', {
+                  node,
+                  loc: { line: i, column: start + index },
+                  data: {
+                    entity: entities[j],
+                  },
+                });
+              }
+            } else if (c === entities[j].char) {
+              report(context, messages.unescapedEntityAlts, 'unescapedEntityAlts', {
+                node,
+                loc: { line: i, column: start + index },
+                data: {
+                  entity: entities[j].char,
+                  alts: entities[j].alternatives.map((alt) => `\`${alt}\``).join(', '),
+                },
+                suggest: entities[j].alternatives.map((alt) => Object.assign(
+                  getMessageData('replaceWithAlt', messages.replaceWithAlt),
+                  {
+                    data: { alt },
+                    fix(fixer) {
+                      const lineToChange = i - node.loc.start.line;
+
+                      const newText = node.raw.split('\n').map((line, idx) => {
+                        if (idx === lineToChange) {
+                          return line.slice(0, index) + alt + line.slice(index + 1);
+                        }
+
+                        return line;
+                      }).join('\n');
+
+                      return fixer.replaceText(node, newText);
+                    },
+                  }
+                )),
+              });
+            }
+          }
+        }
+      }
+    }
+
+    return {
+      'Literal, JSXText'(node) {
+        if (jsxUtil.isJSX(node.parent)) {
+          reportInvalidEntity(node);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unknown-property.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unknown-property.d.ts","sourceRoot":"","sources":["no-unknown-property.js"],"names":[],"mappings":"wBAshBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,671 @@
+/**
+ * @fileoverview Prevent usage of unknown DOM property
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const has = require('hasown');
+const docsUrl = require('../util/docsUrl');
+const getText = require('../util/eslint').getText;
+const testReactVersion = require('../util/version').testReactVersion;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const DEFAULTS = {
+  ignore: [],
+  requireDataLowercase: false,
+};
+
+const DOM_ATTRIBUTE_NAMES = {
+  'accept-charset': 'acceptCharset',
+  class: 'className',
+  'http-equiv': 'httpEquiv',
+  crossorigin: 'crossOrigin',
+  for: 'htmlFor',
+  nomodule: 'noModule',
+};
+
+const ATTRIBUTE_TAGS_MAP = {
+  abbr: ['th', 'td'],
+  charset: ['meta'],
+  checked: ['input'],
+  // image is required for SVG support, all other tags are HTML.
+  crossOrigin: ['script', 'img', 'video', 'audio', 'link', 'image'],
+  displaystyle: ['math'],
+  // https://html.spec.whatwg.org/multipage/links.html#downloading-resources
+  download: ['a', 'area'],
+  fill: [ // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill
+    // Fill color
+    'altGlyph',
+    'circle',
+    'ellipse',
+    'g',
+    'line',
+    'marker',
+    'mask',
+    'path',
+    'polygon',
+    'polyline',
+    'rect',
+    'svg',
+    'symbol',
+    'text',
+    'textPath',
+    'tref',
+    'tspan',
+    'use',
+    // Animation final state
+    'animate',
+    'animateColor',
+    'animateMotion',
+    'animateTransform',
+    'set',
+  ],
+  focusable: ['svg'],
+  imageSizes: ['link'],
+  imageSrcSet: ['link'],
+  property: ['meta'],
+  viewBox: ['marker', 'pattern', 'svg', 'symbol', 'view'],
+  as: ['link'],
+  align: ['applet', 'caption', 'col', 'colgroup', 'hr', 'iframe', 'img', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr'], // deprecated, but known
+  valign: ['tr', 'td', 'th', 'thead', 'tbody', 'tfoot', 'colgroup', 'col'], // deprecated, but known
+  noModule: ['script'],
+  // Media events allowed only on audio and video tags, see https://github.com/facebook/react/blob/256aefbea1449869620fb26f6ec695536ab453f5/CHANGELOG.md#notable-enhancements
+  onAbort: ['audio', 'video'],
+  onCancel: ['dialog'],
+  onCanPlay: ['audio', 'video'],
+  onCanPlayThrough: ['audio', 'video'],
+  onClose: ['dialog'],
+  onDurationChange: ['audio', 'video'],
+  onEmptied: ['audio', 'video'],
+  onEncrypted: ['audio', 'video'],
+  onEnded: ['audio', 'video'],
+  onError: ['audio', 'video', 'img', 'link', 'source', 'script', 'picture', 'iframe'],
+  onLoad: ['script', 'img', 'link', 'picture', 'iframe', 'object', 'source'],
+  onLoadedData: ['audio', 'video'],
+  onLoadedMetadata: ['audio', 'video'],
+  onLoadStart: ['audio', 'video'],
+  onPause: ['audio', 'video'],
+  onPlay: ['audio', 'video'],
+  onPlaying: ['audio', 'video'],
+  onProgress: ['audio', 'video'],
+  onRateChange: ['audio', 'video'],
+  onResize: ['audio', 'video'],
+  onSeeked: ['audio', 'video'],
+  onSeeking: ['audio', 'video'],
+  onStalled: ['audio', 'video'],
+  onSuspend: ['audio', 'video'],
+  onTimeUpdate: ['audio', 'video'],
+  onVolumeChange: ['audio', 'video'],
+  onWaiting: ['audio', 'video'],
+  autoPictureInPicture: ['video'],
+  controls: ['audio', 'video'],
+  controlsList: ['audio', 'video'],
+  disablePictureInPicture: ['video'],
+  disableRemotePlayback: ['audio', 'video'],
+  loop: ['audio', 'video'],
+  muted: ['audio', 'video'],
+  playsInline: ['video'],
+  allowFullScreen: ['iframe', 'video'],
+  webkitAllowFullScreen: ['iframe', 'video'],
+  mozAllowFullScreen: ['iframe', 'video'],
+  poster: ['video'],
+  preload: ['audio', 'video'],
+  scrolling: ['iframe'],
+  returnValue: ['dialog'],
+  webkitDirectory: ['input'],
+  shadowrootmode: ['template'],
+  shadowrootclonable: ['template'],
+  shadowrootdelegatesfocus: ['template'],
+  shadowrootserializable: ['template'],
+  'transform-origin': ['rect'],
+};
+
+const SVGDOM_ATTRIBUTE_NAMES = {
+  'accent-height': 'accentHeight',
+  'alignment-baseline': 'alignmentBaseline',
+  'arabic-form': 'arabicForm',
+  'baseline-shift': 'baselineShift',
+  'cap-height': 'capHeight',
+  'clip-path': 'clipPath',
+  'clip-rule': 'clipRule',
+  'color-interpolation': 'colorInterpolation',
+  'color-interpolation-filters': 'colorInterpolationFilters',
+  'color-profile': 'colorProfile',
+  'color-rendering': 'colorRendering',
+  'dominant-baseline': 'dominantBaseline',
+  'enable-background': 'enableBackground',
+  'fill-opacity': 'fillOpacity',
+  'fill-rule': 'fillRule',
+  'flood-color': 'floodColor',
+  'flood-opacity': 'floodOpacity',
+  'font-family': 'fontFamily',
+  'font-size': 'fontSize',
+  'font-size-adjust': 'fontSizeAdjust',
+  'font-stretch': 'fontStretch',
+  'font-style': 'fontStyle',
+  'font-variant': 'fontVariant',
+  'font-weight': 'fontWeight',
+  'glyph-name': 'glyphName',
+  'glyph-orientation-horizontal': 'glyphOrientationHorizontal',
+  'glyph-orientation-vertical': 'glyphOrientationVertical',
+  'horiz-adv-x': 'horizAdvX',
+  'horiz-origin-x': 'horizOriginX',
+  'image-rendering': 'imageRendering',
+  'letter-spacing': 'letterSpacing',
+  'lighting-color': 'lightingColor',
+  'marker-end': 'markerEnd',
+  'marker-mid': 'markerMid',
+  'marker-start': 'markerStart',
+  'overline-position': 'overlinePosition',
+  'overline-thickness': 'overlineThickness',
+  'paint-order': 'paintOrder',
+  'panose-1': 'panose1',
+  'pointer-events': 'pointerEvents',
+  'rendering-intent': 'renderingIntent',
+  'shape-rendering': 'shapeRendering',
+  'stop-color': 'stopColor',
+  'stop-opacity': 'stopOpacity',
+  'strikethrough-position': 'strikethroughPosition',
+  'strikethrough-thickness': 'strikethroughThickness',
+  'stroke-dasharray': 'strokeDasharray',
+  'stroke-dashoffset': 'strokeDashoffset',
+  'stroke-linecap': 'strokeLinecap',
+  'stroke-linejoin': 'strokeLinejoin',
+  'stroke-miterlimit': 'strokeMiterlimit',
+  'stroke-opacity': 'strokeOpacity',
+  'stroke-width': 'strokeWidth',
+  'text-anchor': 'textAnchor',
+  'text-decoration': 'textDecoration',
+  'text-rendering': 'textRendering',
+  'underline-position': 'underlinePosition',
+  'underline-thickness': 'underlineThickness',
+  'unicode-bidi': 'unicodeBidi',
+  'unicode-range': 'unicodeRange',
+  'units-per-em': 'unitsPerEm',
+  'v-alphabetic': 'vAlphabetic',
+  'v-hanging': 'vHanging',
+  'v-ideographic': 'vIdeographic',
+  'v-mathematical': 'vMathematical',
+  'vector-effect': 'vectorEffect',
+  'vert-adv-y': 'vertAdvY',
+  'vert-origin-x': 'vertOriginX',
+  'vert-origin-y': 'vertOriginY',
+  'word-spacing': 'wordSpacing',
+  'writing-mode': 'writingMode',
+  'x-height': 'xHeight',
+  'xlink:actuate': 'xlinkActuate',
+  'xlink:arcrole': 'xlinkArcrole',
+  'xlink:href': 'xlinkHref',
+  'xlink:role': 'xlinkRole',
+  'xlink:show': 'xlinkShow',
+  'xlink:title': 'xlinkTitle',
+  'xlink:type': 'xlinkType',
+  'xml:base': 'xmlBase',
+  'xml:lang': 'xmlLang',
+  'xml:space': 'xmlSpace',
+};
+
+const DOM_PROPERTY_NAMES_ONE_WORD = [
+  // Global attributes - can be used on any HTML/DOM element
+  // See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
+  'dir', 'draggable', 'hidden', 'id', 'lang', 'nonce', 'part', 'slot', 'style', 'title', 'translate', 'inert',
+  // Element specific attributes
+  // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes (includes global attributes too)
+  // To be considered if these should be added also to ATTRIBUTE_TAGS_MAP
+  'accept', 'action', 'allow', 'alt', 'as', 'async', 'buffered', 'capture', 'challenge', 'cite', 'code', 'cols',
+  'content', 'coords', 'csp', 'data', 'decoding', 'default', 'defer', 'disabled', 'form',
+  'headers', 'height', 'high', 'href', 'icon', 'importance', 'integrity', 'kind', 'label',
+  'language', 'loading', 'list', 'loop', 'low', 'manifest', 'max', 'media', 'method', 'min', 'multiple', 'muted',
+  'name', 'open', 'optimum', 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'profile',
+  'rel', 'required', 'reversed', 'role', 'rows', 'sandbox', 'scope', 'seamless', 'selected', 'shape', 'size', 'sizes',
+  'span', 'src', 'start', 'step', 'summary', 'target', 'type', 'value', 'width', 'wmode', 'wrap',
+  // SVG attributes
+  // See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
+  'accumulate', 'additive', 'alphabetic', 'amplitude', 'ascent', 'azimuth', 'bbox', 'begin',
+  'bias', 'by', 'clip', 'color', 'cursor', 'cx', 'cy', 'd', 'decelerate', 'descent', 'direction',
+  'display', 'divisor', 'dur', 'dx', 'dy', 'elevation', 'end', 'exponent', 'fill', 'filter',
+  'format', 'from', 'fr', 'fx', 'fy', 'g1', 'g2', 'hanging', 'height', 'hreflang', 'ideographic',
+  'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'local', 'mask', 'mode',
+  'offset', 'opacity', 'operator', 'order', 'orient', 'orientation', 'origin', 'overflow', 'path',
+  'ping', 'points', 'r', 'radius', 'rel', 'restart', 'result', 'rotate', 'rx', 'ry', 'scale',
+  'seed', 'slope', 'spacing', 'speed', 'stemh', 'stemv', 'string', 'stroke', 'to', 'transform',
+  'u1', 'u2', 'unicode', 'values', 'version', 'visibility', 'widths', 'x', 'x1', 'x2', 'xmlns',
+  'y', 'y1', 'y2', 'z',
+  // OpenGraph meta tag attributes
+  'property',
+  // React specific attributes
+  'ref', 'key', 'children',
+  // Non-standard
+  'results', 'security',
+  // Video specific
+  'controls',
+  // popovers
+  'popover', 'popovertarget', 'popovertargetaction',
+];
+
+const DOM_PROPERTY_NAMES_TWO_WORDS = [
+  // Global attributes - can be used on any HTML/DOM element
+  // See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
+  'accessKey', 'autoCapitalize', 'autoFocus', 'contentEditable', 'enterKeyHint', 'exportParts',
+  'inputMode', 'itemID', 'itemRef', 'itemProp', 'itemScope', 'itemType', 'spellCheck', 'tabIndex',
+  // Element specific attributes
+  // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes (includes global attributes too)
+  // To be considered if these should be added also to ATTRIBUTE_TAGS_MAP
+  'acceptCharset', 'autoComplete', 'autoPlay', 'border', 'cellPadding', 'cellSpacing', 'classID', 'codeBase',
+  'colSpan', 'contextMenu', 'dateTime', 'encType', 'formAction', 'formEncType', 'formMethod', 'formNoValidate', 'formTarget',
+  'frameBorder', 'hrefLang', 'httpEquiv', 'imageSizes', 'imageSrcSet', 'isMap', 'keyParams', 'keyType', 'marginHeight', 'marginWidth',
+  'maxLength', 'mediaGroup', 'minLength', 'noValidate', 'onAnimationEnd', 'onAnimationIteration', 'onAnimationStart',
+  'onBlur', 'onChange', 'onClick', 'onContextMenu', 'onCopy', 'onCompositionEnd', 'onCompositionStart',
+  'onCompositionUpdate', 'onCut', 'onDoubleClick', 'onDrag', 'onDragEnd', 'onDragEnter', 'onDragExit', 'onDragLeave',
+  'onError', 'onFocus', 'onInput', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onLoad', 'onWheel', 'onDragOver',
+  'onDragStart', 'onDrop', 'onMouseDown', 'onMouseEnter', 'onMouseLeave', 'onMouseMove', 'onMouseOut', 'onMouseOver',
+  'onMouseUp', 'onPaste', 'onScroll', 'onSelect', 'onSubmit', 'onBeforeToggle', 'onToggle', 'onTransitionEnd', 'radioGroup',
+  'readOnly', 'referrerPolicy', 'rowSpan', 'srcDoc', 'srcLang', 'srcSet', 'useMap', 'fetchPriority',
+  // SVG attributes
+  // See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
+  'crossOrigin', 'accentHeight', 'alignmentBaseline', 'arabicForm', 'attributeName',
+  'attributeType', 'baseFrequency', 'baselineShift', 'baseProfile', 'calcMode', 'capHeight',
+  'clipPathUnits', 'clipPath', 'clipRule', 'colorInterpolation', 'colorInterpolationFilters',
+  'colorProfile', 'colorRendering', 'contentScriptType', 'contentStyleType', 'diffuseConstant',
+  'dominantBaseline', 'edgeMode', 'enableBackground', 'fillOpacity', 'fillRule', 'filterRes',
+  'filterUnits', 'floodColor', 'floodOpacity', 'fontFamily', 'fontSize', 'fontSizeAdjust',
+  'fontStretch', 'fontStyle', 'fontVariant', 'fontWeight', 'glyphName',
+  'glyphOrientationHorizontal', 'glyphOrientationVertical', 'glyphRef', 'gradientTransform',
+  'gradientUnits', 'horizAdvX', 'horizOriginX', 'imageRendering', 'kernelMatrix',
+  'kernelUnitLength', 'keyPoints', 'keySplines', 'keyTimes', 'lengthAdjust', 'letterSpacing',
+  'lightingColor', 'limitingConeAngle', 'markerEnd', 'markerMid', 'markerStart', 'markerHeight',
+  'markerUnits', 'markerWidth', 'maskContentUnits', 'maskUnits', 'mathematical', 'numOctaves',
+  'overlinePosition', 'overlineThickness', 'panose1', 'paintOrder', 'pathLength',
+  'patternContentUnits', 'patternTransform', 'patternUnits', 'pointerEvents', 'pointsAtX',
+  'pointsAtY', 'pointsAtZ', 'preserveAlpha', 'preserveAspectRatio', 'primitiveUnits',
+  'referrerPolicy', 'refX', 'refY', 'rendering-intent', 'repeatCount', 'repeatDur',
+  'requiredExtensions', 'requiredFeatures', 'shapeRendering', 'specularConstant',
+  'specularExponent', 'spreadMethod', 'startOffset', 'stdDeviation', 'stitchTiles', 'stopColor',
+  'stopOpacity', 'strikethroughPosition', 'strikethroughThickness', 'strokeDasharray',
+  'strokeDashoffset', 'strokeLinecap', 'strokeLinejoin', 'strokeMiterlimit', 'strokeOpacity',
+  'strokeWidth', 'surfaceScale', 'systemLanguage', 'tableValues', 'targetX', 'targetY',
+  'textAnchor', 'textDecoration', 'textRendering', 'textLength', 'transformOrigin',
+  'underlinePosition', 'underlineThickness', 'unicodeBidi', 'unicodeRange', 'unitsPerEm',
+  'vAlphabetic', 'vHanging', 'vIdeographic', 'vMathematical', 'vectorEffect', 'vertAdvY',
+  'vertOriginX', 'vertOriginY', 'viewBox', 'viewTarget', 'wordSpacing', 'writingMode', 'xHeight',
+  'xChannelSelector', 'xlinkActuate', 'xlinkArcrole', 'xlinkHref', 'xlinkRole', 'xlinkShow',
+  'xlinkTitle', 'xlinkType', 'xmlBase', 'xmlLang', 'xmlnsXlink', 'xmlSpace', 'yChannelSelector',
+  'zoomAndPan',
+  // Safari/Apple specific, no listing available
+  'autoCorrect', // https://stackoverflow.com/questions/47985384/html-autocorrect-for-text-input-is-not-working
+  'autoSave', // https://stackoverflow.com/questions/25456396/what-is-autosave-attribute-supposed-to-do-how-do-i-use-it
+  // React specific attributes https://reactjs.org/docs/dom-elements.html#differences-in-attributes
+  'className', 'dangerouslySetInnerHTML', 'defaultValue', 'defaultChecked', 'htmlFor',
+  // Events' capture events
+  'onBeforeInput', 'onChange',
+  'onInvalid', 'onReset', 'onTouchCancel', 'onTouchEnd', 'onTouchMove', 'onTouchStart', 'suppressContentEditableWarning', 'suppressHydrationWarning',
+  'onAbort', 'onCanPlay', 'onCanPlayThrough', 'onDurationChange', 'onEmptied', 'onEncrypted', 'onEnded',
+  'onLoadedData', 'onLoadedMetadata', 'onLoadStart', 'onPause', 'onPlay', 'onPlaying', 'onProgress', 'onRateChange', 'onResize',
+  'onSeeked', 'onSeeking', 'onStalled', 'onSuspend', 'onTimeUpdate', 'onVolumeChange', 'onWaiting',
+  'onCopyCapture', 'onCutCapture', 'onPasteCapture', 'onCompositionEndCapture', 'onCompositionStartCapture', 'onCompositionUpdateCapture',
+  'onFocusCapture', 'onBlurCapture', 'onChangeCapture', 'onBeforeInputCapture', 'onInputCapture', 'onResetCapture', 'onSubmitCapture',
+  'onInvalidCapture', 'onLoadCapture', 'onErrorCapture', 'onKeyDownCapture', 'onKeyPressCapture', 'onKeyUpCapture',
+  'onAbortCapture', 'onCanPlayCapture', 'onCanPlayThroughCapture', 'onDurationChangeCapture', 'onEmptiedCapture', 'onEncryptedCapture',
+  'onEndedCapture', 'onLoadedDataCapture', 'onLoadedMetadataCapture', 'onLoadStartCapture', 'onPauseCapture', 'onPlayCapture',
+  'onPlayingCapture', 'onProgressCapture', 'onRateChangeCapture', 'onSeekedCapture', 'onSeekingCapture', 'onStalledCapture', 'onSuspendCapture',
+  'onTimeUpdateCapture', 'onVolumeChangeCapture', 'onWaitingCapture', 'onSelectCapture', 'onTouchCancelCapture', 'onTouchEndCapture',
+  'onTouchMoveCapture', 'onTouchStartCapture', 'onScrollCapture', 'onWheelCapture', 'onAnimationEndCapture', 'onAnimationIteration',
+  'onAnimationStartCapture', 'onTransitionEndCapture',
+  'onAuxClick', 'onAuxClickCapture', 'onClickCapture', 'onContextMenuCapture', 'onDoubleClickCapture',
+  'onDragCapture', 'onDragEndCapture', 'onDragEnterCapture', 'onDragExitCapture', 'onDragLeaveCapture',
+  'onDragOverCapture', 'onDragStartCapture', 'onDropCapture', 'onMouseDown', 'onMouseDownCapture',
+  'onMouseMoveCapture', 'onMouseOutCapture', 'onMouseOverCapture', 'onMouseUpCapture',
+  // Video specific
+  'autoPictureInPicture', 'controlsList', 'disablePictureInPicture', 'disableRemotePlayback',
+  // popovers
+  'popoverTarget', 'popoverTargetAction',
+];
+
+const DOM_PROPERTIES_IGNORE_CASE = ['charset', 'allowFullScreen', 'webkitAllowFullScreen', 'mozAllowFullScreen', 'webkitDirectory', 'popoverTarget', 'popoverTargetAction'];
+
+const ARIA_PROPERTIES = [
+  // See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes
+  // Global attributes
+  'aria-atomic', 'aria-braillelabel', 'aria-brailleroledescription', 'aria-busy', 'aria-controls', 'aria-current',
+  'aria-describedby', 'aria-description', 'aria-details',
+  'aria-disabled', 'aria-dropeffect', 'aria-errormessage', 'aria-flowto', 'aria-grabbed', 'aria-haspopup',
+  'aria-hidden', 'aria-invalid', 'aria-keyshortcuts', 'aria-label', 'aria-labelledby', 'aria-live',
+  'aria-owns', 'aria-relevant', 'aria-roledescription',
+  // Widget attributes
+  'aria-autocomplete', 'aria-checked', 'aria-expanded', 'aria-level', 'aria-modal', 'aria-multiline', 'aria-multiselectable',
+  'aria-orientation', 'aria-placeholder', 'aria-pressed', 'aria-readonly', 'aria-required', 'aria-selected',
+  'aria-sort', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext',
+  // Relationship attributes
+  'aria-activedescendant', 'aria-colcount', 'aria-colindex', 'aria-colindextext', 'aria-colspan',
+  'aria-posinset', 'aria-rowcount', 'aria-rowindex', 'aria-rowindextext', 'aria-rowspan', 'aria-setsize',
+];
+
+const REACT_ON_PROPS = [
+  'onGotPointerCapture',
+  'onGotPointerCaptureCapture',
+  'onLostPointerCapture',
+  'onLostPointerCapture',
+  'onLostPointerCaptureCapture',
+  'onPointerCancel',
+  'onPointerCancelCapture',
+  'onPointerDown',
+  'onPointerDownCapture',
+  'onPointerEnter',
+  'onPointerEnterCapture',
+  'onPointerLeave',
+  'onPointerLeaveCapture',
+  'onPointerMove',
+  'onPointerMoveCapture',
+  'onPointerOut',
+  'onPointerOutCapture',
+  'onPointerOver',
+  'onPointerOverCapture',
+  'onPointerUp',
+  'onPointerUpCapture',
+];
+
+function getDOMPropertyNames(context) {
+  return [].concat(
+    DOM_PROPERTY_NAMES_TWO_WORDS,
+    DOM_PROPERTY_NAMES_ONE_WORD,
+
+    testReactVersion(context, '>= 16.1.0') ? [].concat(
+      testReactVersion(context, '>= 16.4.0') ? [].concat(
+        // these were added in React v16.4.0, see https://reactjs.org/blog/2018/05/23/react-v-16-4.html and https://github.com/facebook/react/pull/12507
+        REACT_ON_PROPS,
+        testReactVersion(context, '>= 19') ? [
+          // precedence was added in React v19, see https://react.dev/blog/2024/04/25/react-19#support-for-stylesheets
+          'precedence',
+        ] : []
+      ) : []
+    ) : [
+      // this was removed in React v16.1+, see https://github.com/facebook/react/pull/10823
+      'allowTransparency',
+    ]
+  );
+}
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * Checks if a node's parent is a JSX tag that is written with lowercase letters,
+ * and is not a custom web component. Custom web components have a hyphen in tag name,
+ * or have an `is="some-elem"` attribute.
+ *
+ * Note: does not check if a tag's parent against a list of standard HTML/DOM tags. For example,
+ * a `<fake>`'s child would return `true` because "fake" is written only with lowercase letters
+ * without a hyphen and does not have a `is="some-elem"` attribute.
+ *
+ * @param {Object} childNode - JSX element being tested.
+ * @returns {boolean} Whether or not the node name match the JSX tag convention.
+ */
+function isValidHTMLTagInJSX(childNode) {
+  const tagConvention = /^[a-z][^-]*$/;
+  if (tagConvention.test(childNode.parent.name.name)) {
+    return !childNode.parent.attributes.some((attrNode) => (
+      attrNode.type === 'JSXAttribute'
+        && attrNode.name.type === 'JSXIdentifier'
+        && attrNode.name.name === 'is'
+        // To learn more about custom web components and `is` attribute,
+        // see https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example
+
+    ));
+  }
+  return false;
+}
+
+/**
+ * Checks if the attribute name is included in the attributes that are excluded
+ * from the camel casing.
+ *
+ * // returns 'charSet'
+ * @example normalizeAttributeCase('charset')
+ *
+ * Note - these exclusions are not made by React core team, but `eslint-plugin-react` community.
+ *
+ * @param {string} name - Attribute name to be normalized
+ * @returns {string} Result
+ */
+function normalizeAttributeCase(name) {
+  return DOM_PROPERTIES_IGNORE_CASE.find((element) => element.toLowerCase() === name.toLowerCase()) || name;
+}
+
+/**
+ * Checks if an attribute name is a valid `data-*` attribute:
+ * if the name starts with "data-" and has alphanumeric words (browsers require lowercase, but React and TS lowercase them),
+ * not start with any casing of "xml", and separated by hyphens (-) (which is also called "kebab case" or "dash case"),
+ * then the attribute is a valid data attribute.
+ *
+ * @param {string} name - Attribute name to be tested
+ * @returns {boolean} Result
+ */
+function isValidDataAttribute(name) {
+  return !/^data-xml/i.test(name) && /^data-[^:]*$/.test(name);
+}
+
+/**
+ * Checks if an attribute name has at least one uppercase characters
+ *
+ * @param {string} name
+ * @returns {boolean} Result
+ */
+function hasUpperCaseCharacter(name) {
+  return name.toLowerCase() !== name;
+}
+
+/**
+ * Checks if an attribute name is a standard aria attribute by compering it to a list
+ * of standard aria property names
+ *
+ * @param {string} name - Attribute name to be tested
+ * @returns {boolean} Result
+ */
+
+function isValidAriaAttribute(name) {
+  return ARIA_PROPERTIES.some((element) => element === name);
+}
+
+/**
+ * Extracts the tag name for the JSXAttribute
+ * @param {JSXAttribute} node - JSXAttribute being tested.
+ * @returns {string | null} tag name
+ */
+function getTagName(node) {
+  if (
+    node
+    && node.parent
+    && node.parent.name
+  ) {
+    return node.parent.name.name;
+  }
+  return null;
+}
+
+/**
+ * Test wether the tag name for the JSXAttribute is
+ * something like <Foo.bar />
+ * @param {JSXAttribute} node - JSXAttribute being tested.
+ * @returns {boolean} result
+ */
+function tagNameHasDot(node) {
+  return !!(
+    node.parent
+    && node.parent.name
+    && node.parent.name.type === 'JSXMemberExpression'
+  );
+}
+
+/**
+ * Get the standard name of the attribute.
+ * @param {string} name - Name of the attribute.
+ * @param {object} context - eslint context
+ * @returns {string | undefined} The standard name of the attribute, or undefined if no standard name was found.
+ */
+function getStandardName(name, context) {
+  if (has(DOM_ATTRIBUTE_NAMES, name)) {
+    return DOM_ATTRIBUTE_NAMES[/** @type {keyof DOM_ATTRIBUTE_NAMES} */ (name)];
+  }
+  if (has(SVGDOM_ATTRIBUTE_NAMES, name)) {
+    return SVGDOM_ATTRIBUTE_NAMES[/** @type {keyof SVGDOM_ATTRIBUTE_NAMES} */ (name)];
+  }
+  const names = getDOMPropertyNames(context);
+
+  // Let's find a possible attribute match with a case-insensitive search.
+  return names.find((element) => element.toLowerCase() === name.toLowerCase());
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  invalidPropOnTag: 'Invalid property \'{{name}}\' found on tag \'{{tagName}}\', but it is only allowed on: {{allowedTags}}',
+  unknownPropWithStandardName: 'Unknown property \'{{name}}\' found, use \'{{standardName}}\' instead',
+  unknownProp: 'Unknown property \'{{name}}\' found',
+  dataLowercaseRequired: 'React does not recognize data-* props with uppercase characters on a DOM element. Found \'{{name}}\', use \'{{lowerCaseName}}\' instead',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of unknown DOM property',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('no-unknown-property'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignore: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        requireDataLowercase: {
+          type: 'boolean',
+          default: false,
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    function getIgnoreConfig() {
+      return (context.options[0] && context.options[0].ignore) || DEFAULTS.ignore;
+    }
+
+    function getRequireDataLowercase() {
+      return (context.options[0] && typeof context.options[0].requireDataLowercase !== 'undefined')
+        ? !!context.options[0].requireDataLowercase
+        : DEFAULTS.requireDataLowercase;
+    }
+
+    return {
+      JSXAttribute(node) {
+        const ignoreNames = getIgnoreConfig();
+        const actualName = getText(context, node.name);
+        if (ignoreNames.indexOf(actualName) >= 0) {
+          return;
+        }
+        const name = normalizeAttributeCase(actualName);
+
+        // Ignore tags like <Foo.bar />
+        if (tagNameHasDot(node)) {
+          return;
+        }
+
+        if (isValidDataAttribute(name)) {
+          if (getRequireDataLowercase() && hasUpperCaseCharacter(name)) {
+            report(context, messages.dataLowercaseRequired, 'dataLowercaseRequired', {
+              node,
+              data: {
+                name: actualName,
+                lowerCaseName: actualName.toLowerCase(),
+              },
+            });
+          }
+
+          return;
+        }
+
+        if (isValidAriaAttribute(name)) { return; }
+
+        const tagName = getTagName(node);
+
+        if (tagName === 'fbt' || tagName === 'fbs') { return; } // fbt/fbs nodes are bonkers, let's not go there
+
+        if (!isValidHTMLTagInJSX(node)) { return; }
+
+        // Let's dive deeper into tags that are HTML/DOM elements (`<button>`), and not React components (`<Button />`)
+
+        // Some attributes are allowed on some tags only
+        const allowedTags = has(ATTRIBUTE_TAGS_MAP, name)
+          ? ATTRIBUTE_TAGS_MAP[/** @type {keyof ATTRIBUTE_TAGS_MAP} */ (name)]
+          : null;
+        if (tagName && allowedTags) {
+          // Scenario 1A: Allowed attribute found where not supposed to, report it
+          if (allowedTags.indexOf(tagName) === -1) {
+            report(context, messages.invalidPropOnTag, 'invalidPropOnTag', {
+              node,
+              data: {
+                name: actualName,
+                tagName,
+                allowedTags: allowedTags.join(', '),
+              },
+            });
+          }
+          // Scenario 1B: There are allowed attributes on allowed tags, no need to report it
+          return;
+        }
+
+        // Let's see if the attribute is a close version to some standard property name
+        const standardName = getStandardName(name, context);
+
+        const hasStandardNameButIsNotUsed = standardName && standardName !== name;
+        const usesStandardName = standardName && standardName === name;
+
+        if (usesStandardName) {
+          // Scenario 2A: The attribute name is the standard name, no need to report it
+          return;
+        }
+
+        if (hasStandardNameButIsNotUsed) {
+          // Scenario 2B: The name of the attribute is close to a standard one, report it with the standard name
+          report(context, messages.unknownPropWithStandardName, 'unknownPropWithStandardName', {
+            node,
+            data: {
+              name: actualName,
+              standardName,
+            },
+            fix(fixer) {
+              return fixer.replaceText(node.name, standardName);
+            },
+          });
+          return;
+        }
+
+        // Scenario 3: We have an attribute that is unknown, report it
+        report(context, messages.unknownProp, 'unknownProp', {
+          node,
+          data: {
+            name: actualName,
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unsafe.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unsafe.d.ts","sourceRoot":"","sources":["no-unsafe.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unsafe.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,151 @@
+/**
+ * @fileoverview Prevent usage of unsafe lifecycle methods
+ * @author Sergei Startsev
+ */
+
+'use strict';
+
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const testReactVersion = require('../util/version').testReactVersion;
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  unsafeMethod: '{{method}} is unsafe for use in async rendering. Update the component to use {{newMethod}} instead. {{details}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow usage of unsafe lifecycle methods',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-unsafe'),
+    },
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          checkAliases: {
+            default: false,
+            type: 'boolean',
+          },
+        },
+        additionalProperties: false,
+      },
+    ],
+  },
+
+  create(context) {
+    const config = context.options[0] || {};
+    const checkAliases = config.checkAliases || false;
+
+    const isApplicable = testReactVersion(context, '>= 16.3.0');
+    if (!isApplicable) {
+      return {};
+    }
+
+    const unsafe = {
+      UNSAFE_componentWillMount: {
+        newMethod: 'componentDidMount',
+        details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.',
+      },
+      UNSAFE_componentWillReceiveProps: {
+        newMethod: 'getDerivedStateFromProps',
+        details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.',
+      },
+      UNSAFE_componentWillUpdate: {
+        newMethod: 'componentDidUpdate',
+        details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.',
+      },
+    };
+    if (checkAliases) {
+      unsafe.componentWillMount = unsafe.UNSAFE_componentWillMount;
+      unsafe.componentWillReceiveProps = unsafe.UNSAFE_componentWillReceiveProps;
+      unsafe.componentWillUpdate = unsafe.UNSAFE_componentWillUpdate;
+    }
+
+    /**
+     * Returns a list of unsafe methods
+     * @returns {Array} A list of unsafe methods
+     */
+    function getUnsafeMethods() {
+      return Object.keys(unsafe);
+    }
+
+    /**
+     * Checks if a passed method is unsafe
+     * @param {string} method Life cycle method
+     * @returns {boolean} Returns true for unsafe methods, otherwise returns false
+     */
+    function isUnsafe(method) {
+      const unsafeMethods = getUnsafeMethods();
+      return unsafeMethods.indexOf(method) !== -1;
+    }
+
+    /**
+     * Reports the error for an unsafe method
+     * @param {ASTNode} node The AST node being checked
+     * @param {string} method Life cycle method
+     */
+    function checkUnsafe(node, method) {
+      if (!isUnsafe(method)) {
+        return;
+      }
+
+      const meta = unsafe[method];
+      const newMethod = meta.newMethod;
+      const details = meta.details;
+
+      const propertyNode = astUtil.getComponentProperties(node)
+        .find((property) => astUtil.getPropertyName(property) === method);
+
+      report(context, messages.unsafeMethod, 'unsafeMethod', {
+        node: propertyNode,
+        data: {
+          method,
+          newMethod,
+          details,
+        },
+      });
+    }
+
+    /**
+     * Returns life cycle methods if available
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {Array} The array of methods.
+     */
+    function getLifeCycleMethods(node) {
+      const properties = astUtil.getComponentProperties(node);
+      return properties.map((property) => astUtil.getPropertyName(property));
+    }
+
+    /**
+     * Checks life cycle methods
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function checkLifeCycleMethods(node) {
+      if (componentUtil.isES5Component(node, context) || componentUtil.isES6Component(node, context)) {
+        const methods = getLifeCycleMethods(node);
+        methods
+          .sort((a, b) => a.localeCompare(b))
+          .forEach((method) => checkUnsafe(node, method));
+      }
+    }
+
+    return {
+      ClassDeclaration: checkLifeCycleMethods,
+      ClassExpression: checkLifeCycleMethods,
+      ObjectExpression: checkLifeCycleMethods,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unstable-nested-components.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unstable-nested-components.d.ts","sourceRoot":"","sources":["no-unstable-nested-components.js"],"names":[],"mappings":"wBAsQW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,491 @@
+/**
+ * @fileoverview Prevent creating unstable components inside components
+ * @author Ari Perkkiö
+ */
+
+'use strict';
+
+const minimatch = require('minimatch');
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const COMPONENT_AS_PROPS_INFO = ' If you want to allow component creation in props, set allowAsProps option to true.';
+const HOOK_REGEXP = /^use[A-Z0-9].*$/;
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * Generate error message with given parent component name
+ * @param {string} parentName Name of the parent component, if known
+ * @returns {string} Error message with parent component name
+ */
+function generateErrorMessageWithParentName(parentName) {
+  return `Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component${parentName ? ` “${parentName}” ` : ' '}and pass data as props.`;
+}
+
+/**
+ * Check whether given text matches the pattern passed in.
+ * @param {string} text Text to validate
+ * @param {string} pattern Pattern to match against
+ * @returns {boolean}
+ */
+function propMatchesRenderPropPattern(text, pattern) {
+  return typeof text === 'string' && minimatch(text, pattern);
+}
+
+/**
+ * Get closest parent matching given matcher
+ * @param {ASTNode} node The AST node
+ * @param {Context} context eslint context
+ * @param {Function} matcher Method used to match the parent
+ * @returns {ASTNode} The matching parent node, if any
+ */
+function getClosestMatchingParent(node, context, matcher) {
+  if (!node || !node.parent || node.parent.type === 'Program') {
+    return;
+  }
+
+  if (matcher(node.parent, context)) {
+    return node.parent;
+  }
+
+  return getClosestMatchingParent(node.parent, context, matcher);
+}
+
+/**
+ * Matcher used to check whether given node is a `createElement` call
+ * @param {ASTNode} node The AST node
+ * @param {Context} context eslint context
+ * @returns {boolean} True if node is a `createElement` call, false if not
+ */
+function isCreateElementMatcher(node, context) {
+  return (
+    astUtil.isCallExpression(node)
+    && isCreateElement(context, node)
+  );
+}
+
+/**
+ * Matcher used to check whether given node is a `ObjectExpression`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `ObjectExpression`, false if not
+ */
+function isObjectExpressionMatcher(node) {
+  return node && node.type === 'ObjectExpression';
+}
+
+/**
+ * Matcher used to check whether given node is a `JSXExpressionContainer`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `JSXExpressionContainer`, false if not
+ */
+function isJSXExpressionContainerMatcher(node) {
+  return node && node.type === 'JSXExpressionContainer';
+}
+
+/**
+ * Matcher used to check whether given node is a `JSXAttribute` of `JSXExpressionContainer`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `JSXAttribute` of `JSXExpressionContainer`, false if not
+ */
+function isJSXAttributeOfExpressionContainerMatcher(node) {
+  return (
+    node
+    && node.type === 'JSXAttribute'
+    && node.value
+    && node.value.type === 'JSXExpressionContainer'
+  );
+}
+
+/**
+ * Matcher used to check whether given node is an object `Property`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `Property`, false if not
+ */
+function isPropertyOfObjectExpressionMatcher(node) {
+  return (
+    node
+    && node.parent
+    && node.parent.type === 'Property'
+  );
+}
+
+/**
+ * Check whether given node or its parent is directly inside `map` call
+ * ```jsx
+ * {items.map(item => <li />)}
+ * ```
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is directly inside `map` call, false if not
+ */
+function isMapCall(node) {
+  return (
+    node
+    && node.callee
+    && node.callee.property
+    && node.callee.property.name === 'map'
+  );
+}
+
+/**
+ * Check whether given node is `ReturnStatement` of a React hook
+ * @param {ASTNode} node The AST node
+ * @param {Context} context eslint context
+ * @returns {boolean} True if node is a `ReturnStatement` of a React hook, false if not
+ */
+function isReturnStatementOfHook(node, context) {
+  if (
+    !node
+    || !node.parent
+    || node.parent.type !== 'ReturnStatement'
+  ) {
+    return false;
+  }
+
+  const callExpression = getClosestMatchingParent(node, context, astUtil.isCallExpression);
+  return (
+    callExpression
+    && callExpression.callee
+    && HOOK_REGEXP.test(callExpression.callee.name)
+  );
+}
+
+/**
+ * Check whether given node is declared inside a render prop
+ * ```jsx
+ * <Component renderFooter={() => <div />} />
+ * <Component>{() => <div />}</Component>
+ * ```
+ * @param {ASTNode} node The AST node
+ * @param {Context} context eslint context
+ * @param {string} propNamePattern a pattern to match render props against
+ * @returns {boolean} True if component is declared inside a render prop, false if not
+ */
+function isComponentInRenderProp(node, context, propNamePattern) {
+  if (
+    node
+    && node.parent
+    && node.parent.type === 'Property'
+    && node.parent.key
+    && propMatchesRenderPropPattern(node.parent.key.name, propNamePattern)
+  ) {
+    return true;
+  }
+
+  // Check whether component is a render prop used as direct children, e.g. <Component>{() => <div />}</Component>
+  if (
+    node
+    && node.parent
+    && node.parent.type === 'JSXExpressionContainer'
+    && node.parent.parent
+    && node.parent.parent.type === 'JSXElement'
+  ) {
+    return true;
+  }
+
+  const jsxExpressionContainer = getClosestMatchingParent(node, context, isJSXExpressionContainerMatcher);
+
+  // Check whether prop name indicates accepted patterns
+  if (
+    jsxExpressionContainer
+    && jsxExpressionContainer.parent
+    && jsxExpressionContainer.parent.type === 'JSXAttribute'
+    && jsxExpressionContainer.parent.name
+    && jsxExpressionContainer.parent.name.type === 'JSXIdentifier'
+  ) {
+    const propName = jsxExpressionContainer.parent.name.name;
+
+    // Starts with render, e.g. <Component renderFooter={() => <div />} />
+    if (propMatchesRenderPropPattern(propName, propNamePattern)) {
+      return true;
+    }
+
+    // Uses children prop explicitly, e.g. <Component children={() => <div />} />
+    if (propName === 'children') {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/**
+ * Check whether given node is declared directly inside a render property
+ * ```jsx
+ * const rows = { render: () => <div /> }
+ * <Component rows={ [{ render: () => <div /> }] } />
+ *  ```
+ * @param {ASTNode} node The AST node
+ * @param {string} propNamePattern The pattern to match render props against
+ * @returns {boolean} True if component is declared inside a render property, false if not
+ */
+function isDirectValueOfRenderProperty(node, propNamePattern) {
+  return (
+    node
+    && node.parent
+    && node.parent.type === 'Property'
+    && node.parent.key
+    && node.parent.key.type === 'Identifier'
+    && propMatchesRenderPropPattern(node.parent.key.name, propNamePattern)
+  );
+}
+
+/**
+ * Resolve the component name of given node
+ * @param {ASTNode} node The AST node of the component
+ * @returns {string} Name of the component, if any
+ */
+function resolveComponentName(node) {
+  const parentName = node.id && node.id.name;
+  if (parentName) return parentName;
+
+  return (
+    node.type === 'ArrowFunctionExpression'
+    && node.parent
+    && node.parent.id
+    && node.parent.id.name
+  );
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow creating unstable components inside components',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('no-unstable-nested-components'),
+    },
+    schema: [{
+      type: 'object',
+      properties: {
+        customValidators: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        allowAsProps: {
+          type: 'boolean',
+        },
+        propNamePattern: {
+          type: 'string',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const allowAsProps = context.options.some((option) => option && option.allowAsProps);
+    const propNamePattern = (context.options[0] || {}).propNamePattern || 'render*';
+
+    /**
+     * Check whether given node is declared inside class component's render block
+     * ```jsx
+     * class Component extends React.Component {
+     *   render() {
+     *     class NestedClassComponent extends React.Component {
+     * ...
+     * ```
+     * @param {ASTNode} node The AST node being checked
+     * @returns {boolean} True if node is inside class component's render block, false if not
+     */
+    function isInsideRenderMethod(node) {
+      const parentComponent = utils.getParentComponent(node);
+
+      if (!parentComponent || parentComponent.type !== 'ClassDeclaration') {
+        return false;
+      }
+
+      return (
+        node
+        && node.parent
+        && node.parent.type === 'MethodDefinition'
+        && node.parent.key
+        && node.parent.key.name === 'render'
+      );
+    }
+
+    /**
+     * Check whether given node is a function component declared inside class component.
+     * Util's component detection fails to detect function components inside class components.
+     * ```jsx
+     * class Component extends React.Component {
+     *  render() {
+     *    const NestedComponent = () => <div />;
+     * ...
+     * ```
+     * @param {ASTNode} node The AST node being checked
+     * @returns {boolean} True if given node a function component declared inside class component, false if not
+     */
+    function isFunctionComponentInsideClassComponent(node) {
+      const parentComponent = utils.getParentComponent(node);
+      const parentStatelessComponent = utils.getParentStatelessComponent(node);
+
+      return (
+        parentComponent
+        && parentStatelessComponent
+        && parentComponent.type === 'ClassDeclaration'
+        && utils.getStatelessComponent(parentStatelessComponent)
+        && utils.isReturningJSX(node)
+      );
+    }
+
+    /**
+     * Check whether given node is declared inside `createElement` call's props
+     * ```js
+     * React.createElement(Component, {
+     *   footer: () => React.createElement("div", null)
+     * })
+     * ```
+     * @param {ASTNode} node The AST node
+     * @returns {boolean} True if node is declare inside `createElement` call's props, false if not
+     */
+    function isComponentInsideCreateElementsProp(node) {
+      if (!components.get(node)) {
+        return false;
+      }
+
+      const createElementParent = getClosestMatchingParent(node, context, isCreateElementMatcher);
+
+      return (
+        createElementParent
+        && createElementParent.arguments
+        && createElementParent.arguments[1] === getClosestMatchingParent(node, context, isObjectExpressionMatcher)
+      );
+    }
+
+    /**
+     * Check whether given node is declared inside a component/object prop.
+     * ```jsx
+     * <Component footer={() => <div />} />
+     * { footer: () => <div /> }
+     * ```
+     * @param {ASTNode} node The AST node being checked
+     * @returns {boolean} True if node is a component declared inside prop, false if not
+     */
+    function isComponentInProp(node) {
+      if (isPropertyOfObjectExpressionMatcher(node)) {
+        return utils.isReturningJSX(node);
+      }
+
+      const jsxAttribute = getClosestMatchingParent(node, context, isJSXAttributeOfExpressionContainerMatcher);
+
+      if (!jsxAttribute) {
+        return isComponentInsideCreateElementsProp(node);
+      }
+
+      return utils.isReturningJSX(node);
+    }
+
+    /**
+     * Check whether given node is a stateless component returning non-JSX
+     * ```jsx
+     * {{ a: () => null }}
+     * ```
+     * @param {ASTNode} node The AST node being checked
+     * @returns {boolean} True if node is a stateless component returning non-JSX, false if not
+     */
+    function isStatelessComponentReturningNull(node) {
+      const component = utils.getStatelessComponent(node);
+
+      return component && !utils.isReturningJSX(component);
+    }
+
+    /**
+     * Check whether given node is a unstable nested component
+     * @param {ASTNode} node The AST node being checked
+     */
+    function validate(node) {
+      if (!node || !node.parent) {
+        return;
+      }
+
+      const isDeclaredInsideProps = isComponentInProp(node);
+
+      if (
+        !components.get(node)
+        && !isFunctionComponentInsideClassComponent(node)
+        && !isDeclaredInsideProps) {
+        return;
+      }
+
+      if (
+        // Support allowAsProps option
+        (isDeclaredInsideProps && (allowAsProps || isComponentInRenderProp(node, context, propNamePattern)))
+
+        // Prevent reporting components created inside Array.map calls
+        || isMapCall(node)
+        || isMapCall(node.parent)
+
+        // Do not mark components declared inside hooks (or falsy '() => null' clean-up methods)
+        || isReturnStatementOfHook(node, context)
+
+        // Do not mark objects containing render methods
+        || isDirectValueOfRenderProperty(node, propNamePattern)
+
+        // Prevent reporting nested class components twice
+        || isInsideRenderMethod(node)
+
+        // Prevent falsely reporting detected "components" which do not return JSX
+        || isStatelessComponentReturningNull(node)
+      ) {
+        return;
+      }
+
+      // Get the closest parent component
+      const parentComponent = getClosestMatchingParent(
+        node,
+        context,
+        (nodeToMatch) => components.get(nodeToMatch)
+      );
+
+      if (parentComponent) {
+        const parentName = resolveComponentName(parentComponent);
+
+        // Exclude lowercase parents, e.g. function createTestComponent()
+        // React-dom prevents creating lowercase components
+        if (parentName && parentName[0] === parentName[0].toLowerCase()) {
+          return;
+        }
+
+        let message = generateErrorMessageWithParentName(parentName);
+
+        // Add information about allowAsProps option when component is declared inside prop
+        if (isDeclaredInsideProps && !allowAsProps) {
+          message += COMPONENT_AS_PROPS_INFO;
+        }
+
+        report(context, message, null, {
+          node,
+        });
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      FunctionDeclaration(node) { validate(node); },
+      ArrowFunctionExpression(node) { validate(node); },
+      FunctionExpression(node) { validate(node); },
+      ClassDeclaration(node) { validate(node); },
+      CallExpression(node) { validate(node); },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unused-class-component-methods.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unused-class-component-methods.d.ts","sourceRoot":"","sources":["no-unused-class-component-methods.js"],"names":[],"mappings":"wBAoGW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,258 @@
+/**
+ * @fileoverview Prevent declaring unused methods and properties of component class
+ * @author Paweł Nowak, Berton Zhu
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const componentUtil = require('../util/componentUtil');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const LIFECYCLE_METHODS = new Set([
+  'constructor',
+  'componentDidCatch',
+  'componentDidMount',
+  'componentDidUpdate',
+  'componentWillMount',
+  'componentWillReceiveProps',
+  'componentWillUnmount',
+  'componentWillUpdate',
+  'getChildContext',
+  'getSnapshotBeforeUpdate',
+  'render',
+  'shouldComponentUpdate',
+  'UNSAFE_componentWillMount',
+  'UNSAFE_componentWillReceiveProps',
+  'UNSAFE_componentWillUpdate',
+]);
+
+const ES6_LIFECYCLE = new Set([
+  'state',
+]);
+
+const ES5_LIFECYCLE = new Set([
+  'getInitialState',
+  'getDefaultProps',
+  'mixins',
+]);
+
+function isKeyLiteralLike(node, property) {
+  return property.type === 'Literal'
+     || (property.type === 'TemplateLiteral' && property.expressions.length === 0)
+     || (node.computed === false && property.type === 'Identifier');
+}
+
+// Descend through all wrapping TypeCastExpressions and return the expression
+// that was cast.
+function uncast(node) {
+  while (node.type === 'TypeCastExpression') {
+    node = node.expression;
+  }
+  return node;
+}
+
+// Return the name of an identifier or the string value of a literal. Useful
+// anywhere that a literal may be used as a key (e.g., member expressions,
+// method definitions, ObjectExpression property keys).
+function getName(node) {
+  node = uncast(node);
+  const type = node.type;
+
+  if (type === 'Identifier') {
+    return node.name;
+  }
+  if (type === 'Literal') {
+    return String(node.value);
+  }
+  if (type === 'TemplateLiteral' && node.expressions.length === 0) {
+    return node.quasis[0].value.raw;
+  }
+  return null;
+}
+
+function isThisExpression(node) {
+  return uncast(node).type === 'ThisExpression';
+}
+
+function getInitialClassInfo(node, isClass) {
+  return {
+    classNode: node,
+    isClass,
+    // Set of nodes where properties were defined.
+    properties: new Set(),
+
+    // Set of names of properties that we've seen used.
+    usedProperties: new Set(),
+
+    inStatic: false,
+  };
+}
+
+const messages = {
+  unused: 'Unused method or property "{{name}}"',
+  unusedWithClass: 'Unused method or property "{{name}}" of class "{{className}}"',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow declaring unused methods of component class',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-unused-class-component-methods'),
+    },
+    messages,
+    schema: [],
+  },
+
+  create: ((context) => {
+    let classInfo = null;
+
+    // Takes an ObjectExpression node and adds all named Property nodes to the
+    // current set of properties.
+    function addProperty(node) {
+      classInfo.properties.add(node);
+    }
+
+    // Adds the name of the given node as a used property if the node is an
+    // Identifier or a Literal. Other node types are ignored.
+    function addUsedProperty(node) {
+      const name = getName(node);
+      if (name) {
+        classInfo.usedProperties.add(name);
+      }
+    }
+
+    function reportUnusedProperties() {
+      // Report all unused properties.
+      for (const node of classInfo.properties) { // eslint-disable-line no-restricted-syntax
+        const name = getName(node);
+        if (
+          !classInfo.usedProperties.has(name)
+           && !LIFECYCLE_METHODS.has(name)
+           && (classInfo.isClass ? !ES6_LIFECYCLE.has(name) : !ES5_LIFECYCLE.has(name))
+        ) {
+          const className = (classInfo.classNode.id && classInfo.classNode.id.name) || '';
+
+          const messageID = className ? 'unusedWithClass' : 'unused';
+          report(
+            context,
+            messages[messageID],
+            messageID,
+            {
+              node,
+              data: {
+                name,
+                className,
+              },
+            }
+          );
+        }
+      }
+    }
+
+    function exitMethod() {
+      if (!classInfo || !classInfo.inStatic) {
+        return;
+      }
+
+      classInfo.inStatic = false;
+    }
+
+    return {
+      ClassDeclaration(node) {
+        if (componentUtil.isES6Component(node, context)) {
+          classInfo = getInitialClassInfo(node, true);
+        }
+      },
+
+      ObjectExpression(node) {
+        if (componentUtil.isES5Component(node, context)) {
+          classInfo = getInitialClassInfo(node, false);
+        }
+      },
+
+      'ClassDeclaration:exit'() {
+        if (!classInfo) {
+          return;
+        }
+        reportUnusedProperties();
+        classInfo = null;
+      },
+
+      'ObjectExpression:exit'(node) {
+        if (!classInfo || classInfo.classNode !== node) {
+          return;
+        }
+        reportUnusedProperties();
+        classInfo = null;
+      },
+
+      Property(node) {
+        if (!classInfo || classInfo.classNode !== node.parent) {
+          return;
+        }
+
+        if (isKeyLiteralLike(node, node.key)) {
+          addProperty(node.key);
+        }
+      },
+
+      'ClassProperty, MethodDefinition, PropertyDefinition'(node) {
+        if (!classInfo) {
+          return;
+        }
+
+        if (node.static) {
+          classInfo.inStatic = true;
+          return;
+        }
+
+        if (isKeyLiteralLike(node, node.key)) {
+          addProperty(node.key);
+        }
+      },
+
+      'ClassProperty:exit': exitMethod,
+      'MethodDefinition:exit': exitMethod,
+      'PropertyDefinition:exit': exitMethod,
+
+      MemberExpression(node) {
+        if (!classInfo || classInfo.inStatic) {
+          return;
+        }
+
+        if (isThisExpression(node.object) && isKeyLiteralLike(node, node.property)) {
+          if (node.parent.type === 'AssignmentExpression' && node.parent.left === node) {
+            // detect `this.property = xxx`
+            addProperty(node.property);
+          } else {
+            // detect `this.property()`, `x = this.property`, etc.
+            addUsedProperty(node.property);
+          }
+        }
+      },
+
+      VariableDeclarator(node) {
+        if (!classInfo || classInfo.inStatic) {
+          return;
+        }
+
+        // detect `{ foo, bar: baz } = this`
+        if (node.init && isThisExpression(node.init) && node.id.type === 'ObjectPattern') {
+          node.id.properties
+            .filter((prop) => prop.type === 'Property' && isKeyLiteralLike(prop, prop.key))
+            .forEach((prop) => {
+              addUsedProperty('key' in prop ? prop.key : undefined);
+            });
+        }
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unused-prop-types.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unused-prop-types.d.ts","sourceRoot":"","sources":["no-unused-prop-types.js"],"names":[],"mappings":"wBAiCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,171 @@
+/**
+ * @fileoverview Prevent definitions of unused prop types
+ * @author Evgueni Naverniouk
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+// As for exceptions for props.children or props.className (and alike) look at
+// https://github.com/jsx-eslint/eslint-plugin-react/issues/7
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+/**
+ * Checks if the component must be validated
+ * @param {Object} component The component to process
+ * @returns {boolean} True if the component must be validated, false if not.
+ */
+function mustBeValidated(component) {
+  return !!component && !component.ignoreUnusedPropTypesValidation;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  unusedPropType: '\'{{name}}\' PropType is defined but prop is never used',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow definitions of unused propTypes',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-unused-prop-types'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignore: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+          uniqueItems: true,
+        },
+        customValidators: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        skipShapeProps: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components) => {
+    const defaults = { skipShapeProps: true, customValidators: [], ignore: [] };
+    const configuration = Object.assign({}, defaults, context.options[0] || {});
+
+    /**
+     * Checks if the prop is ignored
+     * @param {string} name Name of the prop to check.
+     * @returns {boolean} True if the prop is ignored, false if not.
+     */
+    function isIgnored(name) {
+      return configuration.ignore.indexOf(name) !== -1;
+    }
+
+    /**
+     * Checks if a prop is used
+     * @param {ASTNode} node The AST node being checked.
+     * @param {Object} prop Declared prop object
+     * @returns {boolean} True if the prop is used, false if not.
+     */
+    function isPropUsed(node, prop) {
+      const usedPropTypes = node.usedPropTypes || [];
+      for (let i = 0, l = usedPropTypes.length; i < l; i++) {
+        const usedProp = usedPropTypes[i];
+        if (
+          prop.type === 'shape'
+          || prop.type === 'exact'
+          || prop.name === '__ANY_KEY__'
+          || usedProp.name === prop.name
+        ) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    /**
+     * Used to recursively loop through each declared prop type
+     * @param {Object} component The component to process
+     * @param {ASTNode[]|true} props List of props to validate
+     */
+    function reportUnusedPropType(component, props) {
+      // Skip props that check instances
+      if (props === true) {
+        return;
+      }
+
+      Object.keys(props || {}).forEach((key) => {
+        const prop = props[key];
+        // Skip props that check instances
+        if (prop === true) {
+          return;
+        }
+
+        if ((prop.type === 'shape' || prop.type === 'exact') && configuration.skipShapeProps) {
+          return;
+        }
+
+        if (prop.node && prop.node.typeAnnotation && prop.node.typeAnnotation.typeAnnotation
+          && prop.node.typeAnnotation.typeAnnotation.type === 'TSNeverKeyword') {
+          return;
+        }
+
+        if (prop.node && !isIgnored(prop.fullName) && !isPropUsed(component, prop)) {
+          report(context, messages.unusedPropType, 'unusedPropType', {
+            node: prop.node.key || prop.node,
+            data: {
+              name: prop.fullName,
+            },
+          });
+        }
+
+        if (prop.children) {
+          reportUnusedPropType(component, prop.children);
+        }
+      });
+    }
+
+    /**
+     * Reports unused proptypes for a given component
+     * @param {Object} component The component to process
+     */
+    function reportUnusedPropTypes(component) {
+      reportUnusedPropType(component, component.declaredPropTypes);
+    }
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    return {
+      'Program:exit'() {
+        // Report undeclared proptypes for all classes
+        values(components.list())
+          .filter((component) => mustBeValidated(component))
+          .forEach((component) => {
+            reportUnusedPropTypes(component);
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-unused-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-unused-state.d.ts","sourceRoot":"","sources":["no-unused-state.js"],"names":[],"mappings":"wBAgFW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,529 @@
+/**
+ * @fileoverview  Attempts to discover all state fields in a React component and
+ * warn if any of them are never read.
+ *
+ * State field definitions are collected from `this.state = {}` assignments in
+ * the constructor, objects passed to `this.setState()`, and `state = {}` class
+ * property assignments.
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const report = require('../util/report');
+const getScope = require('../util/eslint').getScope;
+
+// Descend through all wrapping TypeCastExpressions and return the expression
+// that was cast.
+function uncast(node) {
+  while (node.type === 'TypeCastExpression') {
+    node = node.expression;
+  }
+  return node;
+}
+
+// Return the name of an identifier or the string value of a literal. Useful
+// anywhere that a literal may be used as a key (e.g., member expressions,
+// method definitions, ObjectExpression property keys).
+function getName(node) {
+  node = uncast(node);
+  const type = node.type;
+
+  if (type === 'Identifier') {
+    return node.name;
+  }
+  if (type === 'Literal') {
+    return String(node.value);
+  }
+  if (type === 'TemplateLiteral' && node.expressions.length === 0) {
+    return node.quasis[0].value.raw;
+  }
+  return null;
+}
+
+function isThisExpression(node) {
+  return astUtil.unwrapTSAsExpression(uncast(node)).type === 'ThisExpression';
+}
+
+function getInitialClassInfo() {
+  return {
+    // Set of nodes where state fields were defined.
+    stateFields: new Set(),
+
+    // Set of names of state fields that we've seen used.
+    usedStateFields: new Set(),
+
+    // Names of local variables that may be pointing to this.state. To
+    // track this properly, we would need to keep track of all locals,
+    // shadowing, assignments, etc. To keep things simple, we only
+    // maintain one set of aliases per method and accept that it will
+    // produce some false negatives.
+    aliases: null,
+  };
+}
+
+function isSetStateCall(node) {
+  const unwrappedCalleeNode = astUtil.unwrapTSAsExpression(node.callee);
+
+  return (
+    unwrappedCalleeNode.type === 'MemberExpression'
+    && isThisExpression(unwrappedCalleeNode.object)
+    && getName(unwrappedCalleeNode.property) === 'setState'
+  );
+}
+
+const messages = {
+  unusedStateField: 'Unused state field: \'{{name}}\'',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow definitions of unused state',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('no-unused-state'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    // Non-null when we are inside a React component ClassDeclaration and we have
+    // not yet encountered any use of this.state which we have chosen not to
+    // analyze. If we encounter any such usage (like this.state being spread as
+    // JSX attributes), then this is again set to null.
+    let classInfo = null;
+
+    function isStateParameterReference(node) {
+      const classMethods = [
+        'shouldComponentUpdate',
+        'componentWillUpdate',
+        'UNSAFE_componentWillUpdate',
+        'getSnapshotBeforeUpdate',
+        'componentDidUpdate',
+      ];
+
+      let scope = getScope(context, node);
+      while (scope) {
+        const parent = scope.block && scope.block.parent;
+        if (
+          parent
+          && parent.type === 'MethodDefinition' && (
+            (parent.static && parent.key.name === 'getDerivedStateFromProps')
+            || classMethods.indexOf(parent.key.name) !== -1
+          )
+          && parent.value.type === 'FunctionExpression'
+          && parent.value.params[1]
+          && parent.value.params[1].name === node.name
+        ) {
+          return true;
+        }
+        scope = scope.upper;
+      }
+
+      return false;
+    }
+
+    // Returns true if the given node is possibly a reference to `this.state` or the state parameter of
+    // a lifecycle method.
+    function isStateReference(node) {
+      node = uncast(node);
+
+      const isDirectStateReference = node.type === 'MemberExpression'
+        && isThisExpression(node.object)
+        && node.property.name === 'state';
+
+      const isAliasedStateReference = node.type === 'Identifier'
+        && classInfo.aliases
+        && classInfo.aliases.has(node.name);
+
+      return isDirectStateReference || isAliasedStateReference || isStateParameterReference(node);
+    }
+
+    // Takes an ObjectExpression node and adds all named Property nodes to the
+    // current set of state fields.
+    function addStateFields(node) {
+      node.properties.filter((prop) => (
+        prop.type === 'Property'
+          && (prop.key.type === 'Literal'
+          || (prop.key.type === 'TemplateLiteral' && prop.key.expressions.length === 0)
+          || (prop.computed === false && prop.key.type === 'Identifier'))
+          && getName(prop.key) !== null
+      )).forEach((prop) => {
+        classInfo.stateFields.add(prop);
+      });
+    }
+
+    // Adds the name of the given node as a used state field if the node is an
+    // Identifier or a Literal. Other node types are ignored.
+    function addUsedStateField(node) {
+      if (!classInfo) {
+        return;
+      }
+      const name = getName(node);
+      if (name) {
+        classInfo.usedStateFields.add(name);
+      }
+    }
+
+    // Records used state fields and new aliases for an ObjectPattern which
+    // destructures `this.state`.
+    function handleStateDestructuring(node) {
+      node.properties.forEach((prop) => {
+        if (prop.type === 'Property') {
+          addUsedStateField(prop.key);
+        } else if (
+          (prop.type === 'ExperimentalRestProperty' || prop.type === 'RestElement')
+          && classInfo.aliases
+        ) {
+          classInfo.aliases.add(getName(prop.argument));
+        }
+      });
+    }
+
+    // Used to record used state fields and new aliases for both
+    // AssignmentExpressions and VariableDeclarators.
+    function handleAssignment(left, right) {
+      const unwrappedRight = astUtil.unwrapTSAsExpression(right);
+
+      switch (left.type) {
+        case 'Identifier':
+          if (isStateReference(unwrappedRight) && classInfo.aliases) {
+            classInfo.aliases.add(left.name);
+          }
+          break;
+        case 'ObjectPattern':
+          if (isStateReference(unwrappedRight)) {
+            handleStateDestructuring(left);
+          } else if (isThisExpression(unwrappedRight) && classInfo.aliases) {
+            left.properties.forEach((prop) => {
+              if (prop.type === 'Property' && getName(prop.key) === 'state') {
+                const name = getName(prop.value);
+                if (name) {
+                  classInfo.aliases.add(name);
+                } else if (prop.value.type === 'ObjectPattern') {
+                  handleStateDestructuring(prop.value);
+                }
+              }
+            });
+          }
+          break;
+        default:
+        // pass
+      }
+    }
+
+    function reportUnusedFields() {
+      // Report all unused state fields.
+      classInfo.stateFields.forEach((node) => {
+        const name = getName(node.key);
+        if (!classInfo.usedStateFields.has(name)) {
+          report(context, messages.unusedStateField, 'unusedStateField', {
+            node,
+            data: {
+              name,
+            },
+          });
+        }
+      });
+    }
+
+    function handleES6ComponentEnter(node) {
+      if (componentUtil.isES6Component(node, context)) {
+        classInfo = getInitialClassInfo();
+      }
+    }
+
+    function handleES6ComponentExit() {
+      if (!classInfo) {
+        return;
+      }
+      reportUnusedFields();
+      classInfo = null;
+    }
+
+    function isGDSFP(node) {
+      const name = getName(node.key);
+      if (
+        !node.static
+        || name !== 'getDerivedStateFromProps'
+        || !node.value
+        || !node.value.params
+        || node.value.params.length < 2 // no `state` argument
+      ) {
+        return false;
+      }
+      return true;
+    }
+
+    return {
+      ClassDeclaration: handleES6ComponentEnter,
+
+      'ClassDeclaration:exit': handleES6ComponentExit,
+
+      ClassExpression: handleES6ComponentEnter,
+
+      'ClassExpression:exit': handleES6ComponentExit,
+
+      ObjectExpression(node) {
+        if (componentUtil.isES5Component(node, context)) {
+          classInfo = getInitialClassInfo();
+        }
+      },
+
+      'ObjectExpression:exit'(node) {
+        if (!classInfo) {
+          return;
+        }
+
+        if (componentUtil.isES5Component(node, context)) {
+          reportUnusedFields();
+          classInfo = null;
+        }
+      },
+
+      CallExpression(node) {
+        if (!classInfo) {
+          return;
+        }
+
+        const unwrappedNode = astUtil.unwrapTSAsExpression(node);
+        const unwrappedArgumentNode = astUtil.unwrapTSAsExpression(unwrappedNode.arguments[0]);
+
+        // If we're looking at a `this.setState({})` invocation, record all the
+        // properties as state fields.
+        if (
+          isSetStateCall(unwrappedNode)
+          && unwrappedNode.arguments.length > 0
+          && unwrappedArgumentNode.type === 'ObjectExpression'
+        ) {
+          addStateFields(unwrappedArgumentNode);
+        } else if (
+          isSetStateCall(unwrappedNode)
+          && unwrappedNode.arguments.length > 0
+          && unwrappedArgumentNode.type === 'ArrowFunctionExpression'
+        ) {
+          const unwrappedBodyNode = astUtil.unwrapTSAsExpression(unwrappedArgumentNode.body);
+
+          if (unwrappedBodyNode.type === 'ObjectExpression') {
+            addStateFields(unwrappedBodyNode);
+          }
+          if (unwrappedArgumentNode.params.length > 0 && classInfo.aliases) {
+            const firstParam = unwrappedArgumentNode.params[0];
+            if (firstParam.type === 'ObjectPattern') {
+              handleStateDestructuring(firstParam);
+            } else {
+              classInfo.aliases.add(getName(firstParam));
+            }
+          }
+        }
+      },
+
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!classInfo) {
+          return;
+        }
+        // If we see state being assigned as a class property using an object
+        // expression, record all the fields of that object as state fields.
+        const unwrappedValueNode = astUtil.unwrapTSAsExpression(node.value);
+
+        const name = getName(node.key);
+        if (
+          name === 'state'
+          && !node.static
+          && unwrappedValueNode
+          && unwrappedValueNode.type === 'ObjectExpression'
+        ) {
+          addStateFields(unwrappedValueNode);
+        }
+
+        if (
+          !node.static
+          && unwrappedValueNode
+          && unwrappedValueNode.type === 'ArrowFunctionExpression'
+        ) {
+          // Create a new set for this.state aliases local to this method.
+          classInfo.aliases = new Set();
+        }
+      },
+
+      'ClassProperty:exit'(node) {
+        if (
+          classInfo
+          && !node.static
+          && node.value
+          && node.value.type === 'ArrowFunctionExpression'
+        ) {
+          // Forget our set of local aliases.
+          classInfo.aliases = null;
+        }
+      },
+
+      'PropertyDefinition, ClassProperty'(node) {
+        if (!isGDSFP(node)) {
+          return;
+        }
+
+        const childScope = getScope(context, node).childScopes.find((x) => x.block === node.value);
+        if (!childScope) {
+          return;
+        }
+        const scope = childScope.variableScope.childScopes.find((x) => x.block === node.value);
+        const stateArg = node.value.params[1]; // probably "state"
+        if (!scope || !scope.variables) {
+          return;
+        }
+        const argVar = scope.variables.find((x) => x.name === stateArg.name);
+
+        if (argVar) {
+          const stateRefs = argVar.references;
+
+          stateRefs.forEach((ref) => {
+            const identifier = ref.identifier;
+            if (identifier && identifier.parent && identifier.parent.type === 'MemberExpression') {
+              addUsedStateField(identifier.parent.property);
+            }
+          });
+        }
+      },
+
+      'PropertyDefinition:exit'(node) {
+        if (
+          classInfo
+          && !node.static
+          && node.value
+          && node.value.type === 'ArrowFunctionExpression'
+          && !isGDSFP(node)
+        ) {
+          // Forget our set of local aliases.
+          classInfo.aliases = null;
+        }
+      },
+
+      MethodDefinition() {
+        if (!classInfo) {
+          return;
+        }
+        // Create a new set for this.state aliases local to this method.
+        classInfo.aliases = new Set();
+      },
+
+      'MethodDefinition:exit'() {
+        if (!classInfo) {
+          return;
+        }
+        // Forget our set of local aliases.
+        classInfo.aliases = null;
+      },
+
+      FunctionExpression(node) {
+        if (!classInfo) {
+          return;
+        }
+
+        const parent = node.parent;
+        if (!componentUtil.isES5Component(parent.parent, context)) {
+          return;
+        }
+
+        if (
+          'key' in parent
+          && 'name' in parent.key
+          && parent.key.name === 'getInitialState'
+        ) {
+          const body = node.body.body;
+          const lastBodyNode = body[body.length - 1];
+
+          if (
+            lastBodyNode.type === 'ReturnStatement'
+            && lastBodyNode.argument.type === 'ObjectExpression'
+          ) {
+            addStateFields(lastBodyNode.argument);
+          }
+        } else {
+          // Create a new set for this.state aliases local to this method.
+          classInfo.aliases = new Set();
+        }
+      },
+
+      AssignmentExpression(node) {
+        if (!classInfo) {
+          return;
+        }
+
+        const unwrappedLeft = astUtil.unwrapTSAsExpression(node.left);
+        const unwrappedRight = astUtil.unwrapTSAsExpression(node.right);
+
+        // Check for assignments like `this.state = {}`
+        if (
+          unwrappedLeft.type === 'MemberExpression'
+          && isThisExpression(unwrappedLeft.object)
+          && getName(unwrappedLeft.property) === 'state'
+          && unwrappedRight.type === 'ObjectExpression'
+        ) {
+          // Find the nearest function expression containing this assignment.
+          /** @type {import('eslint').Rule.Node} */
+          let fn = node;
+          while (fn.type !== 'FunctionExpression' && fn.parent) {
+            fn = fn.parent;
+          }
+          // If the nearest containing function is the constructor, then we want
+          // to record all the assigned properties as state fields.
+          if (
+            fn.parent
+            && fn.parent.type === 'MethodDefinition'
+            && fn.parent.kind === 'constructor'
+          ) {
+            addStateFields(unwrappedRight);
+          }
+        } else {
+          // Check for assignments like `alias = this.state` and record the alias.
+          handleAssignment(unwrappedLeft, unwrappedRight);
+        }
+      },
+
+      VariableDeclarator(node) {
+        if (!classInfo || !node.init) {
+          return;
+        }
+        handleAssignment(node.id, node.init);
+      },
+
+      'MemberExpression, OptionalMemberExpression'(node) {
+        if (!classInfo) {
+          return;
+        }
+        if (isStateReference(astUtil.unwrapTSAsExpression(node.object))) {
+          // If we see this.state[foo] access, give up.
+          if (node.computed && node.property.type !== 'Literal') {
+            classInfo = null;
+            return;
+          }
+          // Otherwise, record that we saw this property being accessed.
+          addUsedStateField(node.property);
+        // If we see a `this.state` access in a CallExpression, give up.
+        } else if (isStateReference(node) && astUtil.isCallExpression(node.parent)) {
+          classInfo = null;
+        }
+      },
+
+      JSXSpreadAttribute(node) {
+        if (classInfo && isStateReference(node.argument)) {
+          classInfo = null;
+        }
+      },
+
+      'ExperimentalSpreadProperty, SpreadElement'(node) {
+        if (classInfo && isStateReference(node.argument)) {
+          classInfo = null;
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=no-will-update-set-state.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"no-will-update-set-state.d.ts","sourceRoot":"","sources":["no-will-update-set-state.js"],"names":[],"mappings":"wBAUW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,15 @@
+/**
+ * @fileoverview Prevent usage of setState in componentWillUpdate
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule');
+const testReactVersion = require('../util/version').testReactVersion;
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = makeNoMethodSetStateRule(
+  'componentWillUpdate',
+  (context) => testReactVersion(context, '>= 16.3.0')
+);
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=prefer-es6-class.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"prefer-es6-class.d.ts","sourceRoot":"","sources":["prefer-es6-class.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,58 @@
+/**
+ * @fileoverview Enforce ES5 or ES6 class for React Components
+ * @author Dan Hamilton
+ */
+
+'use strict';
+
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  shouldUseES6Class: 'Component should use es6 class instead of createClass',
+  shouldUseCreateClass: 'Component should use createClass instead of es6 class',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce ES5 or ES6 class for React Components',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('prefer-es6-class'),
+    },
+
+    messages,
+
+    schema: [{
+      enum: ['always', 'never'],
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || 'always';
+
+    return {
+      ObjectExpression(node) {
+        if (componentUtil.isES5Component(node, context) && configuration === 'always') {
+          report(context, messages.shouldUseES6Class, 'shouldUseES6Class', {
+            node,
+          });
+        }
+      },
+      ClassDeclaration(node) {
+        if (componentUtil.isES6Component(node, context) && configuration === 'never') {
+          report(context, messages.shouldUseCreateClass, 'shouldUseCreateClass', {
+            node,
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=prefer-exact-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"prefer-exact-props.d.ts","sourceRoot":"","sources":["prefer-exact-props.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,162 @@
+/**
+ * @fileoverview Prefer exact proptype definitions
+ */
+
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const propsUtil = require('../util/props');
+const propWrapperUtil = require('../util/propWrapper');
+const variableUtil = require('../util/variable');
+const report = require('../util/report');
+const getText = require('../util/eslint').getText;
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+const messages = {
+  propTypes: 'Component propTypes should be exact by using {{exactPropWrappers}}.',
+  flow: 'Component flow props should be set with exact objects.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Prefer exact proptype definitions',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('prefer-exact-props'),
+    },
+    messages,
+    schema: [],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const typeAliases = {};
+    const exactWrappers = propWrapperUtil.getExactPropWrapperFunctions(context);
+
+    function getPropTypesErrorMessage() {
+      const formattedWrappers = propWrapperUtil.formatPropWrapperFunctions(exactWrappers);
+      const message = exactWrappers.size > 1 ? `one of ${formattedWrappers}` : formattedWrappers;
+      return { exactPropWrappers: message };
+    }
+
+    function isNonExactObjectTypeAnnotation(node) {
+      return (
+        node
+        && node.type === 'ObjectTypeAnnotation'
+        && node.properties.length > 0
+        && !node.exact
+      );
+    }
+
+    function hasNonExactObjectTypeAnnotation(node) {
+      const typeAnnotation = node.typeAnnotation;
+      return (
+        typeAnnotation
+        && typeAnnotation.typeAnnotation
+        && isNonExactObjectTypeAnnotation(typeAnnotation.typeAnnotation)
+      );
+    }
+
+    function hasGenericTypeAnnotation(node) {
+      const typeAnnotation = node.typeAnnotation;
+      return (
+        typeAnnotation
+        && typeAnnotation.typeAnnotation
+        && typeAnnotation.typeAnnotation.type === 'GenericTypeAnnotation'
+      );
+    }
+
+    function isNonEmptyObjectExpression(node) {
+      return (
+        node
+        && node.type === 'ObjectExpression'
+        && node.properties.length > 0
+      );
+    }
+
+    function isNonExactPropWrapperFunction(node) {
+      return (
+        astUtil.isCallExpression(node)
+        && !propWrapperUtil.isExactPropWrapperFunction(context, getText(context, node.callee))
+      );
+    }
+
+    function reportPropTypesError(node) {
+      report(context, messages.propTypes, 'propTypes', {
+        node,
+        data: getPropTypesErrorMessage(),
+      });
+    }
+
+    function reportFlowError(node) {
+      report(context, messages.flow, 'flow', {
+        node,
+      });
+    }
+
+    return {
+      TypeAlias(node) {
+        // working around an issue with eslint@3 and babel-eslint not finding the TypeAlias in scope
+        typeAliases[node.id.name] = node;
+      },
+
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!propsUtil.isPropTypesDeclaration(node)) {
+          return;
+        }
+
+        if (hasNonExactObjectTypeAnnotation(node)) {
+          reportFlowError(node);
+        } else if (exactWrappers.size > 0 && isNonEmptyObjectExpression(node.value)) {
+          reportPropTypesError(node);
+        } else if (exactWrappers.size > 0 && isNonExactPropWrapperFunction(node.value)) {
+          reportPropTypesError(node);
+        }
+      },
+
+      Identifier(node) {
+        if (!utils.getStatelessComponent(node.parent)) {
+          return;
+        }
+
+        if (hasNonExactObjectTypeAnnotation(node)) {
+          reportFlowError(node);
+        } else if (hasGenericTypeAnnotation(node)) {
+          const identifier = node.typeAnnotation.typeAnnotation.id.name;
+          const typeAlias = typeAliases[identifier];
+          const propsDefinition = typeAlias ? typeAlias.right : null;
+          if (isNonExactObjectTypeAnnotation(propsDefinition)) {
+            reportFlowError(node);
+          }
+        }
+      },
+
+      MemberExpression(node) {
+        if (!propsUtil.isPropTypesDeclaration(node) || exactWrappers.size === 0) {
+          return;
+        }
+
+        const right = node.parent.right;
+        if (isNonEmptyObjectExpression(right)) {
+          reportPropTypesError(node);
+        } else if (isNonExactPropWrapperFunction(right)) {
+          reportPropTypesError(node);
+        } else if (right.type === 'Identifier') {
+          const identifier = right.name;
+          const propsDefinition = variableUtil.findVariableByName(context, node, identifier);
+          if (isNonEmptyObjectExpression(propsDefinition)) {
+            reportPropTypesError(node);
+          } else if (isNonExactPropWrapperFunction(propsDefinition)) {
+            reportPropTypesError(node);
+          }
+        }
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=prefer-read-only-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"prefer-read-only-props.d.ts","sourceRoot":"","sources":["prefer-read-only-props.js"],"names":[],"mappings":"wBAiDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,117 @@
+/**
+ * @fileoverview Require component props to be typed as read-only.
+ * @author Luke Zapart
+ */
+
+'use strict';
+
+const flatMap = require('array.prototype.flatmap');
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+function isFlowPropertyType(node) {
+  return node.type === 'ObjectTypeProperty';
+}
+
+function isTypescriptPropertyType(node) {
+  return node.type === 'TSPropertySignature';
+}
+
+function isCovariant(node) {
+  return (node.variance && node.variance.kind === 'plus')
+    || (
+      node.parent
+      && node.parent.parent
+      && node.parent.parent.parent
+      && node.parent.parent.parent.id
+      && node.parent.parent.parent.id.name === '$ReadOnly'
+    );
+}
+
+function isReadonly(node) {
+  return (
+    node.typeAnnotation
+    && node.typeAnnotation.parent
+    && node.typeAnnotation.parent.readonly
+  );
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  readOnlyProp: 'Prop \'{{name}}\' should be read-only.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce that props are read-only',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('prefer-read-only-props'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [],
+  },
+
+  create: Components.detect((context, components) => {
+    function reportReadOnlyProp(prop, propName, fixer) {
+      report(context, messages.readOnlyProp, 'readOnlyProp', {
+        node: prop.node,
+        data: {
+          name: propName,
+        },
+        fix: fixer,
+      });
+    }
+
+    return {
+      'Program:exit'() {
+        flatMap(
+          values(components.list()),
+          (component) => component.declaredPropTypes || []
+        ).forEach((declaredPropTypes) => {
+          Object.keys(declaredPropTypes).forEach((propName) => {
+            const prop = declaredPropTypes[propName];
+            if (!prop.node) {
+              return;
+            }
+
+            if (isFlowPropertyType(prop.node)) {
+              if (!isCovariant(prop.node)) {
+                reportReadOnlyProp(prop, propName, (fixer) => {
+                  if (!prop.node.variance) {
+                    // Insert covariance
+                    return fixer.insertTextBefore(prop.node, '+');
+                  }
+
+                  // Replace contravariance with covariance
+                  return fixer.replaceText(prop.node.variance, '+');
+                });
+              }
+
+              return;
+            }
+
+            if (isTypescriptPropertyType(prop.node)) {
+              if (!isReadonly(prop.node)) {
+                reportReadOnlyProp(prop, propName, (fixer) => (
+                  fixer.insertTextBefore(prop.node, 'readonly ')
+                ));
+              }
+            }
+          });
+        });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=prefer-stateless-function.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"prefer-stateless-function.d.ts","sourceRoot":"","sources":["prefer-stateless-function.js"],"names":[],"mappings":"wBA8BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,397 @@
+/**
+ * @fileoverview Enforce stateless components to be written as a pure function
+ * @author Yannick Croissant
+ * @author Alberto Rodríguez
+ * @copyright 2015 Alberto Rodríguez. All rights reserved.
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const testReactVersion = require('../util/version').testReactVersion;
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getScope = eslintUtil.getScope;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  componentShouldBePure: 'Component should be written as a pure function',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce stateless components to be written as a pure function',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('prefer-stateless-function'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignorePureComponents: {
+          default: false,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    const configuration = context.options[0] || {};
+    const ignorePureComponents = configuration.ignorePureComponents || false;
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    /**
+     * Checks whether a given array of statements is a single call of `super`.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode[]} body - An array of statements to check.
+     * @returns {boolean} `true` if the body is a single call of `super`.
+     */
+    function isSingleSuperCall(body) {
+      return (
+        body.length === 1
+        && body[0].type === 'ExpressionStatement'
+        && astUtil.isCallExpression(body[0].expression)
+        && body[0].expression.callee.type === 'Super'
+      );
+    }
+
+    /**
+     * Checks whether a given node is a pattern which doesn't have any side effects.
+     * Default parameters and Destructuring parameters can have side effects.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode} node - A pattern node.
+     * @returns {boolean} `true` if the node doesn't have any side effects.
+     */
+    function isSimple(node) {
+      return node.type === 'Identifier' || node.type === 'RestElement';
+    }
+
+    /**
+     * Checks whether a given array of expressions is `...arguments` or not.
+     * `super(...arguments)` passes all arguments through.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode[]} superArgs - An array of expressions to check.
+     * @returns {boolean} `true` if the superArgs is `...arguments`.
+     */
+    function isSpreadArguments(superArgs) {
+      return (
+        superArgs.length === 1
+        && superArgs[0].type === 'SpreadElement'
+        && superArgs[0].argument.type === 'Identifier'
+        && superArgs[0].argument.name === 'arguments'
+      );
+    }
+
+    /**
+     * Checks whether given 2 nodes are identifiers which have the same name or not.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode} ctorParam - A node to check.
+     * @param {ASTNode} superArg - A node to check.
+     * @returns {boolean} `true` if the nodes are identifiers which have the same
+     *      name.
+     */
+    function isValidIdentifierPair(ctorParam, superArg) {
+      return (
+        ctorParam.type === 'Identifier'
+        && superArg.type === 'Identifier'
+        && ctorParam.name === superArg.name
+      );
+    }
+
+    /**
+     * Checks whether given 2 nodes are a rest/spread pair which has the same values.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode} ctorParam - A node to check.
+     * @param {ASTNode} superArg - A node to check.
+     * @returns {boolean} `true` if the nodes are a rest/spread pair which has the
+     *      same values.
+     */
+    function isValidRestSpreadPair(ctorParam, superArg) {
+      return (
+        ctorParam.type === 'RestElement'
+        && superArg.type === 'SpreadElement'
+        && isValidIdentifierPair(ctorParam.argument, superArg.argument)
+      );
+    }
+
+    /**
+     * Checks whether given 2 nodes have the same value or not.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode} ctorParam - A node to check.
+     * @param {ASTNode} superArg - A node to check.
+     * @returns {boolean} `true` if the nodes have the same value or not.
+     */
+    function isValidPair(ctorParam, superArg) {
+      return (
+        isValidIdentifierPair(ctorParam, superArg)
+        || isValidRestSpreadPair(ctorParam, superArg)
+      );
+    }
+
+    /**
+     * Checks whether the parameters of a constructor and the arguments of `super()`
+     * have the same values or not.
+     * @see eslint no-useless-constructor rule
+     * @param {ASTNode[]} ctorParams - The parameters of a constructor to check.
+     * @param {ASTNode} superArgs - The arguments of `super()` to check.
+     * @returns {boolean} `true` if those have the same values.
+     */
+    function isPassingThrough(ctorParams, superArgs) {
+      if (ctorParams.length !== superArgs.length) {
+        return false;
+      }
+
+      for (let i = 0; i < ctorParams.length; ++i) {
+        if (!isValidPair(ctorParams[i], superArgs[i])) {
+          return false;
+        }
+      }
+
+      return true;
+    }
+
+    /**
+     * Checks whether the constructor body is a redundant super call.
+     * @see eslint no-useless-constructor rule
+     * @param {Array} body - constructor body content.
+     * @param {Array} ctorParams - The params to check against super call.
+     * @returns {boolean} true if the constructor body is redundant
+     */
+    function isRedundantSuperCall(body, ctorParams) {
+      return (
+        isSingleSuperCall(body)
+        && ctorParams.every(isSimple)
+        && (
+          isSpreadArguments(body[0].expression.arguments)
+          || isPassingThrough(ctorParams, body[0].expression.arguments)
+        )
+      );
+    }
+
+    /**
+     * Check if a given AST node have any other properties the ones available in stateless components
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if the node has at least one other property, false if not.
+     */
+    function hasOtherProperties(node) {
+      const properties = astUtil.getComponentProperties(node);
+      return properties.some((property) => {
+        const name = astUtil.getPropertyName(property);
+        const isDisplayName = name === 'displayName';
+        const isPropTypes = name === 'propTypes' || ((name === 'props') && property.typeAnnotation);
+        const contextTypes = name === 'contextTypes';
+        const defaultProps = name === 'defaultProps';
+        const isUselessConstructor = property.kind === 'constructor'
+          && !!property.value.body
+          && isRedundantSuperCall(property.value.body.body, property.value.params);
+        const isRender = name === 'render';
+        return !isDisplayName && !isPropTypes && !contextTypes && !defaultProps && !isUselessConstructor && !isRender;
+      });
+    }
+
+    /**
+     * Mark component as pure as declared
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markSCUAsDeclared(node) {
+      components.set(node, {
+        hasSCU: true,
+      });
+    }
+
+    /**
+     * Mark childContextTypes as declared
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markChildContextTypesAsDeclared(node) {
+      components.set(node, {
+        hasChildContextTypes: true,
+      });
+    }
+
+    /**
+     * Mark a setState as used
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markThisAsUsed(node) {
+      components.set(node, {
+        useThis: true,
+      });
+    }
+
+    /**
+     * Mark a props or context as used
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markPropsOrContextAsUsed(node) {
+      components.set(node, {
+        usePropsOrContext: true,
+      });
+    }
+
+    /**
+     * Mark a ref as used
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markRefAsUsed(node) {
+      components.set(node, {
+        useRef: true,
+      });
+    }
+
+    /**
+     * Mark return as invalid
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markReturnAsInvalid(node) {
+      components.set(node, {
+        invalidReturn: true,
+      });
+    }
+
+    /**
+     * Mark a ClassDeclaration as having used decorators
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markDecoratorsAsUsed(node) {
+      components.set(node, {
+        useDecorators: true,
+      });
+    }
+
+    function visitClass(node) {
+      if (ignorePureComponents && componentUtil.isPureComponent(node, context)) {
+        markSCUAsDeclared(node);
+      }
+
+      if (node.decorators && node.decorators.length) {
+        markDecoratorsAsUsed(node);
+      }
+    }
+
+    return {
+      ClassDeclaration: visitClass,
+      ClassExpression: visitClass,
+
+      // Mark `this` destructuring as a usage of `this`
+      VariableDeclarator(node) {
+        // Ignore destructuring on other than `this`
+        if (!node.id || node.id.type !== 'ObjectPattern' || !node.init || node.init.type !== 'ThisExpression') {
+          return;
+        }
+        // Ignore `props` and `context`
+        const useThis = node.id.properties.some((property) => {
+          const name = astUtil.getPropertyName(property);
+          return name !== 'props' && name !== 'context';
+        });
+        if (!useThis) {
+          markPropsOrContextAsUsed(node);
+          return;
+        }
+        markThisAsUsed(node);
+      },
+
+      // Mark `this` usage
+      MemberExpression(node) {
+        if (node.object.type !== 'ThisExpression') {
+          if (node.property && node.property.name === 'childContextTypes') {
+            const component = utils.getRelatedComponent(node);
+            if (!component) {
+              return;
+            }
+            markChildContextTypesAsDeclared(component.node);
+          }
+          return;
+        // Ignore calls to `this.props` and `this.context`
+        }
+        if (
+          (node.property.name || node.property.value) === 'props'
+          || (node.property.name || node.property.value) === 'context'
+        ) {
+          markPropsOrContextAsUsed(node);
+          return;
+        }
+        markThisAsUsed(node);
+      },
+
+      // Mark `ref` usage
+      JSXAttribute(node) {
+        const name = getText(context, node.name);
+        if (name !== 'ref') {
+          return;
+        }
+        markRefAsUsed(node);
+      },
+
+      // Mark `render` that do not return some JSX
+      ReturnStatement(node) {
+        let blockNode;
+        let scope = getScope(context, node);
+        while (scope) {
+          blockNode = scope.block && scope.block.parent;
+          if (blockNode && (blockNode.type === 'MethodDefinition' || blockNode.type === 'Property')) {
+            break;
+          }
+          scope = scope.upper;
+        }
+        const isRender = blockNode
+          && blockNode.key
+          && blockNode.key.name === 'render';
+        const allowNull = testReactVersion(context, '>= 15.0.0'); // Stateless components can return null since React 15
+        const isReturningJSX = utils.isReturningJSX(node, !allowNull);
+        const isReturningNull = node.argument && (node.argument.value === null || node.argument.value === false);
+        if (
+          !isRender
+          || (allowNull && (isReturningJSX || isReturningNull))
+          || (!allowNull && isReturningJSX)
+        ) {
+          return;
+        }
+        markReturnAsInvalid(node);
+      },
+
+      'Program:exit'() {
+        const list = components.list();
+        values(list)
+          .filter((component) => (
+            !hasOtherProperties(component.node)
+            && !component.useThis
+            && !component.useRef
+            && !component.invalidReturn
+            && !component.hasChildContextTypes
+            && !component.useDecorators
+            && !component.hasSCU
+            && (
+              componentUtil.isES5Component(component.node, context)
+              || componentUtil.isES6Component(component.node, context)
+            )
+          ))
+          .forEach((component) => {
+            report(context, messages.componentShouldBePure, 'componentShouldBePure', {
+              node: component.node,
+            });
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=prop-types.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"prop-types.d.ts","sourceRoot":"","sources":["prop-types.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,225 @@
+/**
+ * @fileoverview Prevent missing props validation in a React component definition
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+// As for exceptions for props.children or props.className (and alike) look at
+// https://github.com/jsx-eslint/eslint-plugin-react/issues/7
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  missingPropType: '\'{{name}}\' is missing in props validation',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow missing props validation in a React component definition',
+      category: 'Best Practices',
+      recommended: true,
+      url: docsUrl('prop-types'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignore: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        customValidators: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        skipUndeclared: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components) => {
+    const configuration = context.options[0] || {};
+    const ignored = configuration.ignore || [];
+    const skipUndeclared = configuration.skipUndeclared || false;
+
+    /**
+     * Checks if the prop is ignored
+     * @param {string} name Name of the prop to check.
+     * @returns {boolean} True if the prop is ignored, false if not.
+     */
+    function isIgnored(name) {
+      return ignored.indexOf(name) !== -1;
+    }
+
+    /**
+     * Checks if the component must be validated
+     * @param {Object} component The component to process
+     * @returns {boolean} True if the component must be validated, false if not.
+     */
+    function mustBeValidated(component) {
+      const isSkippedByConfig = skipUndeclared && typeof component.declaredPropTypes === 'undefined';
+      return !!(
+        component
+        && component.usedPropTypes
+        && !component.ignorePropsValidation
+        && !isSkippedByConfig
+      );
+    }
+
+    /**
+     * Internal: Checks if the prop is declared
+     * @param {Object} declaredPropTypes Description of propTypes declared in the current component
+     * @param {string[]} keyList Dot separated name of the prop to check.
+     * @returns {boolean} True if the prop is declared, false if not.
+     */
+    function internalIsDeclaredInComponent(declaredPropTypes, keyList) {
+      for (let i = 0, j = keyList.length; i < j; i++) {
+        const key = keyList[i];
+        const propType = (
+          declaredPropTypes && (
+            // Check if this key is declared
+            (declaredPropTypes[key] // If not, check if this type accepts any key
+            || declaredPropTypes.__ANY_KEY__) // eslint-disable-line no-underscore-dangle
+          )
+        );
+
+        if (!propType) {
+          // If it's a computed property, we can't make any further analysis, but is valid
+          return key === '__COMPUTED_PROP__';
+        }
+        if (typeof propType === 'object' && !propType.type) {
+          return true;
+        }
+        // Consider every children as declared
+        if (propType.children === true || propType.containsUnresolvedSpread || propType.containsIndexers) {
+          return true;
+        }
+        if (propType.acceptedProperties) {
+          return key in propType.acceptedProperties;
+        }
+        if (propType.type === 'union') {
+          // If we fall in this case, we know there is at least one complex type in the union
+          if (i + 1 >= j) {
+            // this is the last key, accept everything
+            return true;
+          }
+          // non trivial, check all of them
+          const unionTypes = propType.children;
+          const unionPropType = {};
+          for (let k = 0, z = unionTypes.length; k < z; k++) {
+            unionPropType[key] = unionTypes[k];
+            const isValid = internalIsDeclaredInComponent(
+              unionPropType,
+              keyList.slice(i)
+            );
+            if (isValid) {
+              return true;
+            }
+          }
+
+          // every possible union were invalid
+          return false;
+        }
+        declaredPropTypes = propType.children;
+      }
+      return true;
+    }
+
+    /**
+     * Checks if the prop is declared
+     * @param {ASTNode} node The AST node being checked.
+     * @param {string[]} names List of names of the prop to check.
+     * @returns {boolean} True if the prop is declared, false if not.
+     */
+    function isDeclaredInComponent(node, names) {
+      while (node) {
+        const component = components.get(node);
+
+        const isDeclared = component && component.confidence >= 2
+          && internalIsDeclaredInComponent(component.declaredPropTypes || {}, names);
+
+        if (isDeclared) {
+          return true;
+        }
+
+        node = node.parent;
+      }
+      return false;
+    }
+
+    /**
+     * Reports undeclared proptypes for a given component
+     * @param {Object} component The component to process
+     */
+    function reportUndeclaredPropTypes(component) {
+      const undeclareds = component.usedPropTypes.filter((propType) => (
+        propType.node
+        && !isIgnored(propType.allNames[0])
+        && !isDeclaredInComponent(component.node, propType.allNames)
+      ));
+      undeclareds.forEach((propType) => {
+        report(context, messages.missingPropType, 'missingPropType', {
+          node: propType.node,
+          data: {
+            name: propType.allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]'),
+          },
+        });
+      });
+    }
+
+    /**
+     * @param {Object} component The current component to process
+     * @param {Array} list The all components to process
+     * @returns {boolean} True if the component is nested False if not.
+     */
+    function checkNestedComponent(component, list) {
+      const componentIsMemo = component.node.callee && component.node.callee.name === 'memo';
+      const argumentIsForwardRef = component.node.arguments && component.node.arguments[0].callee && component.node.arguments[0].callee.name === 'forwardRef';
+      if (componentIsMemo && argumentIsForwardRef) {
+        const forwardComponent = list.find(
+          (innerComponent) => (
+            innerComponent.node.range[0] === component.node.arguments[0].range[0]
+            && innerComponent.node.range[0] === component.node.arguments[0].range[0]
+          ));
+
+        const isValidated = mustBeValidated(forwardComponent);
+        const isIgnorePropsValidation = forwardComponent.ignorePropsValidation;
+
+        return isIgnorePropsValidation || isValidated;
+      }
+    }
+
+    return {
+      'Program:exit'() {
+        const list = components.list();
+        // Report undeclared proptypes for all classes
+        values(list)
+          .filter((component) => mustBeValidated(component))
+          .forEach((component) => {
+            if (checkNestedComponent(component, values(list))) return;
+            reportUndeclaredPropTypes(component);
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=react-in-jsx-scope.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"react-in-jsx-scope.d.ts","sourceRoot":"","sources":["react-in-jsx-scope.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,56 @@
+/**
+ * @fileoverview Prevent missing React when using JSX
+ * @author Glen Mailer
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const pragmaUtil = require('../util/pragma');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+const messages = {
+  notInScope: '\'{{name}}\' must be in scope when using JSX',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow missing React when using JSX',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('react-in-jsx-scope'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create(context) {
+    const pragma = pragmaUtil.getFromContext(context);
+
+    function checkIfReactIsInScope(node) {
+      if (variableUtil.getVariableFromContext(context, node, pragma)) {
+        return;
+      }
+      report(context, messages.notInScope, 'notInScope', {
+        node,
+        data: {
+          name: pragma,
+        },
+      });
+    }
+
+    return {
+      JSXOpeningElement: checkIfReactIsInScope,
+      JSXOpeningFragment: checkIfReactIsInScope,
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=require-default-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"require-default-props.d.ts","sourceRoot":"","sources":["require-default-props.js"],"names":[],"mappings":"wBAiCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,209 @@
+/**
+ * @fileOverview Enforce a defaultProps definition for every prop that is not a required prop.
+ * @author Vitor Balocco
+ */
+
+'use strict';
+
+const entries = require('object.entries');
+const values = require('object.values');
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noDefaultWithRequired: 'propType "{{name}}" is required and should not have a defaultProps declaration.',
+  shouldHaveDefault: 'propType "{{name}}" is not required, but has no corresponding defaultProps declaration.',
+  noDefaultPropsWithFunction: 'Don’t use defaultProps with function components.',
+  shouldAssignObjectDefault: 'propType "{{name}}" is not required, but has no corresponding default argument value.',
+  destructureInSignature: 'Must destructure props in the function signature to initialize an optional prop.',
+};
+
+function isPropWithNoDefaulVal(prop) {
+  if (prop.type === 'RestElement' || prop.type === 'ExperimentalRestProperty') {
+    return false;
+  }
+  return prop.value.type !== 'AssignmentPattern';
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce a defaultProps definition for every prop that is not a required prop',
+      category: 'Best Practices',
+      url: docsUrl('require-default-props'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        forbidDefaultForRequired: {
+          type: 'boolean',
+        },
+        classes: {
+          enum: ['defaultProps', 'ignore'],
+        },
+        functions: {
+          enum: ['defaultArguments', 'defaultProps', 'ignore'],
+        },
+        /**
+         * @deprecated
+         */
+        ignoreFunctionalComponents: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components) => {
+    const configuration = context.options[0] || {};
+    const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false;
+    const classes = configuration.classes || 'defaultProps';
+    /**
+     * @todo
+     * - Remove ignoreFunctionalComponents
+     * - Change default to 'defaultArguments'
+     */
+    const functions = configuration.ignoreFunctionalComponents
+      ? 'ignore'
+      : configuration.functions || 'defaultProps';
+
+    /**
+     * Reports all propTypes passed in that don't have a defaultProps counterpart.
+     * @param  {Object[]} propTypes    List of propTypes to check.
+     * @param  {Object}   defaultProps Object of defaultProps to check. Keys are the props names.
+     * @return {void}
+     */
+    function reportPropTypesWithoutDefault(propTypes, defaultProps) {
+      entries(propTypes).forEach((propType) => {
+        const propName = propType[0];
+        const prop = propType[1];
+
+        if (!prop.node) {
+          return;
+        }
+        if (prop.isRequired) {
+          if (forbidDefaultForRequired && defaultProps[propName]) {
+            report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', {
+              node: prop.node,
+              data: { name: propName },
+            });
+          }
+          return;
+        }
+
+        if (defaultProps[propName]) {
+          return;
+        }
+
+        report(context, messages.shouldHaveDefault, 'shouldHaveDefault', {
+          node: prop.node,
+          data: { name: propName },
+        });
+      });
+    }
+
+    /**
+     * If functions option is 'defaultArguments', reports defaultProps is used and all params that doesn't initialized.
+     * @param {Object} componentNode Node of component.
+     * @param {Object[]} declaredPropTypes List of propTypes to check `isRequired`.
+     * @param {Object} defaultProps Object of defaultProps to check used.
+     */
+    function reportFunctionComponent(componentNode, declaredPropTypes, defaultProps) {
+      if (defaultProps) {
+        report(context, messages.noDefaultPropsWithFunction, 'noDefaultPropsWithFunction', {
+          node: componentNode,
+        });
+      }
+
+      const props = componentNode.params[0];
+      const propTypes = declaredPropTypes;
+
+      if (!props) {
+        return;
+      }
+
+      if (props.type === 'Identifier') {
+        const hasOptionalProp = values(propTypes).some((propType) => !propType.isRequired);
+        if (hasOptionalProp) {
+          report(context, messages.destructureInSignature, 'destructureInSignature', {
+            node: props,
+          });
+        }
+      } else if (props.type === 'ObjectPattern') {
+        // Filter required props with default value and report error
+        props.properties.filter((prop) => {
+          const propName = prop && prop.key && prop.key.name;
+          const isPropRequired = propTypes[propName] && propTypes[propName].isRequired;
+          return propTypes[propName] && isPropRequired && !isPropWithNoDefaulVal(prop);
+        }).forEach((prop) => {
+          report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', {
+            node: prop,
+            data: { name: prop.key.name },
+          });
+        });
+
+        // Filter non required props with no default value and report error
+        props.properties.filter((prop) => {
+          const propName = prop && prop.key && prop.key.name;
+          const isPropRequired = propTypes[propName] && propTypes[propName].isRequired;
+          return propTypes[propName] && !isPropRequired && isPropWithNoDefaulVal(prop);
+        }).forEach((prop) => {
+          report(context, messages.shouldAssignObjectDefault, 'shouldAssignObjectDefault', {
+            node: prop,
+            data: { name: prop.key.name },
+          });
+        });
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public API
+    // --------------------------------------------------------------------------
+
+    return {
+      'Program:exit'() {
+        const list = components.list();
+
+        values(list).filter((component) => {
+          if (functions === 'ignore' && astUtil.isFunctionLike(component.node)) {
+            return false;
+          }
+          if (classes === 'ignore' && astUtil.isClass(component.node)) {
+            return false;
+          }
+
+          // If this defaultProps is "unresolved", then we should ignore this component and not report
+          // any errors for it, to avoid false-positives with e.g. external defaultProps declarations or spread operators.
+          if (component.defaultProps === 'unresolved') {
+            return false;
+          }
+          return component.declaredPropTypes !== undefined;
+        }).forEach((component) => {
+          if (functions === 'defaultArguments' && astUtil.isFunctionLike(component.node)) {
+            reportFunctionComponent(
+              component.node,
+              component.declaredPropTypes,
+              component.defaultProps
+            );
+          } else {
+            reportPropTypesWithoutDefault(
+              component.declaredPropTypes,
+              component.defaultProps || {}
+            );
+          }
+        });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=require-optimization.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"require-optimization.d.ts","sourceRoot":"","sources":["require-optimization.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,240 @@
+/**
+ * @fileoverview Enforce React components to have a shouldComponentUpdate method
+ * @author Evgueni Naverniouk
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getScope = require('../util/eslint').getScope;
+
+const messages = {
+  noShouldComponentUpdate: 'Component is not optimized. Please add a shouldComponentUpdate method.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce React components to have a shouldComponentUpdate method',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('require-optimization'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        allowDecorators: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components) => {
+    const configuration = context.options[0] || {};
+    const allowDecorators = configuration.allowDecorators || [];
+
+    /**
+     * Checks to see if our component is decorated by PureRenderMixin via reactMixin
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if node is decorated with a PureRenderMixin, false if not.
+     */
+    function hasPureRenderDecorator(node) {
+      if (node.decorators && node.decorators.length) {
+        for (let i = 0, l = node.decorators.length; i < l; i++) {
+          if (
+            node.decorators[i].expression
+            && node.decorators[i].expression.callee
+            && node.decorators[i].expression.callee.object
+            && node.decorators[i].expression.callee.object.name === 'reactMixin'
+            && node.decorators[i].expression.callee.property
+            && node.decorators[i].expression.callee.property.name === 'decorate'
+            && node.decorators[i].expression.arguments
+            && node.decorators[i].expression.arguments.length
+            && node.decorators[i].expression.arguments[0].name === 'PureRenderMixin'
+          ) {
+            return true;
+          }
+        }
+      }
+
+      return false;
+    }
+
+    /**
+     * Checks to see if our component is custom decorated
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if node is decorated name with a custom decorated, false if not.
+     */
+    function hasCustomDecorator(node) {
+      const allowLength = allowDecorators.length;
+
+      if (allowLength && node.decorators && node.decorators.length) {
+        for (let i = 0; i < allowLength; i++) {
+          for (let j = 0, l = node.decorators.length; j < l; j++) {
+            const expression = node.decorators[j].expression;
+            if (
+              expression
+              && expression.name === allowDecorators[i]
+            ) {
+              return true;
+            }
+          }
+        }
+      }
+
+      return false;
+    }
+
+    /**
+     * Checks if we are declaring a shouldComponentUpdate method
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if we are declaring a shouldComponentUpdate method, false if not.
+     */
+    function isSCUDeclared(node) {
+      return !!node && node.name === 'shouldComponentUpdate';
+    }
+
+    /**
+     * Checks if we are declaring a PureRenderMixin mixin
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if we are declaring a PureRenderMixin method, false if not.
+     */
+    function isPureRenderDeclared(node) {
+      let hasPR = false;
+      if (node.value && node.value.elements) {
+        for (let i = 0, l = node.value.elements.length; i < l; i++) {
+          if (node.value.elements[i] && node.value.elements[i].name === 'PureRenderMixin') {
+            hasPR = true;
+            break;
+          }
+        }
+      }
+
+      return (
+        !!node
+        && node.key.name === 'mixins'
+        && hasPR
+      );
+    }
+
+    /**
+     * Mark shouldComponentUpdate as declared
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markSCUAsDeclared(node) {
+      components.set(node, {
+        hasSCU: true,
+      });
+    }
+
+    /**
+     * Reports missing optimization for a given component
+     * @param {Object} component The component to process
+     */
+    function reportMissingOptimization(component) {
+      report(context, messages.noShouldComponentUpdate, 'noShouldComponentUpdate', {
+        node: component.node,
+      });
+    }
+
+    /**
+     * Checks if we are declaring function in class
+     * @param {ASTNode} node
+     * @returns {boolean} True if we are declaring function in class, false if not.
+     */
+    function isFunctionInClass(node) {
+      let blockNode;
+      let scope = getScope(context, node);
+      while (scope) {
+        blockNode = scope.block;
+        if (blockNode && blockNode.type === 'ClassDeclaration') {
+          return true;
+        }
+        scope = scope.upper;
+      }
+
+      return false;
+    }
+
+    return {
+      ArrowFunctionExpression(node) {
+        // Skip if the function is declared in the class
+        if (isFunctionInClass(node)) {
+          return;
+        }
+        // Stateless Functional Components cannot be optimized (yet)
+        markSCUAsDeclared(node);
+      },
+
+      ClassDeclaration(node) {
+        if (!(
+          hasPureRenderDecorator(node)
+          || hasCustomDecorator(node)
+          || componentUtil.isPureComponent(node, context)
+        )) {
+          return;
+        }
+        markSCUAsDeclared(node);
+      },
+
+      FunctionDeclaration(node) {
+        // Skip if the function is declared in the class
+        if (isFunctionInClass(node)) {
+          return;
+        }
+        // Stateless Functional Components cannot be optimized (yet)
+        markSCUAsDeclared(node);
+      },
+
+      FunctionExpression(node) {
+        // Skip if the function is declared in the class
+        if (isFunctionInClass(node)) {
+          return;
+        }
+        // Stateless Functional Components cannot be optimized (yet)
+        markSCUAsDeclared(node);
+      },
+
+      MethodDefinition(node) {
+        if (!isSCUDeclared(node.key)) {
+          return;
+        }
+        markSCUAsDeclared(node);
+      },
+
+      ObjectExpression(node) {
+        // Search for the shouldComponentUpdate declaration
+        const found = node.properties.some((property) => (
+          property.key
+          && (isSCUDeclared(property.key) || isPureRenderDeclared(property))
+        ));
+        if (found) {
+          markSCUAsDeclared(node);
+        }
+      },
+
+      'Program:exit'() {
+        // Report missing shouldComponentUpdate for all components
+        values(components.list())
+          .filter((component) => !component.hasSCU)
+          .forEach((component) => {
+            reportMissingOptimization(component);
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=require-render-return.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"require-render-return.d.ts","sourceRoot":"","sources":["require-render-return.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/require-render-return.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,106 @@
+/**
+ * @fileoverview Enforce ES5 or ES6 class for returning value in render function.
+ * @author Mark Orel
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const Components = require('../util/Components');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const getAncestors = require('../util/eslint').getAncestors;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  noRenderReturn: 'Your render method should have a return statement',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce ES5 or ES6 class for returning value in render function',
+      category: 'Possible Errors',
+      recommended: true,
+      url: docsUrl('require-render-return'),
+    },
+
+    messages,
+
+    schema: [],
+  },
+
+  create: Components.detect((context, components) => {
+    /**
+     * Mark a return statement as present
+     * @param {ASTNode} node The AST node being checked.
+     */
+    function markReturnStatementPresent(node) {
+      components.set(node, {
+        hasReturnStatement: true,
+      });
+    }
+
+    /**
+     * Find render method in a given AST node
+     * @param {ASTNode} node The component to find render method.
+     * @returns {ASTNode} Method node if found, undefined if not.
+     */
+    function findRenderMethod(node) {
+      const properties = astUtil.getComponentProperties(node);
+      return properties
+        .filter((property) => astUtil.getPropertyName(property) === 'render' && property.value)
+        .find((property) => astUtil.isFunctionLikeExpression(property.value));
+    }
+
+    return {
+      ReturnStatement(node) {
+        const ancestors = getAncestors(context, node).reverse();
+        let depth = 0;
+        ancestors.forEach((ancestor) => {
+          if (/Function(Expression|Declaration)$/.test(ancestor.type)) {
+            depth += 1;
+          }
+          if (
+            /(MethodDefinition|Property|ClassProperty|PropertyDefinition)$/.test(ancestor.type)
+            && astUtil.getPropertyName(ancestor) === 'render'
+            && depth <= 1
+          ) {
+            markReturnStatementPresent(node);
+          }
+        });
+      },
+
+      ArrowFunctionExpression(node) {
+        if (node.expression === false || astUtil.getPropertyName(node.parent) !== 'render') {
+          return;
+        }
+        markReturnStatementPresent(node);
+      },
+
+      'Program:exit'() {
+        values(components.list())
+          .filter((component) => (
+            findRenderMethod(component.node)
+            && !component.hasReturnStatement
+            && (
+              componentUtil.isES5Component(component.node, context)
+              || componentUtil.isES6Component(component.node, context)
+            )
+          ))
+          .forEach((component) => {
+            report(context, messages.noRenderReturn, 'noRenderReturn', {
+              node: findRenderMethod(component.node),
+            });
+          });
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=self-closing-comp.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"self-closing-comp.d.ts","sourceRoot":"","sources":["self-closing-comp.js"],"names":[],"mappings":"wBA4CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,104 @@
+/**
+ * @fileoverview Prevent extra closing tags for components without children
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+const jsxUtil = require('../util/jsx');
+const report = require('../util/report');
+
+const optionDefaults = { component: true, html: true };
+
+function isComponent(node) {
+  return (
+    node.name
+    && (node.name.type === 'JSXIdentifier' || node.name.type === 'JSXMemberExpression')
+    && !jsxUtil.isDOMComponent(node)
+  );
+}
+
+function childrenIsEmpty(node) {
+  return node.parent.children.length === 0;
+}
+
+function childrenIsMultilineSpaces(node) {
+  const childrens = node.parent.children;
+
+  return (
+    childrens.length === 1
+    && (childrens[0].type === 'Literal' || childrens[0].type === 'JSXText')
+    && childrens[0].value.indexOf('\n') !== -1
+    && childrens[0].value.replace(/(?!\xA0)\s/g, '') === ''
+  );
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  notSelfClosing: 'Empty components are self-closing',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow extra closing tags for components without children',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('self-closing-comp'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        component: {
+          default: optionDefaults.component,
+          type: 'boolean',
+        },
+        html: {
+          default: optionDefaults.html,
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    function isShouldBeSelfClosed(node) {
+      const configuration = Object.assign({}, optionDefaults, context.options[0]);
+      return (
+        (configuration.component && isComponent(node))
+        || (configuration.html && jsxUtil.isDOMComponent(node))
+      ) && !node.selfClosing && (childrenIsEmpty(node) || childrenIsMultilineSpaces(node));
+    }
+
+    return {
+      JSXOpeningElement(node) {
+        if (!isShouldBeSelfClosed(node)) {
+          return;
+        }
+        report(context, messages.notSelfClosing, 'notSelfClosing', {
+          node,
+          fix(fixer) {
+            // Represents the last character of the JSXOpeningElement, the '>' character
+            const openingElementEnding = node.range[1] - 1;
+            // Represents the last character of the JSXClosingElement, the '>' character
+            const closingElementEnding = node.parent.closingElement.range[1];
+
+            // Replace />.*<\/.*>/ with '/>'
+            const range = [openingElementEnding, closingElementEnding];
+            return fixer.replaceTextRange(range, ' />');
+          },
+        });
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=sort-comp.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"sort-comp.d.ts","sourceRoot":"","sources":["sort-comp.js"],"names":[],"mappings":"wBAwFW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-comp.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,449 @@
+/**
+ * @fileoverview Enforce component methods order
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const has = require('hasown');
+const entries = require('object.entries');
+const values = require('object.values');
+const arrayIncludes = require('array-includes');
+
+const Components = require('../util/Components');
+const astUtil = require('../util/ast');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+const defaultConfig = {
+  order: [
+    'static-methods',
+    'lifecycle',
+    'everything-else',
+    'render',
+  ],
+  groups: {
+    lifecycle: [
+      'displayName',
+      'propTypes',
+      'contextTypes',
+      'childContextTypes',
+      'mixins',
+      'statics',
+      'defaultProps',
+      'constructor',
+      'getDefaultProps',
+      'state',
+      'getInitialState',
+      'getChildContext',
+      'getDerivedStateFromProps',
+      'componentWillMount',
+      'UNSAFE_componentWillMount',
+      'componentDidMount',
+      'componentWillReceiveProps',
+      'UNSAFE_componentWillReceiveProps',
+      'shouldComponentUpdate',
+      'componentWillUpdate',
+      'UNSAFE_componentWillUpdate',
+      'getSnapshotBeforeUpdate',
+      'componentDidUpdate',
+      'componentDidCatch',
+      'componentWillUnmount',
+    ],
+  },
+};
+
+/**
+ * Get the methods order from the default config and the user config
+ * @param {Object} userConfig The user configuration.
+ * @returns {Array} Methods order
+ */
+function getMethodsOrder(userConfig) {
+  userConfig = userConfig || {};
+
+  const groups = Object.assign({}, defaultConfig.groups, userConfig.groups);
+  const order = userConfig.order || defaultConfig.order;
+
+  let config = [];
+  let entry;
+  for (let i = 0, j = order.length; i < j; i++) {
+    entry = order[i];
+    if (has(groups, entry)) {
+      config = config.concat(groups[entry]);
+    } else {
+      config.push(entry);
+    }
+  }
+
+  return config;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  unsortedProps: '{{propA}} should be placed {{position}} {{propB}}',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce component methods order',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('sort-comp'),
+    },
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        order: {
+          type: 'array',
+          items: {
+            type: 'string',
+          },
+        },
+        groups: {
+          type: 'object',
+          patternProperties: {
+            '^.*$': {
+              type: 'array',
+              items: {
+                type: 'string',
+              },
+            },
+          },
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create: Components.detect((context, components) => {
+    /** @satisfies {Record<string, { node: ASTNode, score: number, closest: { distance: number, ref: { node: null | ASTNode, index: number } } }>} */
+    const errors = {};
+    const methodsOrder = getMethodsOrder(context.options[0]);
+
+    // --------------------------------------------------------------------------
+    // Public
+    // --------------------------------------------------------------------------
+
+    const regExpRegExp = /\/(.*)\/([gimsuy]*)/;
+
+    /**
+     * Get indexes of the matching patterns in methods order configuration
+     * @param {Object} method - Method metadata.
+     * @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match.
+     */
+    function getRefPropIndexes(method) {
+      const methodGroupIndexes = [];
+
+      methodsOrder.forEach((currentGroup, groupIndex) => {
+        if (currentGroup === 'getters') {
+          if (method.getter) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (currentGroup === 'setters') {
+          if (method.setter) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (currentGroup === 'type-annotations') {
+          if (method.typeAnnotation) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (currentGroup === 'static-variables') {
+          if (method.staticVariable) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (currentGroup === 'static-methods') {
+          if (method.staticMethod) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (currentGroup === 'instance-variables') {
+          if (method.instanceVariable) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (currentGroup === 'instance-methods') {
+          if (method.instanceMethod) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else if (arrayIncludes([
+          'displayName',
+          'propTypes',
+          'contextTypes',
+          'childContextTypes',
+          'mixins',
+          'statics',
+          'defaultProps',
+          'constructor',
+          'getDefaultProps',
+          'state',
+          'getInitialState',
+          'getChildContext',
+          'getDerivedStateFromProps',
+          'componentWillMount',
+          'UNSAFE_componentWillMount',
+          'componentDidMount',
+          'componentWillReceiveProps',
+          'UNSAFE_componentWillReceiveProps',
+          'shouldComponentUpdate',
+          'componentWillUpdate',
+          'UNSAFE_componentWillUpdate',
+          'getSnapshotBeforeUpdate',
+          'componentDidUpdate',
+          'componentDidCatch',
+          'componentWillUnmount',
+          'render',
+        ], currentGroup)) {
+          if (currentGroup === method.name) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        } else {
+          // Is the group a regex?
+          const isRegExp = currentGroup.match(regExpRegExp);
+          if (isRegExp) {
+            const isMatching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name);
+            if (isMatching) {
+              methodGroupIndexes.push(groupIndex);
+            }
+          } else if (currentGroup === method.name) {
+            methodGroupIndexes.push(groupIndex);
+          }
+        }
+      });
+
+      // No matching pattern, return 'everything-else' index
+      if (methodGroupIndexes.length === 0) {
+        const everythingElseIndex = methodsOrder.indexOf('everything-else');
+
+        if (everythingElseIndex !== -1) {
+          methodGroupIndexes.push(everythingElseIndex);
+        } else {
+          // No matching pattern and no 'everything-else' group
+          methodGroupIndexes.push(Infinity);
+        }
+      }
+
+      return methodGroupIndexes;
+    }
+
+    /**
+     * Get properties name
+     * @param {Object} node - Property.
+     * @returns {string} Property name.
+     */
+    function getPropertyName(node) {
+      if (node.kind === 'get') {
+        return 'getter functions';
+      }
+
+      if (node.kind === 'set') {
+        return 'setter functions';
+      }
+
+      return astUtil.getPropertyName(node);
+    }
+
+    /**
+     * Store a new error in the error list
+     * @param {Object} propA - Mispositioned property.
+     * @param {Object} propB - Reference property.
+     */
+    function storeError(propA, propB) {
+      // Initialize the error object if needed
+      if (!errors[propA.index]) {
+        errors[propA.index] = {
+          node: propA.node,
+          score: 0,
+          closest: {
+            distance: Infinity,
+            ref: {
+              node: null,
+              index: 0,
+            },
+          },
+        };
+      }
+      // Increment the prop score
+      errors[propA.index].score += 1;
+      // Stop here if we already have pushed another node at this position
+      if (getPropertyName(errors[propA.index].node) !== getPropertyName(propA.node)) {
+        return;
+      }
+      // Stop here if we already have a closer reference
+      if (Math.abs(propA.index - propB.index) > errors[propA.index].closest.distance) {
+        return;
+      }
+      // Update the closest reference
+      errors[propA.index].closest.distance = Math.abs(propA.index - propB.index);
+      errors[propA.index].closest.ref.node = propB.node;
+      errors[propA.index].closest.ref.index = propB.index;
+    }
+
+    /**
+     * Dedupe errors, only keep the ones with the highest score and delete the others
+     */
+    function dedupeErrors() {
+      entries(errors).forEach((entry) => {
+        const i = entry[0];
+        const error = entry[1];
+
+        const index = error.closest.ref.index;
+        if (errors[index]) {
+          if (error.score > errors[index].score) {
+            delete errors[index];
+          } else {
+            delete errors[i];
+          }
+        }
+      });
+    }
+
+    /**
+     * Report errors
+     */
+    function reportErrors() {
+      dedupeErrors();
+
+      entries(errors).forEach((entry) => {
+        const nodeA = entry[1].node;
+        const nodeB = entry[1].closest.ref.node;
+        const indexA = entry[0];
+        const indexB = entry[1].closest.ref.index;
+
+        report(context, messages.unsortedProps, 'unsortedProps', {
+          node: nodeA,
+          data: {
+            propA: getPropertyName(nodeA),
+            propB: getPropertyName(nodeB),
+            position: indexA < indexB ? 'before' : 'after',
+          },
+        });
+      });
+    }
+
+    /**
+     * Compare two properties and find out if they are in the right order
+     * @param {Array} propertiesInfos Array containing all the properties metadata.
+     * @param {Object} propA First property name and metadata
+     * @param {Object} propB Second property name.
+     * @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties.
+     */
+    function comparePropsOrder(propertiesInfos, propA, propB) {
+      let i;
+      let j;
+      let k;
+      let l;
+      let refIndexA;
+      let refIndexB;
+
+      // Get references indexes (the correct position) for given properties
+      const refIndexesA = getRefPropIndexes(propA);
+      const refIndexesB = getRefPropIndexes(propB);
+
+      // Get current indexes for given properties
+      const classIndexA = propertiesInfos.indexOf(propA);
+      const classIndexB = propertiesInfos.indexOf(propB);
+
+      // Loop around the references indexes for the 1st property
+      for (i = 0, j = refIndexesA.length; i < j; i++) {
+        refIndexA = refIndexesA[i];
+
+        // Loop around the properties for the 2nd property (for comparison)
+        for (k = 0, l = refIndexesB.length; k < l; k++) {
+          refIndexB = refIndexesB[k];
+
+          if (
+            // Comparing the same properties
+            refIndexA === refIndexB
+            // 1st property is placed before the 2nd one in reference and in current component
+            || ((refIndexA < refIndexB) && (classIndexA < classIndexB))
+            // 1st property is placed after the 2nd one in reference and in current component
+            || ((refIndexA > refIndexB) && (classIndexA > classIndexB))
+          ) {
+            return {
+              correct: true,
+              indexA: classIndexA,
+              indexB: classIndexB,
+            };
+          }
+        }
+      }
+
+      // We did not find any correct match between reference and current component
+      return {
+        correct: false,
+        indexA: refIndexA,
+        indexB: refIndexB,
+      };
+    }
+
+    /**
+     * Check properties order from a properties list and store the eventual errors
+     * @param {Array} properties Array containing all the properties.
+     */
+    function checkPropsOrder(properties) {
+      const propertiesInfos = properties.map((node) => ({
+        name: getPropertyName(node),
+        getter: node.kind === 'get',
+        setter: node.kind === 'set',
+        staticVariable: node.static
+          && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')
+          && (!node.value || !astUtil.isFunctionLikeExpression(node.value)),
+        staticMethod: node.static
+          && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition' || node.type === 'MethodDefinition')
+          && node.value
+          && (astUtil.isFunctionLikeExpression(node.value)),
+        instanceVariable: !node.static
+          && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')
+          && (!node.value || !astUtil.isFunctionLikeExpression(node.value)),
+        instanceMethod: !node.static
+          && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')
+          && node.value
+          && (astUtil.isFunctionLikeExpression(node.value)),
+        typeAnnotation: !!node.typeAnnotation && node.value === null,
+      }));
+
+      // Loop around the properties
+      propertiesInfos.forEach((propA, i) => {
+        // Loop around the properties a second time (for comparison)
+        propertiesInfos.forEach((propB, k) => {
+          if (i === k) {
+            return;
+          }
+
+          // Compare the properties order
+          const order = comparePropsOrder(propertiesInfos, propA, propB);
+
+          if (!order.correct) {
+            // Store an error if the order is incorrect
+            storeError({
+              node: properties[i],
+              index: order.indexA,
+            }, {
+              node: properties[k],
+              index: order.indexB,
+            });
+          }
+        });
+      });
+    }
+
+    return {
+      'Program:exit'() {
+        values(components.list()).forEach((component) => {
+          const properties = astUtil.getComponentProperties(component.node);
+          checkPropsOrder(properties);
+        });
+
+        reportErrors();
+      },
+    };
+  }),
+
+  defaultConfig,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=sort-default-props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"sort-default-props.d.ts","sourceRoot":"","sources":["sort-default-props.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-default-props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,180 @@
+/**
+ * @fileoverview Enforce default props alphabetical sorting
+ * @author Vladimir Kattsov
+ * @deprecated
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getFirstTokens = eslintUtil.getFirstTokens;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  propsNotSorted: 'Default prop types declarations should be sorted alphabetically',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce defaultProps declarations alphabetical sorting',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('sort-default-props'),
+    },
+    // fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        ignoreCase: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const ignoreCase = configuration.ignoreCase || false;
+
+    /**
+     * Get properties name
+     * @param {Object} node - Property.
+     * @returns {string} Property name.
+     */
+    function getPropertyName(node) {
+      if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
+        return node.key.name;
+      }
+      if (node.type === 'MemberExpression') {
+        return node.property.name;
+      // Special case for class properties
+      // (babel-eslint@5 does not expose property name so we have to rely on tokens)
+      }
+      if (node.type === 'ClassProperty') {
+        const tokens = getFirstTokens(context, node, 2);
+        return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
+      }
+      return '';
+    }
+
+    /**
+     * Checks if the Identifier node passed in looks like a defaultProps declaration.
+     * @param   {ASTNode}  node The node to check. Must be an Identifier node.
+     * @returns {boolean}       `true` if the node is a defaultProps declaration, `false` if not
+     */
+    function isDefaultPropsDeclaration(node) {
+      const propName = getPropertyName(node);
+      return (propName === 'defaultProps' || propName === 'getDefaultProps');
+    }
+
+    function getKey(node) {
+      return getText(context, node.key || node.argument);
+    }
+
+    /**
+     * Find a variable by name in the current scope.
+     * @param  {ASTNode} node The node to look for.
+     * @param  {string} name Name of the variable to look for.
+     * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+     */
+    function findVariableByName(node, name) {
+      const variable = variableUtil.getVariableFromContext(context, node, name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+        return null;
+      }
+
+      if (variable.defs[0].node.type === 'TypeAlias') {
+        return variable.defs[0].node.right;
+      }
+
+      return variable.defs[0].node.init;
+    }
+
+    /**
+     * Checks if defaultProps declarations are sorted
+     * @param {Array} declarations The array of AST nodes being checked.
+     * @returns {void}
+     */
+    function checkSorted(declarations) {
+      // function fix(fixer) {
+      //   return propTypesSortUtil.fixPropTypesSort(context, fixer, declarations, ignoreCase);
+      // }
+
+      declarations.reduce((prev, curr, idx, decls) => {
+        if (/Spread(?:Property|Element)$/.test(curr.type)) {
+          return decls[idx + 1];
+        }
+
+        let prevPropName = getKey(prev);
+        let currentPropName = getKey(curr);
+
+        if (ignoreCase) {
+          prevPropName = prevPropName.toLowerCase();
+          currentPropName = currentPropName.toLowerCase();
+        }
+
+        if (currentPropName < prevPropName) {
+          report(context, messages.propsNotSorted, 'propsNotSorted', {
+            node: curr,
+            // fix
+          });
+
+          return prev;
+        }
+
+        return curr;
+      }, declarations[0]);
+    }
+
+    function checkNode(node) {
+      if (!node) {
+        return;
+      }
+      if (node.type === 'ObjectExpression') {
+        checkSorted(node.properties);
+      } else if (node.type === 'Identifier') {
+        const propTypesObject = findVariableByName(node, node.name);
+        if (propTypesObject && propTypesObject.properties) {
+          checkSorted(propTypesObject.properties);
+        }
+      }
+    }
+
+    // --------------------------------------------------------------------------
+    // Public API
+    // --------------------------------------------------------------------------
+
+    return {
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!isDefaultPropsDeclaration(node)) {
+          return;
+        }
+
+        checkNode(node.value);
+      },
+
+      MemberExpression(node) {
+        if (!isDefaultPropsDeclaration(node)) {
+          return;
+        }
+
+        checkNode('right' in node.parent && node.parent.right);
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=sort-prop-types.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"sort-prop-types.d.ts","sourceRoot":"","sources":["sort-prop-types.js"],"names":[],"mappings":"wBAsCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,316 @@
+/**
+ * @fileoverview Enforce propTypes declarations alphabetical sorting
+ */
+
+'use strict';
+
+const astUtil = require('../util/ast');
+const variableUtil = require('../util/variable');
+const propsUtil = require('../util/props');
+const docsUrl = require('../util/docsUrl');
+const propWrapperUtil = require('../util/propWrapper');
+const propTypesSortUtil = require('../util/propTypesSort');
+const report = require('../util/report');
+const eslintUtil = require('../util/eslint');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  requiredPropsFirst: 'Required prop types must be listed before all other prop types',
+  callbackPropsLast: 'Callback prop types must be listed after all other prop types',
+  propsNotSorted: 'Prop types declarations should be sorted alphabetically',
+};
+
+function getKey(context, node) {
+  if (node.type === 'ObjectTypeProperty') {
+    return getSourceCode(context).getFirstToken(node).value;
+  }
+  if (node.key && node.key.value) {
+    return node.key.value;
+  }
+  return getText(context, node.key || node.argument);
+}
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce propTypes declarations alphabetical sorting',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('sort-prop-types'),
+    },
+    fixable: 'code',
+
+    messages,
+
+    schema: [{
+      type: 'object',
+      properties: {
+        requiredFirst: {
+          type: 'boolean',
+        },
+        callbacksLast: {
+          type: 'boolean',
+        },
+        ignoreCase: {
+          type: 'boolean',
+        },
+        // Whether alphabetical sorting should be enforced
+        noSortAlphabetically: {
+          type: 'boolean',
+        },
+        sortShapeProp: {
+          type: 'boolean',
+        },
+        checkTypes: {
+          type: 'boolean',
+        },
+      },
+      additionalProperties: false,
+    }],
+  },
+
+  create(context) {
+    const configuration = context.options[0] || {};
+    const requiredFirst = configuration.requiredFirst || false;
+    const callbacksLast = configuration.callbacksLast || false;
+    const ignoreCase = configuration.ignoreCase || false;
+    const noSortAlphabetically = configuration.noSortAlphabetically || false;
+    const sortShapeProp = configuration.sortShapeProp || false;
+    const checkTypes = configuration.checkTypes || false;
+
+    const typeAnnotations = new Map();
+
+    /**
+     * Checks if propTypes declarations are sorted
+     * @param {Array} declarations The array of AST nodes being checked.
+     * @returns {void}
+     */
+    function checkSorted(declarations) {
+      // Declarations will be `undefined` if the `shape` is not a literal. For
+      // example, if it is a propType imported from another file.
+      if (!declarations) {
+        return;
+      }
+
+      function fix(fixer) {
+        return propTypesSortUtil.fixPropTypesSort(
+          context,
+          fixer,
+          declarations,
+          ignoreCase,
+          requiredFirst,
+          callbacksLast,
+          noSortAlphabetically,
+          sortShapeProp,
+          checkTypes
+        );
+      }
+
+      const callbackPropsLastSeen = new WeakSet();
+      const requiredPropsFirstSeen = new WeakSet();
+      const propsNotSortedSeen = new WeakSet();
+
+      declarations.reduce((prev, curr, idx, decls) => {
+        if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') {
+          return decls[idx + 1];
+        }
+
+        let prevPropName = getKey(context, prev);
+        let currentPropName = getKey(context, curr);
+        const previousIsRequired = propTypesSortUtil.isRequiredProp(prev);
+        const currentIsRequired = propTypesSortUtil.isRequiredProp(curr);
+        const previousIsCallback = propTypesSortUtil.isCallbackPropName(prevPropName);
+        const currentIsCallback = propTypesSortUtil.isCallbackPropName(currentPropName);
+
+        if (ignoreCase) {
+          prevPropName = String(prevPropName).toLowerCase();
+          currentPropName = String(currentPropName).toLowerCase();
+        }
+
+        if (requiredFirst) {
+          if (previousIsRequired && !currentIsRequired) {
+            // Transition between required and non-required. Don't compare for alphabetical.
+            return curr;
+          }
+          if (!previousIsRequired && currentIsRequired) {
+            // Encountered a non-required prop after a required prop
+            if (!requiredPropsFirstSeen.has(curr)) {
+              requiredPropsFirstSeen.add(curr);
+              report(context, messages.requiredPropsFirst, 'requiredPropsFirst', {
+                node: curr,
+                fix,
+              });
+            }
+            return curr;
+          }
+        }
+
+        if (callbacksLast) {
+          if (!previousIsCallback && currentIsCallback) {
+            // Entering the callback prop section
+            return curr;
+          }
+          if (previousIsCallback && !currentIsCallback) {
+            // Encountered a non-callback prop after a callback prop
+            if (!callbackPropsLastSeen.has(prev)) {
+              callbackPropsLastSeen.add(prev);
+              report(context, messages.callbackPropsLast, 'callbackPropsLast', {
+                node: prev,
+                fix,
+              });
+            }
+            return prev;
+          }
+        }
+
+        if (!noSortAlphabetically && currentPropName < prevPropName) {
+          if (!propsNotSortedSeen.has(curr)) {
+            propsNotSortedSeen.add(curr);
+            report(context, messages.propsNotSorted, 'propsNotSorted', {
+              node: curr,
+              fix,
+            });
+          }
+          return prev;
+        }
+
+        return curr;
+      }, declarations[0]);
+    }
+
+    function checkNode(node) {
+      if (!node) {
+        return;
+      }
+
+      if (node.type === 'ObjectExpression') {
+        checkSorted(node.properties);
+      } else if (node.type === 'Identifier') {
+        const propTypesObject = variableUtil.findVariableByName(context, node, node.name);
+        if (propTypesObject && propTypesObject.properties) {
+          checkSorted(propTypesObject.properties);
+        }
+      } else if (astUtil.isCallExpression(node)) {
+        const innerNode = node.arguments && node.arguments[0];
+        if (propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && innerNode) {
+          checkNode(innerNode);
+        }
+      }
+    }
+
+    function handleFunctionComponent(node) {
+      const firstArg = node.params
+        && node.params.length > 0
+        && node.params[0].typeAnnotation
+        && node.params[0].typeAnnotation.typeAnnotation;
+      if (firstArg && firstArg.type === 'TSTypeReference') {
+        const propType = typeAnnotations.get(firstArg.typeName.name)
+          && typeAnnotations.get(firstArg.typeName.name)[0];
+        if (propType && propType.members) {
+          checkSorted(propType.members);
+        }
+      } else if (firstArg && firstArg.type === 'TSTypeLiteral') {
+        if (firstArg.members) {
+          checkSorted(firstArg.members);
+        }
+      } else if (firstArg && firstArg.type === 'GenericTypeAnnotation') {
+        const propType = typeAnnotations.get(firstArg.id.name)
+          && typeAnnotations.get(firstArg.id.name)[0];
+        if (propType && propType.properties) {
+          checkSorted(propType.properties);
+        }
+      } else if (firstArg && firstArg.type === 'ObjectTypeAnnotation') {
+        if (firstArg.properties) {
+          checkSorted(firstArg.properties);
+        }
+      }
+    }
+
+    return Object.assign({
+      CallExpression(node) {
+        if (!sortShapeProp || !propTypesSortUtil.isShapeProp(node) || !(node.arguments && node.arguments[0])) {
+          return;
+        }
+
+        const firstArg = node.arguments[0];
+        if (firstArg.properties) {
+          checkSorted(firstArg.properties);
+        } else if (firstArg.type === 'Identifier') {
+          const variable = variableUtil.findVariableByName(context, node, firstArg.name);
+          if (variable && variable.properties) {
+            checkSorted(variable.properties);
+          }
+        }
+      },
+
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!propsUtil.isPropTypesDeclaration(node)) {
+          return;
+        }
+        checkNode(node.value);
+      },
+
+      MemberExpression(node) {
+        if (!propsUtil.isPropTypesDeclaration(node)) {
+          return;
+        }
+
+        checkNode(node.parent.right);
+      },
+
+      ObjectExpression(node) {
+        node.properties.forEach((property) => {
+          if (!property.key) {
+            return;
+          }
+
+          if (!propsUtil.isPropTypesDeclaration(property)) {
+            return;
+          }
+          if (property.value.type === 'ObjectExpression') {
+            checkSorted(property.value.properties);
+          }
+        });
+      },
+    }, checkTypes ? {
+      TSTypeLiteral(node) {
+        if (node && node.parent.id) {
+          const currentNode = [].concat(
+            typeAnnotations.get(node.parent.id.name) || [],
+            node
+          );
+          typeAnnotations.set(node.parent.id.name, currentNode);
+        }
+      },
+
+      TypeAlias(node) {
+        if (node.right.type === 'ObjectTypeAnnotation') {
+          const currentNode = [].concat(
+            typeAnnotations.get(node.id.name) || [],
+            node.right
+          );
+          typeAnnotations.set(node.id.name, currentNode);
+        }
+      },
+
+      TSTypeAliasDeclaration(node) {
+        if (node.typeAnnotation.type === 'TSTypeLiteral' || node.typeAnnotation.type === 'ObjectTypeAnnotation') {
+          const currentNode = [].concat(
+            typeAnnotations.get(node.id.name) || [],
+            node.typeAnnotation
+          );
+          typeAnnotations.set(node.id.name, currentNode);
+        }
+      },
+      FunctionDeclaration: handleFunctionComponent,
+      ArrowFunctionExpression: handleFunctionComponent,
+    } : null);
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=state-in-constructor.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"state-in-constructor.d.ts","sourceRoot":"","sources":["state-in-constructor.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,68 @@
+/**
+ * @fileoverview Enforce the state initialization style to be either in a constructor or with a class property
+ * @author Kanitkorn Sujautra
+ */
+
+'use strict';
+
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  stateInitConstructor: 'State initialization should be in a constructor',
+  stateInitClassProp: 'State initialization should be in a class property',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce class component state initialization style',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('state-in-constructor'),
+    },
+
+    messages,
+
+    schema: [{
+      enum: ['always', 'never'],
+    }],
+  },
+
+  create(context) {
+    const option = context.options[0] || 'always';
+    return {
+      'ClassProperty, PropertyDefinition'(node) {
+        if (
+          option === 'always'
+          && !node.static
+          && node.key.name === 'state'
+          && componentUtil.getParentES6Component(context, node)
+        ) {
+          report(context, messages.stateInitConstructor, 'stateInitConstructor', {
+            node,
+          });
+        }
+      },
+      AssignmentExpression(node) {
+        if (
+          option === 'never'
+          && componentUtil.isStateMemberExpression(node.left)
+          && astUtil.inConstructor(context, node)
+          && componentUtil.getParentES6Component(context, node)
+        ) {
+          report(context, messages.stateInitClassProp, 'stateInitClassProp', {
+            node,
+          });
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=static-property-placement.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"static-property-placement.d.ts","sourceRoot":"","sources":["static-property-placement.js"],"names":[],"mappings":"wBA0DW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,194 @@
+/**
+ * @fileoverview Defines where React component static properties should be positioned.
+ * @author Daniel Mason
+ */
+
+'use strict';
+
+const fromEntries = require('object.fromentries');
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const componentUtil = require('../util/componentUtil');
+const propsUtil = require('../util/props');
+const report = require('../util/report');
+const getScope = require('../util/eslint').getScope;
+
+// ------------------------------------------------------------------------------
+// Positioning Options
+// ------------------------------------------------------------------------------
+const STATIC_PUBLIC_FIELD = 'static public field';
+const STATIC_GETTER = 'static getter';
+const PROPERTY_ASSIGNMENT = 'property assignment';
+const POSITION_SETTINGS = [STATIC_PUBLIC_FIELD, STATIC_GETTER, PROPERTY_ASSIGNMENT];
+
+// ------------------------------------------------------------------------------
+// Rule messages
+// ------------------------------------------------------------------------------
+const ERROR_MESSAGES = {
+  [STATIC_PUBLIC_FIELD]: 'notStaticClassProp',
+  [STATIC_GETTER]: 'notGetterClassFunc',
+  [PROPERTY_ASSIGNMENT]: 'declareOutsideClass',
+};
+
+// ------------------------------------------------------------------------------
+// Properties to check
+// ------------------------------------------------------------------------------
+const propertiesToCheck = {
+  propTypes: propsUtil.isPropTypesDeclaration,
+  defaultProps: propsUtil.isDefaultPropsDeclaration,
+  childContextTypes: propsUtil.isChildContextTypesDeclaration,
+  contextTypes: propsUtil.isContextTypesDeclaration,
+  contextType: propsUtil.isContextTypeDeclaration,
+  displayName: (node) => propsUtil.isDisplayNameDeclaration(astUtil.getPropertyNameNode(node)),
+};
+
+const classProperties = Object.keys(propertiesToCheck);
+const schemaProperties = fromEntries(classProperties.map((property) => [property, { enum: POSITION_SETTINGS }]));
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  notStaticClassProp: '\'{{name}}\' should be declared as a static class property.',
+  notGetterClassFunc: '\'{{name}}\' should be declared as a static getter class function.',
+  declareOutsideClass: '\'{{name}}\' should be declared outside the class body.',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforces where React component static properties should be positioned.',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('static-property-placement'),
+    },
+    fixable: null, // or 'code' or 'whitespace'
+
+    messages,
+
+    schema: [
+      { enum: POSITION_SETTINGS },
+      {
+        type: 'object',
+        properties: schemaProperties,
+        additionalProperties: false,
+      },
+    ],
+  },
+
+  create: Components.detect((context, components, utils) => {
+    // variables should be defined here
+    const options = context.options;
+    const defaultCheckType = options[0] || STATIC_PUBLIC_FIELD;
+    const hasAdditionalConfig = options.length > 1;
+    const additionalConfig = hasAdditionalConfig ? options[1] : {};
+
+    // Set config
+    const config = fromEntries(classProperties.map((property) => [
+      property,
+      additionalConfig[property] || defaultCheckType,
+    ]));
+
+    // ----------------------------------------------------------------------
+    // Helpers
+    // ----------------------------------------------------------------------
+
+    /**
+      * Checks if we are declaring context in class
+      * @param {ASTNode} node
+      * @returns {boolean} True if we are declaring context in class, false if not.
+     */
+    function isContextInClass(node) {
+      let blockNode;
+      let scope = getScope(context, node);
+      while (scope) {
+        blockNode = scope.block;
+        if (blockNode && blockNode.type === 'ClassDeclaration') {
+          return true;
+        }
+        scope = scope.upper;
+      }
+
+      return false;
+    }
+
+    /**
+     * Check if we should report this property node
+     * @param {ASTNode} node
+     * @param {string} expectedRule
+     */
+    function reportNodeIncorrectlyPositioned(node, expectedRule) {
+      // Detect if this node is an expected property declaration adn return the property name
+      const name = classProperties.find((propertyName) => {
+        if (propertiesToCheck[propertyName](node)) {
+          return !!propertyName;
+        }
+
+        return false;
+      });
+
+      // If name is set but the configured rule does not match expected then report error
+      if (
+        name
+        && (
+          config[name] !== expectedRule
+          || (!node.static && (config[name] === STATIC_PUBLIC_FIELD || config[name] === STATIC_GETTER))
+        )
+      ) {
+        const messageId = ERROR_MESSAGES[config[name]];
+        report(context, messages[messageId], messageId, {
+          node,
+          data: { name },
+        });
+      }
+    }
+
+    // ----------------------------------------------------------------------
+    // Public
+    // ----------------------------------------------------------------------
+    return {
+      'ClassProperty, PropertyDefinition'(node) {
+        if (!componentUtil.getParentES6Component(context, node)) {
+          return;
+        }
+
+        reportNodeIncorrectlyPositioned(node, STATIC_PUBLIC_FIELD);
+      },
+
+      MemberExpression(node) {
+        // If definition type is undefined then it must not be a defining expression or if the definition is inside a
+        // class body then skip this node.
+        const right = node.parent.right;
+        if (!right || right.type === 'undefined' || isContextInClass(node)) {
+          return;
+        }
+
+        // Get the related component
+        const relatedComponent = utils.getRelatedComponent(node);
+
+        // If the related component is not an ES6 component then skip this node
+        if (!relatedComponent || !componentUtil.isES6Component(relatedComponent.node, context)) {
+          return;
+        }
+
+        // Report if needed
+        reportNodeIncorrectlyPositioned(node, PROPERTY_ASSIGNMENT);
+      },
+
+      MethodDefinition(node) {
+        // If the function is inside a class and is static getter then check if correctly positioned
+        if (
+          componentUtil.getParentES6Component(context, node)
+          && node.static
+          && node.kind === 'get'
+        ) {
+          // Report error if needed
+          reportNodeIncorrectlyPositioned(node, STATIC_GETTER);
+        }
+      },
+    };
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=style-prop-object.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"style-prop-object.d.ts","sourceRoot":"","sources":["style-prop-object.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/style-prop-object.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,145 @@
+/**
+ * @fileoverview Enforce style prop value is an object
+ * @author David Petersen
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+  stylePropNotObject: 'Style prop value must be an object',
+};
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Enforce style prop value is an object',
+      category: 'Possible Errors',
+      recommended: false,
+      url: docsUrl('style-prop-object'),
+    },
+
+    messages,
+
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          allow: {
+            type: 'array',
+            items: {
+              type: 'string',
+            },
+            additionalItems: false,
+            uniqueItems: true,
+          },
+        },
+      },
+    ],
+  },
+
+  create(context) {
+    const allowed = new Set(((context.options.length > 0) && context.options[0].allow) || []);
+
+    /**
+     * @param {ASTNode} expression An Identifier node
+     * @returns {boolean}
+     */
+    function isNonNullaryLiteral(expression) {
+      return expression.type === 'Literal' && expression.value !== null;
+    }
+
+    /**
+     * @param {object} node A Identifier node
+     */
+    function checkIdentifiers(node) {
+      const variable = variableUtil.getVariableFromContext(context, node, node.name);
+
+      if (!variable || !variable.defs[0] || !variable.defs[0].node.init) {
+        return;
+      }
+
+      if (isNonNullaryLiteral(variable.defs[0].node.init)) {
+        report(context, messages.stylePropNotObject, 'stylePropNotObject', {
+          node,
+        });
+      }
+    }
+
+    return {
+      CallExpression(node) {
+        if (
+          isCreateElement(context, node)
+          && node.arguments.length > 1
+        ) {
+          if ('name' in node.arguments[0] && node.arguments[0].name) {
+            // store name of component
+            const componentName = node.arguments[0].name;
+
+            // allowed list contains the name
+            if (allowed.has(componentName)) {
+              // abort operation
+              return;
+            }
+          }
+          if (node.arguments[1].type === 'ObjectExpression') {
+            const style = node.arguments[1].properties.find((property) => (
+              'key' in property
+              && property.key
+              && 'name' in property.key
+              && property.key.name === 'style'
+              && !property.computed
+            ));
+
+            if (style && 'value' in style) {
+              if (style.value.type === 'Identifier') {
+                checkIdentifiers(style.value);
+              } else if (isNonNullaryLiteral(style.value)) {
+                report(context, messages.stylePropNotObject, 'stylePropNotObject', {
+                  node: style.value,
+                });
+              }
+            }
+          }
+        }
+      },
+
+      JSXAttribute(node) {
+        if (!node.value || node.name.name !== 'style') {
+          return;
+        }
+        // store parent element
+        const parentElement = node.parent;
+
+        // parent element is a JSXOpeningElement
+        if (parentElement && parentElement.type === 'JSXOpeningElement') {
+          // get the name of the JSX element
+          const name = parentElement.name && parentElement.name.name;
+
+          // allowed list contains the name
+          if (allowed.has(name)) {
+            // abort operation
+            return;
+          }
+        }
+
+        if (node.value.type !== 'JSXExpressionContainer' || isNonNullaryLiteral(node.value.expression)) {
+          report(context, messages.stylePropNotObject, 'stylePropNotObject', {
+            node,
+          });
+        } else if (node.value.expression.type === 'Identifier') {
+          checkIdentifiers(node.value.expression);
+        }
+      },
+    };
+  },
+};
Index: frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare const _exports: import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=void-dom-elements-no-children.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"void-dom-elements-no-children.d.ts","sourceRoot":"","sources":["void-dom-elements-no-children.js"],"names":[],"mappings":"wBAiDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"}
Index: frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,166 @@
+/**
+ * @fileoverview Prevent void elements (e.g. <img />, <br />) from receiving
+ *   children
+ * @author Joe Lencioni
+ */
+
+'use strict';
+
+const has = require('hasown');
+
+const docsUrl = require('../util/docsUrl');
+const isCreateElement = require('../util/isCreateElement');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+// Using an object here to avoid array scan. We should switch to Set once
+// support is good enough.
+const VOID_DOM_ELEMENTS = {
+  area: true,
+  base: true,
+  br: true,
+  col: true,
+  embed: true,
+  hr: true,
+  img: true,
+  input: true,
+  keygen: true,
+  link: true,
+  menuitem: true,
+  meta: true,
+  param: true,
+  source: true,
+  track: true,
+  wbr: true,
+};
+
+function isVoidDOMElement(elementName) {
+  return has(VOID_DOM_ELEMENTS, elementName);
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const noChildrenInVoidEl = 'Void DOM element <{{element}} /> cannot receive children.';
+
+/** @type {import('eslint').Rule.RuleModule} */
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow void DOM elements (e.g. `<img />`, `<br />`) from receiving children',
+      category: 'Best Practices',
+      recommended: false,
+      url: docsUrl('void-dom-elements-no-children'),
+    },
+
+    messages: {
+      noChildrenInVoidEl,
+    },
+
+    schema: [],
+  },
+
+  create: (context) => ({
+    JSXElement(node) {
+      const elementName = node.openingElement.name.name;
+
+      if (!isVoidDOMElement(elementName)) {
+        // e.g. <div />
+        return;
+      }
+
+      if (node.children.length > 0) {
+        // e.g. <br>Foo</br>
+        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
+          node,
+          data: {
+            element: elementName,
+          },
+        });
+      }
+
+      const attributes = node.openingElement.attributes;
+
+      const hasChildrenAttributeOrDanger = attributes.some((attribute) => {
+        if (!attribute.name) {
+          return false;
+        }
+
+        return attribute.name.name === 'children' || attribute.name.name === 'dangerouslySetInnerHTML';
+      });
+
+      if (hasChildrenAttributeOrDanger) {
+        // e.g. <br children="Foo" />
+        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
+          node,
+          data: {
+            element: elementName,
+          },
+        });
+      }
+    },
+
+    CallExpression(node) {
+      if (node.callee.type !== 'MemberExpression' && node.callee.type !== 'Identifier') {
+        return;
+      }
+
+      if (!isCreateElement(context, node)) {
+        return;
+      }
+
+      const args = node.arguments;
+
+      if (args.length < 1) {
+        // React.createElement() should not crash linter
+        return;
+      }
+
+      const elementName = 'value' in args[0] ? args[0].value : undefined;
+
+      if (!isVoidDOMElement(elementName)) {
+        // e.g. React.createElement('div');
+        return;
+      }
+
+      if (args.length < 2 || args[1].type !== 'ObjectExpression') {
+        return;
+      }
+
+      const firstChild = args[2];
+      if (firstChild) {
+        // e.g. React.createElement('br', undefined, 'Foo')
+        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
+          node,
+          data: {
+            element: elementName,
+          },
+        });
+      }
+
+      const props = args[1].properties;
+
+      const hasChildrenPropOrDanger = props.some((prop) => {
+        if (!('key' in prop) || !prop.key || !('name' in prop.key)) {
+          return false;
+        }
+
+        return prop.key.name === 'children' || prop.key.name === 'dangerouslySetInnerHTML';
+      });
+
+      if (hasChildrenPropOrDanger) {
+        // e.g. React.createElement('br', { children: 'Foo' })
+        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
+          node,
+          data: {
+            element: elementName,
+          },
+        });
+      }
+    },
+  }),
+};
Index: frontend/node_modules/eslint-plugin-react/lib/types.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/types.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,29 @@
+import eslint from 'eslint';
+import estree from 'estree';
+
+declare global {
+  interface ASTNode extends estree.BaseNode {
+    [_: string]: any; // TODO: fixme
+  }
+  type Scope = eslint.Scope.Scope;
+  type Token = eslint.AST.Token;
+  type Fixer = eslint.Rule.RuleFixer;
+  type JSXAttribute = ASTNode;
+  type JSXElement = ASTNode;
+  type JSXFragment = ASTNode;
+  type JSXOpeningElement = ASTNode;
+  type JSXSpreadAttribute = ASTNode;
+
+  type Context = eslint.Rule.RuleContext;
+
+  type TypeDeclarationBuilder = (annotation: ASTNode, parentName: string, seen: Set<typeof annotation>) => object;
+
+  type TypeDeclarationBuilders = {
+    [k in string]: TypeDeclarationBuilder;
+  };
+
+  type UnionTypeDefinition = {
+    type: 'union' | 'shape';
+    children: unknown[];
+  };
+}
Index: frontend/node_modules/eslint-plugin-react/lib/util/Components.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/Components.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/Components.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,76 @@
+declare const _exports: typeof Components & {
+    detect(rule: any): (context?: any) => {
+        [_: string]: Function;
+    };
+};
+export = _exports;
+/**
+ * Components
+ */
+declare class Components {
+    /**
+     * Add a node to the components list, or update it if it's already in the list
+     *
+     * @param {ASTNode} node The AST node being added.
+     * @param {number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes)
+     * @returns {Object} Added component object
+     */
+    add(node: ASTNode, confidence: number): any;
+    /**
+     * Find a component in the list using its node
+     *
+     * @param {ASTNode} node The AST node being searched.
+     * @returns {Object} Component object, undefined if the component is not found or has confidence value of 0.
+     */
+    get(node: ASTNode): any;
+    /**
+     * Update a component in the list
+     *
+     * @param {ASTNode} node The AST node being updated.
+     * @param {Object} props Additional properties to add to the component.
+     */
+    set(node: ASTNode, props: any): void;
+    /**
+     * Return the components list
+     * Components for which we are not confident are not returned
+     *
+     * @returns {Object} Components list
+     */
+    list(): any;
+    /**
+     * Return the length of the components list
+     * Components for which we are not confident are not counted
+     *
+     * @returns {number} Components list length
+     */
+    length(): number;
+    /**
+     * Return the node naming the default React import
+     * It can be used to determine the local name of import, even if it's imported
+     * with an unusual name.
+     *
+     * @returns {ASTNode} React default import node
+     */
+    getDefaultReactImports(): ASTNode;
+    /**
+     * Return the nodes of all React named imports
+     *
+     * @returns {Object} The list of React named imports
+     */
+    getNamedReactImports(): any;
+    /**
+     * Add the default React import specifier to the scope
+     *
+     * @param {ASTNode} specifier The AST Node of the default React import
+     * @returns {void}
+     */
+    addDefaultReactImport(specifier: ASTNode): void;
+    /**
+     * Add a named React import specifier to the scope
+     *
+     * @param {ASTNode} specifier The AST Node of a named React import
+     * @returns {void}
+     */
+    addNamedReactImport(specifier: ASTNode): void;
+}
+//# sourceMappingURL=Components.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/Components.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/Components.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/Components.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"Components.d.ts","sourceRoot":"","sources":["Components.js"],"names":[],"mappings":";;;;;;AA2DA;;GAEG;AACH;IAME;;;;;;OAMG;IACH,UAJW,OAAO,cACP,MAAM,OAmBhB;IAED;;;;;OAKG;IACH,UAHW,OAAO,OAUjB;IAED;;;;;OAKG;IACH,UAHW,OAAO,oBAwBjB;IAED;;;;;OAKG;IACH,YAoCC;IAED;;;;;OAKG;IACH,UAFa,MAAM,CAKlB;IAED;;;;;;OAMG;IACH,0BAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,4BAEC;IAED;;;;;OAKG;IACH,iCAHW,OAAO,GACL,IAAI,CAOhB;IAED;;;;;OAKG;IACH,+BAHW,OAAO,GACL,IAAI,CAOhB;CACF"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/Components.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/Components.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/Components.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,959 @@
+/**
+ * @fileoverview Utility class and functions for React components detection
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const arrayIncludes = require('array-includes');
+const fromEntries = require('object.fromentries');
+const values = require('object.values');
+const iterFrom = require('es-iterator-helpers/Iterator.from');
+const map = require('es-iterator-helpers/Iterator.prototype.map');
+
+const variableUtil = require('./variable');
+const pragmaUtil = require('./pragma');
+const astUtil = require('./ast');
+const componentUtil = require('./componentUtil');
+const propTypesUtil = require('./propTypes');
+const jsxUtil = require('./jsx');
+const usedPropTypesUtil = require('./usedPropTypes');
+const defaultPropsUtil = require('./defaultProps');
+const isFirstLetterCapitalized = require('./isFirstLetterCapitalized');
+const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport');
+const eslintUtil = require('./eslint');
+
+const getScope = eslintUtil.getScope;
+const getText = eslintUtil.getText;
+
+function getId(node) {
+  return node ? `${node.range[0]}:${node.range[1]}` : '';
+}
+
+function usedPropTypesAreEquivalent(propA, propB) {
+  if (propA.name === propB.name) {
+    if (!propA.allNames && !propB.allNames) {
+      return true;
+    }
+    if (Array.isArray(propA.allNames) && Array.isArray(propB.allNames) && propA.allNames.join('') === propB.allNames.join('')) {
+      return true;
+    }
+    return false;
+  }
+  return false;
+}
+
+function mergeUsedPropTypes(propsList, newPropsList) {
+  const propsToAdd = newPropsList.filter((newProp) => {
+    const newPropIsAlreadyInTheList = propsList.some((prop) => usedPropTypesAreEquivalent(prop, newProp));
+    return !newPropIsAlreadyInTheList;
+  });
+
+  return propsList.concat(propsToAdd);
+}
+
+const USE_HOOK_PREFIX_REGEX = /^use[A-Z]/;
+
+const Lists = new WeakMap();
+const ReactImports = new WeakMap();
+
+/**
+ * Components
+ */
+class Components {
+  constructor() {
+    Lists.set(this, {});
+    ReactImports.set(this, {});
+  }
+
+  /**
+   * Add a node to the components list, or update it if it's already in the list
+   *
+   * @param {ASTNode} node The AST node being added.
+   * @param {number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes)
+   * @returns {Object} Added component object
+   */
+  add(node, confidence) {
+    const id = getId(node);
+    const list = Lists.get(this);
+    if (list[id]) {
+      if (confidence === 0 || list[id].confidence === 0) {
+        list[id].confidence = 0;
+      } else {
+        list[id].confidence = Math.max(list[id].confidence, confidence);
+      }
+      return list[id];
+    }
+    list[id] = {
+      node,
+      confidence,
+    };
+    return list[id];
+  }
+
+  /**
+   * Find a component in the list using its node
+   *
+   * @param {ASTNode} node The AST node being searched.
+   * @returns {Object} Component object, undefined if the component is not found or has confidence value of 0.
+   */
+  get(node) {
+    const id = getId(node);
+    const item = Lists.get(this)[id];
+    if (item && item.confidence >= 1) {
+      return item;
+    }
+    return null;
+  }
+
+  /**
+   * Update a component in the list
+   *
+   * @param {ASTNode} node The AST node being updated.
+   * @param {Object} props Additional properties to add to the component.
+   */
+  set(node, props) {
+    const list = Lists.get(this);
+    let component = list[getId(node)];
+    while (!component || component.confidence < 1) {
+      node = node.parent;
+      if (!node) {
+        return;
+      }
+      component = list[getId(node)];
+    }
+
+    Object.assign(
+      component,
+      props,
+      {
+        usedPropTypes: mergeUsedPropTypes(
+          component.usedPropTypes || [],
+          props.usedPropTypes || []
+        ),
+      }
+    );
+  }
+
+  /**
+   * Return the components list
+   * Components for which we are not confident are not returned
+   *
+   * @returns {Object} Components list
+   */
+  list() {
+    const thisList = Lists.get(this);
+    const list = {};
+    const usedPropTypes = {};
+
+    // Find props used in components for which we are not confident
+    Object.keys(thisList).filter((i) => thisList[i].confidence < 2).forEach((i) => {
+      let component = null;
+      let node = null;
+      node = thisList[i].node;
+      while (!component && node.parent) {
+        node = node.parent;
+        // Stop moving up if we reach a decorator
+        if (node.type === 'Decorator') {
+          break;
+        }
+        component = this.get(node);
+      }
+      if (component) {
+        const newUsedProps = (thisList[i].usedPropTypes || []).filter((propType) => !propType.node || propType.node.kind !== 'init');
+
+        const componentId = getId(component.node);
+
+        usedPropTypes[componentId] = mergeUsedPropTypes(usedPropTypes[componentId] || [], newUsedProps);
+      }
+    });
+
+    // Assign used props in not confident components to the parent component
+    Object.keys(thisList).filter((j) => thisList[j].confidence >= 2).forEach((j) => {
+      const id = getId(thisList[j].node);
+      list[j] = thisList[j];
+      if (usedPropTypes[id]) {
+        list[j].usedPropTypes = mergeUsedPropTypes(list[j].usedPropTypes || [], usedPropTypes[id]);
+      }
+    });
+    return list;
+  }
+
+  /**
+   * Return the length of the components list
+   * Components for which we are not confident are not counted
+   *
+   * @returns {number} Components list length
+   */
+  length() {
+    const list = Lists.get(this);
+    return values(list).filter((component) => component.confidence >= 2).length;
+  }
+
+  /**
+   * Return the node naming the default React import
+   * It can be used to determine the local name of import, even if it's imported
+   * with an unusual name.
+   *
+   * @returns {ASTNode} React default import node
+   */
+  getDefaultReactImports() {
+    return ReactImports.get(this).defaultReactImports;
+  }
+
+  /**
+   * Return the nodes of all React named imports
+   *
+   * @returns {Object} The list of React named imports
+   */
+  getNamedReactImports() {
+    return ReactImports.get(this).namedReactImports;
+  }
+
+  /**
+   * Add the default React import specifier to the scope
+   *
+   * @param {ASTNode} specifier The AST Node of the default React import
+   * @returns {void}
+   */
+  addDefaultReactImport(specifier) {
+    const info = ReactImports.get(this);
+    ReactImports.set(this, Object.assign({}, info, {
+      defaultReactImports: (info.defaultReactImports || []).concat(specifier),
+    }));
+  }
+
+  /**
+   * Add a named React import specifier to the scope
+   *
+   * @param {ASTNode} specifier The AST Node of a named React import
+   * @returns {void}
+   */
+  addNamedReactImport(specifier) {
+    const info = ReactImports.get(this);
+    ReactImports.set(this, Object.assign({}, info, {
+      namedReactImports: (info.namedReactImports || []).concat(specifier),
+    }));
+  }
+}
+
+function getWrapperFunctions(context, pragma) {
+  const componentWrapperFunctions = context.settings.componentWrapperFunctions || [];
+
+  // eslint-disable-next-line arrow-body-style
+  return componentWrapperFunctions.map((wrapperFunction) => {
+    return typeof wrapperFunction === 'string'
+      ? { property: wrapperFunction }
+      : Object.assign({}, wrapperFunction, {
+        object: wrapperFunction.object === '<pragma>' ? pragma : wrapperFunction.object,
+      });
+  }).concat([
+    { property: 'forwardRef', object: pragma },
+    { property: 'memo', object: pragma },
+  ]);
+}
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * Merge many eslint rules into one
+ * @param {{[_: string]: Function}[]} rules the returned values for eslint rule.create(context)
+ * @returns {{[_: string]: Function}} merged rule
+ */
+function mergeRules(rules) {
+  /** @type {Map<string, Function[]>} */
+  const handlersByKey = new Map();
+  rules.forEach((rule) => {
+    Object.keys(rule).forEach((key) => {
+      const fns = handlersByKey.get(key);
+      if (!fns) {
+        handlersByKey.set(key, [rule[key]]);
+      } else {
+        fns.push(rule[key]);
+      }
+    });
+  });
+
+  /** @type {{ [key: string]: Function }} */
+  return fromEntries(map(iterFrom(handlersByKey), (entry) => [
+    entry[0],
+    function mergedHandler(node) {
+      entry[1].forEach((fn) => {
+        fn(node);
+      });
+    },
+  ]));
+}
+
+function componentRule(rule, context) {
+  const pragma = pragmaUtil.getFromContext(context);
+  const components = new Components();
+  const wrapperFunctions = getWrapperFunctions(context, pragma);
+
+  // Utilities for component detection
+  const utils = {
+    /**
+     * Check if variable is destructured from pragma import
+     *
+     * @param {ASTNode} node The AST node to check
+     * @param {string} variable The variable name to check
+     * @returns {boolean} True if createElement is destructured from the pragma
+     */
+    isDestructuredFromPragmaImport(node, variable) {
+      return isDestructuredFromPragmaImport(context, node, variable);
+    },
+
+    /**
+     * @param {ASTNode} node
+     * @param {boolean=} strict
+     * @returns {boolean}
+     */
+    isReturningJSX(node, strict) {
+      return jsxUtil.isReturningJSX(context, node, strict, true);
+    },
+
+    isReturningJSXOrNull(node, strict) {
+      return jsxUtil.isReturningJSX(context, node, strict);
+    },
+
+    isReturningOnlyNull(node) {
+      return jsxUtil.isReturningOnlyNull(node, context);
+    },
+
+    getPragmaComponentWrapper(node) {
+      let isPragmaComponentWrapper;
+      let currentNode = node;
+      let prevNode;
+      do {
+        currentNode = currentNode.parent;
+        isPragmaComponentWrapper = this.isPragmaComponentWrapper(currentNode);
+        if (isPragmaComponentWrapper) {
+          prevNode = currentNode;
+        }
+      } while (isPragmaComponentWrapper);
+
+      return prevNode;
+    },
+
+    getComponentNameFromJSXElement(node) {
+      if (node.type !== 'JSXElement') {
+        return null;
+      }
+      if (node.openingElement && node.openingElement.name && node.openingElement.name.name) {
+        return node.openingElement.name.name;
+      }
+      return null;
+    },
+
+    /**
+     * Getting the first JSX element's name.
+     * @param {object} node
+     * @returns {string | null}
+     */
+    getNameOfWrappedComponent(node) {
+      if (node.length < 1) {
+        return null;
+      }
+      const body = node[0].body;
+      if (!body) {
+        return null;
+      }
+      if (body.type === 'JSXElement') {
+        return this.getComponentNameFromJSXElement(body);
+      }
+      if (body.type === 'BlockStatement') {
+        const jsxElement = body.body.find((item) => item.type === 'ReturnStatement');
+        return jsxElement
+          && jsxElement.argument
+          && this.getComponentNameFromJSXElement(jsxElement.argument);
+      }
+      return null;
+    },
+
+    /**
+     * Get the list of names of components created till now
+     * @returns {string | boolean}
+     */
+    getDetectedComponents() {
+      const list = components.list();
+      return values(list).filter((val) => {
+        if (val.node.type === 'ClassDeclaration') {
+          return true;
+        }
+        if (
+          val.node.type === 'ArrowFunctionExpression'
+          && val.node.parent
+          && val.node.parent.type === 'VariableDeclarator'
+          && val.node.parent.id
+        ) {
+          return true;
+        }
+        return false;
+      }).map((val) => {
+        if (val.node.type === 'ArrowFunctionExpression') return val.node.parent.id.name;
+        return val.node.id && val.node.id.name;
+      });
+    },
+
+    /**
+     * It will check whether memo/forwardRef is wrapping existing component or
+     * creating a new one.
+     * @param {object} node
+     * @returns {boolean}
+     */
+    nodeWrapsComponent(node) {
+      const childComponent = this.getNameOfWrappedComponent(node.arguments);
+      const componentList = this.getDetectedComponents();
+      return !!childComponent && arrayIncludes(componentList, childComponent);
+    },
+
+    isPragmaComponentWrapper(node) {
+      if (!astUtil.isCallExpression(node)) {
+        return false;
+      }
+
+      return wrapperFunctions.some((wrapperFunction) => {
+        if (node.callee.type === 'MemberExpression') {
+          return wrapperFunction.object
+            && wrapperFunction.object === node.callee.object.name
+            && wrapperFunction.property === node.callee.property.name
+            && !this.nodeWrapsComponent(node);
+        }
+        return wrapperFunction.property === node.callee.name
+          && (!wrapperFunction.object
+            // Functions coming from the current pragma need special handling
+            || (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node, node.callee.name))
+          );
+      });
+    },
+
+    /**
+     * Find a return statement in the current node
+     *
+     * @param {ASTNode} node The AST node being checked
+     */
+    findReturnStatement: astUtil.findReturnStatement,
+
+    /**
+     * Get the parent component node from the current scope
+     * @param {ASTNode} node
+     *
+     * @returns {ASTNode} component node, null if we are not in a component
+     */
+    getParentComponent(node) {
+      return (
+        componentUtil.getParentES6Component(context, node)
+        || componentUtil.getParentES5Component(context, node)
+        || utils.getParentStatelessComponent(node)
+      );
+    },
+
+    /**
+     * @param {ASTNode} node
+     * @returns {boolean}
+     */
+    isInAllowedPositionForComponent(node) {
+      switch (node.parent.type) {
+        case 'VariableDeclarator':
+        case 'AssignmentExpression':
+        case 'Property':
+        case 'ReturnStatement':
+        case 'ExportDefaultDeclaration':
+        case 'ArrowFunctionExpression': {
+          return true;
+        }
+        case 'SequenceExpression': {
+          return utils.isInAllowedPositionForComponent(node.parent)
+            && node === node.parent.expressions[node.parent.expressions.length - 1];
+        }
+        default:
+          return false;
+      }
+    },
+
+    /**
+     * Get node if node is a stateless component, or node.parent in cases like
+     * `React.memo` or `React.forwardRef`. Otherwise returns `undefined`.
+     * @param {ASTNode} node
+     * @returns {ASTNode | undefined}
+     */
+    getStatelessComponent(node) {
+      const parent = node.parent;
+      if (
+        node.type === 'FunctionDeclaration'
+        && (!node.id || isFirstLetterCapitalized(node.id.name))
+        && utils.isReturningJSXOrNull(node)
+      ) {
+        return node;
+      }
+
+      if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
+        const isPropertyAssignment = parent.type === 'AssignmentExpression'
+          && parent.left.type === 'MemberExpression';
+        const isModuleExportsAssignment = isPropertyAssignment
+          && parent.left.object.name === 'module'
+          && parent.left.property.name === 'exports';
+
+        if (node.parent.type === 'ExportDefaultDeclaration') {
+          if (utils.isReturningJSX(node)) {
+            return node;
+          }
+          return undefined;
+        }
+
+        if (node.parent.type === 'VariableDeclarator' && utils.isReturningJSXOrNull(node)) {
+          if (isFirstLetterCapitalized(node.parent.id.name)) {
+            return node;
+          }
+          return undefined;
+        }
+
+        // case: const any = () => { return (props) => null }
+        // case: const any = () => (props) => null
+        if (
+          (node.parent.type === 'ReturnStatement' || (node.parent.type === 'ArrowFunctionExpression' && node.parent.expression))
+          && !utils.isReturningJSX(node)
+        ) {
+          return undefined;
+        }
+
+        // case: any = () => { return => null }
+        // case: any = () => null
+        if (node.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) {
+          if (isFirstLetterCapitalized(node.parent.left.name)) {
+            return node;
+          }
+          return undefined;
+        }
+
+        // case: any = () => () => null
+        if (node.parent.type === 'ArrowFunctionExpression' && node.parent.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) {
+          if (isFirstLetterCapitalized(node.parent.parent.left.name)) {
+            return node;
+          }
+          return undefined;
+        }
+
+        // case: { any: () => () => null }
+        if (node.parent.type === 'ArrowFunctionExpression' && node.parent.parent.type === 'Property' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) {
+          if (isFirstLetterCapitalized(node.parent.parent.key.name)) {
+            return node;
+          }
+          return undefined;
+        }
+
+        // case: any = function() {return function() {return null;};}
+        if (node.parent.type === 'ReturnStatement') {
+          if (isFirstLetterCapitalized(node.id && node.id.name)) {
+            return node;
+          }
+          const functionExpr = node.parent.parent.parent;
+          if (functionExpr.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) {
+            if (isFirstLetterCapitalized(functionExpr.parent.left.name)) {
+              return node;
+            }
+            return undefined;
+          }
+        }
+
+        // case: { any: function() {return function() {return null;};} }
+        if (node.parent.type === 'ReturnStatement') {
+          const functionExpr = node.parent.parent.parent;
+          if (functionExpr.parent.type === 'Property' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) {
+            if (isFirstLetterCapitalized(functionExpr.parent.key.name)) {
+              return node;
+            }
+            return undefined;
+          }
+        }
+
+        // for case abc = { [someobject.somekey]: props => { ... return not-jsx } }
+        if (
+          node.parent
+          && node.parent.key
+          && node.parent.key.type === 'MemberExpression'
+          && !utils.isReturningJSX(node)
+          && !utils.isReturningOnlyNull(node)
+        ) {
+          return undefined;
+        }
+
+        if (
+          node.parent.type === 'Property' && (
+            (node.parent.method && !node.parent.computed) // case: { f() { return ... } }
+            || (!node.id && !node.parent.computed) // case: { f: () => ... }
+          )
+        ) {
+          if (
+            isFirstLetterCapitalized(node.parent.key.name)
+            && utils.isReturningJSX(node)
+          ) {
+            return node;
+          }
+          return undefined;
+        }
+
+        // Case like `React.memo(() => <></>)` or `React.forwardRef(...)`
+        const pragmaComponentWrapper = utils.getPragmaComponentWrapper(node);
+        if (pragmaComponentWrapper && utils.isReturningJSXOrNull(node)) {
+          return pragmaComponentWrapper;
+        }
+
+        if (!(utils.isInAllowedPositionForComponent(node) && utils.isReturningJSXOrNull(node))) {
+          return undefined;
+        }
+
+        if (utils.isParentComponentNotStatelessComponent(node)) {
+          return undefined;
+        }
+
+        if (node.id) {
+          return isFirstLetterCapitalized(node.id.name) ? node : undefined;
+        }
+
+        if (
+          isPropertyAssignment
+          && !isModuleExportsAssignment
+          && !isFirstLetterCapitalized(parent.left.property.name)
+        ) {
+          return undefined;
+        }
+
+        if (parent.type === 'Property' && utils.isReturningOnlyNull(node)) {
+          return undefined;
+        }
+
+        return node;
+      }
+
+      return undefined;
+    },
+
+    /**
+     * Get the parent stateless component node from the current scope
+     *
+     * @param {ASTNode} node The AST node being checked
+     * @returns {ASTNode} component node, null if we are not in a component
+     */
+    getParentStatelessComponent(node) {
+      let scope = getScope(context, node);
+      while (scope) {
+        const statelessComponent = utils.getStatelessComponent(scope.block);
+        if (statelessComponent) {
+          return statelessComponent;
+        }
+        scope = scope.upper;
+      }
+      return null;
+    },
+
+    /**
+     * Get the related component from a node
+     *
+     * @param {ASTNode} node The AST node being checked (must be a MemberExpression).
+     * @returns {ASTNode | null} component node, null if we cannot find the component
+     */
+    getRelatedComponent(node) {
+      let i;
+      let j;
+      let k;
+      let l;
+      let componentNode;
+      // Get the component path
+      const componentPath = [];
+      let nodeTemp = node;
+      while (nodeTemp) {
+        if (nodeTemp.property && nodeTemp.property.type === 'Identifier') {
+          componentPath.push(nodeTemp.property.name);
+        }
+        if (nodeTemp.object && nodeTemp.object.type === 'Identifier') {
+          componentPath.push(nodeTemp.object.name);
+        }
+        nodeTemp = nodeTemp.object;
+      }
+      componentPath.reverse();
+      const componentName = componentPath.slice(0, componentPath.length - 1).join('.');
+
+      // Find the variable in the current scope
+      const variableName = componentPath.shift();
+      if (!variableName) {
+        return null;
+      }
+      const variableInScope = variableUtil.getVariableFromContext(context, node, variableName);
+      if (!variableInScope) {
+        return null;
+      }
+
+      // Try to find the component using variable references
+      variableInScope.references.some((ref) => {
+        let refId = ref.identifier;
+        if (refId.parent && refId.parent.type === 'MemberExpression') {
+          refId = refId.parent;
+        }
+        if (getText(context, refId) !== componentName) {
+          return false;
+        }
+        if (refId.type === 'MemberExpression') {
+          componentNode = refId.parent.right;
+        } else if (
+          refId.parent
+          && refId.parent.type === 'VariableDeclarator'
+          && refId.parent.init
+          && refId.parent.init.type !== 'Identifier'
+        ) {
+          componentNode = refId.parent.init;
+        }
+        return true;
+      });
+
+      if (componentNode) {
+        // Return the component
+        return components.add(componentNode, 1);
+      }
+
+      // Try to find the component using variable declarations
+      const defs = variableInScope.defs;
+      const defInScope = defs.find((def) => (
+        def.type === 'ClassName'
+        || def.type === 'FunctionName'
+        || def.type === 'Variable'
+      ));
+      if (!defInScope || !defInScope.node) {
+        return null;
+      }
+      componentNode = defInScope.node.init || defInScope.node;
+
+      // Traverse the node properties to the component declaration
+      for (i = 0, j = componentPath.length; i < j; i++) {
+        if (!componentNode.properties) {
+          continue; // eslint-disable-line no-continue
+        }
+        for (k = 0, l = componentNode.properties.length; k < l; k++) {
+          if (componentNode.properties[k].key && componentNode.properties[k].key.name === componentPath[i]) {
+            componentNode = componentNode.properties[k];
+            break;
+          }
+        }
+        if (!componentNode || !componentNode.value) {
+          return null;
+        }
+        componentNode = componentNode.value;
+      }
+
+      // Return the component
+      return components.add(componentNode, 1);
+    },
+
+    isParentComponentNotStatelessComponent(node) {
+      return !!(
+        node.parent
+        && node.parent.key
+        && node.parent.key.type === 'Identifier'
+        // custom component functions must start with a capital letter (returns false otherwise)
+        && node.parent.key.name.charAt(0) === node.parent.key.name.charAt(0).toLowerCase()
+        // react render function cannot have params
+        && !!(node.params || []).length
+      );
+    },
+
+    /**
+     * Identify whether a node (CallExpression) is a call to a React hook
+     *
+     * @param {ASTNode} node The AST node being searched. (expects CallExpression)
+     * @param {('useCallback'|'useContext'|'useDebugValue'|'useEffect'|'useImperativeHandle'|'useLayoutEffect'|'useMemo'|'useReducer'|'useRef'|'useState')[]} [expectedHookNames] React hook names to which search is limited.
+     * @returns {boolean} True if the node is a call to a React hook
+     */
+    isReactHookCall(node, expectedHookNames) {
+      if (!astUtil.isCallExpression(node)) {
+        return false;
+      }
+
+      const defaultReactImports = components.getDefaultReactImports();
+      const namedReactImports = components.getNamedReactImports();
+
+      const defaultReactImportName = defaultReactImports
+        && defaultReactImports[0]
+        && defaultReactImports[0].local.name;
+      const reactHookImportSpecifiers = namedReactImports
+        && namedReactImports.filter((specifier) => USE_HOOK_PREFIX_REGEX.test(specifier.imported.name));
+      const reactHookImportNames = reactHookImportSpecifiers
+        && fromEntries(reactHookImportSpecifiers.map((specifier) => [specifier.local.name, specifier.imported.name]));
+
+      const isPotentialReactHookCall = defaultReactImportName
+        && node.callee.type === 'MemberExpression'
+        && node.callee.object.type === 'Identifier'
+        && node.callee.object.name === defaultReactImportName
+        && node.callee.property.type === 'Identifier'
+        && node.callee.property.name.match(USE_HOOK_PREFIX_REGEX);
+
+      const isPotentialHookCall = reactHookImportNames
+        && node.callee.type === 'Identifier'
+        && node.callee.name.match(USE_HOOK_PREFIX_REGEX);
+
+      const scope = (isPotentialReactHookCall || isPotentialHookCall) && getScope(context, node);
+
+      const reactResolvedDefs = isPotentialReactHookCall
+        && scope.references
+        && scope.references.find(
+          (reference) => reference.identifier.name === defaultReactImportName
+        ).resolved.defs;
+
+      const isReactShadowed = isPotentialReactHookCall && reactResolvedDefs
+        && reactResolvedDefs.some((reactDef) => reactDef.type !== 'ImportBinding');
+
+      const potentialHookReference = isPotentialHookCall
+        && scope.references
+        && scope.references.find(
+          (reference) => reactHookImportNames[reference.identifier.name]
+        );
+
+      const hookResolvedDefs = potentialHookReference && potentialHookReference.resolved.defs;
+      const localHookName = (
+        isPotentialReactHookCall
+        && node.callee.property.name
+      ) || (
+        isPotentialHookCall
+        && potentialHookReference
+        && node.callee.name
+      );
+      const isHookShadowed = isPotentialHookCall
+        && hookResolvedDefs
+        && hookResolvedDefs.some(
+          (hookDef) => hookDef.name.name === localHookName
+          && hookDef.type !== 'ImportBinding'
+        );
+
+      const isHookCall = (isPotentialReactHookCall && !isReactShadowed)
+        || (isPotentialHookCall && localHookName && !isHookShadowed);
+
+      if (!isHookCall) {
+        return false;
+      }
+
+      if (!expectedHookNames) {
+        return true;
+      }
+
+      return arrayIncludes(
+        expectedHookNames,
+        (reactHookImportNames && reactHookImportNames[localHookName]) || localHookName
+      );
+    },
+  };
+
+  // Component detection instructions
+  const detectionInstructions = {
+    CallExpression(node) {
+      if (!utils.isPragmaComponentWrapper(node)) {
+        return;
+      }
+      if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
+        components.add(node, 2);
+      }
+    },
+
+    ClassExpression(node) {
+      if (!componentUtil.isES6Component(node, context)) {
+        return;
+      }
+      components.add(node, 2);
+    },
+
+    ClassDeclaration(node) {
+      if (!componentUtil.isES6Component(node, context)) {
+        return;
+      }
+      components.add(node, 2);
+    },
+
+    ObjectExpression(node) {
+      if (!componentUtil.isES5Component(node, context)) {
+        return;
+      }
+      components.add(node, 2);
+    },
+
+    FunctionExpression(node) {
+      if (node.async && node.generator) {
+        components.add(node, 0);
+        return;
+      }
+
+      const component = utils.getStatelessComponent(node);
+      if (!component) {
+        return;
+      }
+      components.add(component, 2);
+    },
+
+    FunctionDeclaration(node) {
+      if (node.async && node.generator) {
+        components.add(node, 0);
+        return;
+      }
+
+      const cNode = utils.getStatelessComponent(node);
+      if (!cNode) {
+        return;
+      }
+      components.add(cNode, 2);
+    },
+
+    ArrowFunctionExpression(node) {
+      const component = utils.getStatelessComponent(node);
+      if (!component) {
+        return;
+      }
+      components.add(component, 2);
+    },
+
+    ThisExpression(node) {
+      const component = utils.getParentStatelessComponent(node);
+      if (!component || !/Function/.test(component.type) || !node.parent.property) {
+        return;
+      }
+      // Ban functions accessing a property on a ThisExpression
+      components.add(node, 0);
+    },
+  };
+
+  // Detect React import specifiers
+  const reactImportInstructions = {
+    ImportDeclaration(node) {
+      const isReactImported = node.source.type === 'Literal' && node.source.value === 'react';
+      if (!isReactImported) {
+        return;
+      }
+
+      node.specifiers.forEach((specifier) => {
+        if (specifier.type === 'ImportDefaultSpecifier') {
+          components.addDefaultReactImport(specifier);
+        }
+        if (specifier.type === 'ImportSpecifier') {
+          components.addNamedReactImport(specifier);
+        }
+      });
+    },
+  };
+
+  const ruleInstructions = rule(context, components, utils);
+  const propTypesInstructions = propTypesUtil(context, components, utils);
+  const usedPropTypesInstructions = usedPropTypesUtil(context, components, utils);
+  const defaultPropsInstructions = defaultPropsUtil(context, components, utils);
+
+  const mergedRule = mergeRules([
+    detectionInstructions,
+    propTypesInstructions,
+    usedPropTypesInstructions,
+    defaultPropsInstructions,
+    reactImportInstructions,
+    ruleInstructions,
+  ]);
+
+  return mergedRule;
+}
+
+module.exports = Object.assign(Components, {
+  detect(rule) {
+    return componentRule.bind(this, rule);
+  },
+});
Index: frontend/node_modules/eslint-plugin-react/lib/util/annotations.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/annotations.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/annotations.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,8 @@
+/**
+ * Checks if we are declaring a `props` argument with a flow type annotation.
+ * @param {ASTNode} node The AST node being checked.
+ * @param {Object} context
+ * @returns {boolean} True if the node is a type annotated props declaration, false if not.
+ */
+export function isAnnotatedFunctionPropsDeclaration(node: ASTNode, context: any): boolean;
+//# sourceMappingURL=annotations.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/annotations.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/annotations.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/annotations.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["annotations.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,0DAJW,OAAO,iBAEL,OAAO,CAenB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/annotations.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/annotations.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/annotations.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview Utility functions for type annotation detection.
+ * @author Yannick Croissant
+ * @author Vitor Balocco
+ */
+
+'use strict';
+
+const getFirstTokens = require('./eslint').getFirstTokens;
+
+/**
+ * Checks if we are declaring a `props` argument with a flow type annotation.
+ * @param {ASTNode} node The AST node being checked.
+ * @param {Object} context
+ * @returns {boolean} True if the node is a type annotated props declaration, false if not.
+ */
+function isAnnotatedFunctionPropsDeclaration(node, context) {
+  if (!node || !node.params || !node.params.length) {
+    return false;
+  }
+
+  const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0];
+
+  const tokens = getFirstTokens(context, typeNode, 2);
+  const isAnnotated = typeNode.typeAnnotation;
+  const isDestructuredProps = typeNode.type === 'ObjectPattern';
+  const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props');
+
+  return (isAnnotated && (isDestructuredProps || isProps));
+}
+
+module.exports = {
+  isAnnotatedFunctionPropsDeclaration,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/ast.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/ast.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/ast.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,136 @@
+/**
+ * Find a return statement in the current node
+ *
+ * @param {ASTNode} node The AST node being checked
+ * @returns {ASTNode | false}
+ */
+export function findReturnStatement(node: ASTNode): ASTNode | false;
+/**
+ * Get properties for a given AST node
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Array} Properties array.
+ */
+export function getComponentProperties(node: ASTNode): any[];
+/**
+ * Gets the first node in a line from the initial node, excluding whitespace.
+ * @param {Object} context The node to check
+ * @param {ASTNode} node The node to check
+ * @return {ASTNode} the first node in the line
+ */
+export function getFirstNodeInLine(context: any, node: ASTNode): ASTNode;
+/**
+ * Retrieve the name of a key node
+ * @param {Context} context The AST node with the key.
+ * @param {any} node The AST node with the key.
+ * @return {string | undefined} the name of the key
+ */
+export function getKeyValue(context: Context, node: any): string | undefined;
+/**
+ * Get properties name
+ * @param {Object} node - Property.
+ * @returns {string} Property name.
+ */
+export function getPropertyName(node: any): string;
+/**
+ * Get node with property's name
+ * @param {Object} node - Property.
+ * @returns {Object} Property name node.
+ */
+export function getPropertyNameNode(node: any): any;
+/**
+ * Check if we are in a class constructor
+ * @param {Context} context
+ * @param {ASTNode} node The AST node being checked.
+ * @return {boolean}
+ */
+export function inConstructor(context: Context, node: ASTNode): boolean;
+/**
+ * Checks if a node is being assigned a value: props.bar = 'bar'
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {boolean}
+ */
+export function isAssignmentLHS(node: ASTNode): boolean;
+/**
+ * Matcher used to check whether given node is a `CallExpression`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `CallExpression`, false if not
+ */
+export function isCallExpression(node: ASTNode): boolean;
+/**
+ * Checks if the node is a class.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a class
+ */
+export function isClass(node: ASTNode): boolean;
+/**
+ * Checks if the node is a function.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a function
+ */
+export function isFunction(node: ASTNode): boolean;
+/**
+ * Checks if node is a function declaration or expression or arrow function.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a function-like
+ */
+export function isFunctionLike(node: ASTNode): boolean;
+/**
+ * Checks if the node is a function or arrow function expression.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a function-like expression
+ */
+export function isFunctionLikeExpression(node: ASTNode): boolean;
+/**
+ * Checks if the node is the first in its line, excluding whitespace.
+ * @param {Object} context The node to check
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's the first node in its line
+ */
+export function isNodeFirstInLine(context: any, node: ASTNode): boolean;
+/**
+ * Checks if a node is surrounded by parenthesis.
+ *
+ * @param {object} context - Context from the rule
+ * @param {ASTNode} node - Node to be checked
+ * @returns {boolean}
+ */
+export function isParenthesized(context: object, node: ASTNode): boolean;
+export function isTSAsExpression(node: any): boolean;
+export function isTSFunctionType(node: any): boolean;
+export function isTSInterfaceDeclaration(node: any): boolean;
+export function isTSInterfaceHeritage(node: any): boolean;
+export function isTSIntersectionType(node: any): boolean;
+export function isTSParenthesizedType(node: any): boolean;
+export function isTSTypeAliasDeclaration(node: any): boolean;
+export function isTSTypeAnnotation(node: any): boolean;
+export function isTSTypeDeclaration(node: any): boolean;
+export function isTSTypeLiteral(node: any): boolean;
+export function isTSTypeParameterInstantiation(node: any): boolean;
+export function isTSTypeQuery(node: any): boolean;
+export function isTSTypeReference(node: any): boolean;
+/**
+ * Wrapper for estraverse.traverse
+ *
+ * @param {ASTNode} ASTnode The AST node being checked
+ * @param {Object} visitor Visitor Object for estraverse
+ */
+export function traverse(ASTnode: ASTNode, visitor: any): void;
+/**
+ * Helper function for traversing "returns" (return statements or the
+ * returned expression in the case of an arrow function) of a function
+ *
+ * @param {ASTNode} ASTNode The AST node being checked
+ * @param {Context} context The context of `ASTNode`.
+ * @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn
+ *   Function to execute for each returnStatement found
+ * @returns {undefined}
+ */
+export function traverseReturns(ASTNode: ASTNode, context: Context, onReturn: (returnValue: ASTNode, breakTraverse: () => void) => void): undefined;
+/**
+ * Extracts the expression node that is wrapped inside a TS type assertion
+ *
+ * @param {ASTNode} node - potential TS node
+ * @returns {ASTNode} - unwrapped expression node
+ */
+export function unwrapTSAsExpression(node: ASTNode): ASTNode;
+//# sourceMappingURL=ast.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/ast.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/ast.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/ast.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["ast.js"],"names":[],"mappings":"AAkDA;;;;;GAKG;AACH,0CAHW,OAAO,GACL,OAAO,GAAG,KAAK,CAa3B;AA0GD;;;;GAIG;AACH,6CAHW,OAAO,SAajB;AAED;;;;;GAKG;AACH,uDAHW,OAAO,GACN,OAAO,CAgBlB;AA8ED;;;;;GAKG;AACH,qCAJW,OAAO,QACP,GAAG,GACF,MAAM,GAAG,SAAS,CAqB7B;AAtJD;;;;GAIG;AACH,4CAFa,MAAM,CAKlB;AA3BD;;;;GAIG;AACH,oDAYC;AAoGD;;;;;GAKG;AACH,uCAJW,OAAO,QACP,OAAO,GACN,OAAO,CAYlB;AAuDD;;;;GAIG;AACH,sCAHW,OAAO,GACL,OAAO,CAQnB;AAMD;;;;GAIG;AACH,uCAHW,OAAO,GACL,OAAO,CAInB;AAxGD;;;;GAIG;AACH,8BAHW,OAAO,GACN,OAAO,CAIlB;AAzBD;;;;GAIG;AACH,iCAHW,OAAO,GACN,OAAO,CAIlB;AAED;;;;GAIG;AACH,qCAHW,OAAO,GACN,OAAO,CAIlB;AAzBD;;;;GAIG;AACH,+CAHW,OAAO,GACN,OAAO,CAIlB;AApBD;;;;;GAKG;AACH,sDAHW,OAAO,GACN,OAAO,CAOlB;AA4FD;;;;;;GAMG;AACH,yCAJW,MAAM,QACN,OAAO,GACL,OAAO,CAUnB;AAeD,qDAEC;AAqFD,qDAIC;AAtCD,6DAOC;AAbD,0DAIC;AAVD,yDAIC;AAoCD,0DAIC;AAbD,6DAOC;AAlDD,uDAIC;AA6BD,wDAQC;AAnCD,oDAIC;AA4DD,mEAIC;AAVD,kDAIC;AA1ED,sDAIC;AAtWD;;;;;GAKG;AACH,kCAHW,OAAO,sBAgBjB;AAqCD;;;;;;;;;GASG;AACH,yCANW,OAAO,WACP,OAAO,0BACO,OAAO,iBAAiB,MAAM,IAAI,KAAK,IAAI,GAEvD,SAAS,CAgErB;AAwND;;;;;GAKG;AACH,2CAHW,OAAO,GACL,OAAO,CAInB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/ast.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/ast.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/ast.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,483 @@
+/**
+ * @fileoverview Utility functions for AST
+ */
+
+'use strict';
+
+const estraverse = require('estraverse');
+const eslintUtil = require('./eslint');
+
+const getFirstTokens = eslintUtil.getFirstTokens;
+const getScope = eslintUtil.getScope;
+const getSourceCode = eslintUtil.getSourceCode;
+// const pragmaUtil = require('./pragma');
+
+/**
+ * Wrapper for estraverse.traverse
+ *
+ * @param {ASTNode} ASTnode The AST node being checked
+ * @param {Object} visitor Visitor Object for estraverse
+ */
+function traverse(ASTnode, visitor) {
+  const opts = Object.assign({}, {
+    fallback(node) {
+      return Object.keys(node).filter((key) => key === 'children' || key === 'argument');
+    },
+  }, visitor);
+
+  opts.keys = Object.assign({}, visitor.keys, {
+    JSXElement: ['children'],
+    JSXFragment: ['children'],
+  });
+
+  estraverse.traverse(ASTnode, opts);
+}
+
+function loopNodes(nodes) {
+  for (let i = nodes.length - 1; i >= 0; i--) {
+    if (nodes[i].type === 'ReturnStatement') {
+      return nodes[i];
+    }
+    if (nodes[i].type === 'SwitchStatement') {
+      const j = nodes[i].cases.length - 1;
+      if (j >= 0) {
+        return loopNodes(nodes[i].cases[j].consequent);
+      }
+    }
+  }
+  return false;
+}
+
+/**
+ * Find a return statement in the current node
+ *
+ * @param {ASTNode} node The AST node being checked
+ * @returns {ASTNode | false}
+ */
+function findReturnStatement(node) {
+  if (
+    (!node.value || !node.value.body || !node.value.body.body)
+    && (!node.body || !node.body.body)
+  ) {
+    return false;
+  }
+
+  const bodyNodes = node.value ? node.value.body.body : node.body.body;
+
+  return loopNodes(bodyNodes);
+}
+
+// eslint-disable-next-line valid-jsdoc -- valid-jsdoc cannot parse function types.
+/**
+ * Helper function for traversing "returns" (return statements or the
+ * returned expression in the case of an arrow function) of a function
+ *
+ * @param {ASTNode} ASTNode The AST node being checked
+ * @param {Context} context The context of `ASTNode`.
+ * @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn
+ *   Function to execute for each returnStatement found
+ * @returns {undefined}
+ */
+function traverseReturns(ASTNode, context, onReturn) {
+  const nodeType = ASTNode.type;
+
+  if (nodeType === 'ReturnStatement') {
+    onReturn(ASTNode.argument, () => {});
+    return;
+  }
+
+  if (nodeType === 'ArrowFunctionExpression' && ASTNode.expression) {
+    onReturn(ASTNode.body, () => {});
+    return;
+  }
+
+  /* TODO: properly warn on React.forwardRefs having typo properties
+  if (astUtil.isCallExpression(ASTNode)) {
+    const callee = ASTNode.callee;
+    const pragma = pragmaUtil.getFromContext(context);
+    if (
+      callee.type === 'MemberExpression'
+      && callee.object.type === 'Identifier'
+      && callee.object.name === pragma
+      && callee.property.type === 'Identifier'
+      && callee.property.name === 'forwardRef'
+      && ASTNode.arguments.length > 0
+    ) {
+      return enterFunc(ASTNode.arguments[0]);
+    }
+    return;
+  }
+  */
+
+  if (
+    nodeType !== 'FunctionExpression'
+    && nodeType !== 'FunctionDeclaration'
+    && nodeType !== 'ArrowFunctionExpression'
+    && nodeType !== 'MethodDefinition'
+  ) {
+    return;
+  }
+
+  traverse(ASTNode.body, {
+    enter(node) {
+      const breakTraverse = () => {
+        this.break();
+      };
+      switch (node.type) {
+        case 'ReturnStatement':
+          this.skip();
+          onReturn(node.argument, breakTraverse);
+          return;
+        case 'BlockStatement':
+        case 'IfStatement':
+        case 'ForStatement':
+        case 'WhileStatement':
+        case 'SwitchStatement':
+        case 'SwitchCase':
+          return;
+        default:
+          this.skip();
+      }
+    },
+  });
+}
+
+/**
+ * Get node with property's name
+ * @param {Object} node - Property.
+ * @returns {Object} Property name node.
+ */
+function getPropertyNameNode(node) {
+  if (
+    node.key
+    || node.type === 'MethodDefinition'
+    || node.type === 'Property'
+  ) {
+    return node.key;
+  }
+  if (node.type === 'MemberExpression') {
+    return node.property;
+  }
+  return null;
+}
+
+/**
+ * Get properties name
+ * @param {Object} node - Property.
+ * @returns {string} Property name.
+ */
+function getPropertyName(node) {
+  const nameNode = getPropertyNameNode(node);
+  return nameNode ? nameNode.name : '';
+}
+
+/**
+ * Get properties for a given AST node
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Array} Properties array.
+ */
+function getComponentProperties(node) {
+  switch (node.type) {
+    case 'ClassDeclaration':
+    case 'ClassExpression':
+      return node.body.body;
+    case 'ObjectExpression':
+      return node.properties;
+    default:
+      return [];
+  }
+}
+
+/**
+ * Gets the first node in a line from the initial node, excluding whitespace.
+ * @param {Object} context The node to check
+ * @param {ASTNode} node The node to check
+ * @return {ASTNode} the first node in the line
+ */
+function getFirstNodeInLine(context, node) {
+  const sourceCode = getSourceCode(context);
+  let token = node;
+  let lines;
+  do {
+    token = sourceCode.getTokenBefore(token);
+    lines = token.type === 'JSXText'
+      ? token.value.split('\n')
+      : null;
+  } while (
+    token.type === 'JSXText'
+        && /^\s*$/.test(lines[lines.length - 1])
+  );
+  return token;
+}
+
+/**
+ * Checks if the node is the first in its line, excluding whitespace.
+ * @param {Object} context The node to check
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's the first node in its line
+ */
+function isNodeFirstInLine(context, node) {
+  const token = getFirstNodeInLine(context, node);
+  const startLine = node.loc.start.line;
+  const endLine = token ? token.loc.end.line : -1;
+  return startLine !== endLine;
+}
+
+/**
+ * Checks if the node is a function or arrow function expression.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a function-like expression
+ */
+function isFunctionLikeExpression(node) {
+  return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
+}
+
+/**
+ * Checks if the node is a function.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a function
+ */
+function isFunction(node) {
+  return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
+}
+
+/**
+ * Checks if node is a function declaration or expression or arrow function.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a function-like
+ */
+function isFunctionLike(node) {
+  return node.type === 'FunctionDeclaration' || isFunctionLikeExpression(node);
+}
+
+/**
+ * Checks if the node is a class.
+ * @param {ASTNode} node The node to check
+ * @return {boolean} true if it's a class
+ */
+function isClass(node) {
+  return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
+}
+
+/**
+ * Check if we are in a class constructor
+ * @param {Context} context
+ * @param {ASTNode} node The AST node being checked.
+ * @return {boolean}
+ */
+function inConstructor(context, node) {
+  let scope = getScope(context, node);
+  while (scope) {
+    // @ts-ignore
+    if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') {
+      return true;
+    }
+    scope = scope.upper;
+  }
+  return false;
+}
+
+/**
+ * Removes quotes from around an identifier.
+ * @param {string} string the identifier to strip
+ * @returns {string}
+ */
+function stripQuotes(string) {
+  return string.replace(/^'|'$/g, '');
+}
+
+/**
+ * Retrieve the name of a key node
+ * @param {Context} context The AST node with the key.
+ * @param {any} node The AST node with the key.
+ * @return {string | undefined} the name of the key
+ */
+function getKeyValue(context, node) {
+  if (node.type === 'ObjectTypeProperty') {
+    const tokens = getFirstTokens(context, node, 2);
+    return (tokens[0].value === '+' || tokens[0].value === '-'
+      ? tokens[1].value
+      : stripQuotes(tokens[0].value)
+    );
+  }
+  if (node.type === 'GenericTypeAnnotation') {
+    return node.id.name;
+  }
+  if (node.type === 'ObjectTypeAnnotation') {
+    return;
+  }
+  const key = node.key || node.argument;
+  if (!key) {
+    return;
+  }
+  return key.type === 'Identifier' ? key.name : key.value;
+}
+
+/**
+ * Checks if a node is surrounded by parenthesis.
+ *
+ * @param {object} context - Context from the rule
+ * @param {ASTNode} node - Node to be checked
+ * @returns {boolean}
+ */
+function isParenthesized(context, node) {
+  const sourceCode = getSourceCode(context);
+  const previousToken = sourceCode.getTokenBefore(node);
+  const nextToken = sourceCode.getTokenAfter(node);
+
+  return !!previousToken && !!nextToken
+    && previousToken.value === '(' && previousToken.range[1] <= node.range[0]
+    && nextToken.value === ')' && nextToken.range[0] >= node.range[1];
+}
+
+/**
+ * Checks if a node is being assigned a value: props.bar = 'bar'
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {boolean}
+ */
+function isAssignmentLHS(node) {
+  return (
+    node.parent
+    && node.parent.type === 'AssignmentExpression'
+    && node.parent.left === node
+  );
+}
+
+function isTSAsExpression(node) {
+  return node && node.type === 'TSAsExpression';
+}
+
+/**
+ * Matcher used to check whether given node is a `CallExpression`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `CallExpression`, false if not
+ */
+function isCallExpression(node) {
+  return node && node.type === 'CallExpression';
+}
+
+/**
+ * Extracts the expression node that is wrapped inside a TS type assertion
+ *
+ * @param {ASTNode} node - potential TS node
+ * @returns {ASTNode} - unwrapped expression node
+ */
+function unwrapTSAsExpression(node) {
+  return isTSAsExpression(node) ? node.expression : node;
+}
+
+function isTSTypeReference(node) {
+  if (!node) return false;
+
+  return node.type === 'TSTypeReference';
+}
+
+function isTSTypeAnnotation(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeAnnotation';
+}
+
+function isTSTypeLiteral(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeLiteral';
+}
+
+function isTSIntersectionType(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSIntersectionType';
+}
+
+function isTSInterfaceHeritage(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSInterfaceHeritage';
+}
+
+function isTSInterfaceDeclaration(node) {
+  if (!node) { return false; }
+
+  return (node.type === 'ExportNamedDeclaration' && node.declaration
+    ? node.declaration.type
+    : node.type
+  ) === 'TSInterfaceDeclaration';
+}
+
+function isTSTypeDeclaration(node) {
+  if (!node) { return false; }
+
+  const nodeToCheck = node.type === 'ExportNamedDeclaration' && node.declaration
+    ? node.declaration
+    : node;
+
+  return nodeToCheck.type === 'VariableDeclaration' && nodeToCheck.kind === 'type';
+}
+
+function isTSTypeAliasDeclaration(node) {
+  if (!node) { return false; }
+
+  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
+    return node.declaration.type === 'TSTypeAliasDeclaration' && node.exportKind === 'type';
+  }
+  return node.type === 'TSTypeAliasDeclaration';
+}
+
+function isTSParenthesizedType(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeAliasDeclaration';
+}
+
+function isTSFunctionType(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSFunctionType';
+}
+
+function isTSTypeQuery(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeQuery';
+}
+
+function isTSTypeParameterInstantiation(node) {
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeParameterInstantiation';
+}
+
+module.exports = {
+  findReturnStatement,
+  getComponentProperties,
+  getFirstNodeInLine,
+  getKeyValue,
+  getPropertyName,
+  getPropertyNameNode,
+  inConstructor,
+  isAssignmentLHS,
+  isCallExpression,
+  isClass,
+  isFunction,
+  isFunctionLike,
+  isFunctionLikeExpression,
+  isNodeFirstInLine,
+  isParenthesized,
+  isTSAsExpression,
+  isTSFunctionType,
+  isTSInterfaceDeclaration,
+  isTSInterfaceHeritage,
+  isTSIntersectionType,
+  isTSParenthesizedType,
+  isTSTypeAliasDeclaration,
+  isTSTypeAnnotation,
+  isTSTypeDeclaration,
+  isTSTypeLiteral,
+  isTSTypeParameterInstantiation,
+  isTSTypeQuery,
+  isTSTypeReference,
+  traverse,
+  traverseReturns,
+  unwrapTSAsExpression,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,46 @@
+/**
+ * @param {ASTNode} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+export function isES5Component(node: ASTNode, context: Context): boolean;
+/**
+ * @param {ASTNode} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+export function isES6Component(node: ASTNode, context: Context): boolean;
+/**
+ * Get the parent ES5 component node from the current scope
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @returns {ASTNode|null}
+ */
+export function getParentES5Component(context: Context, node: ASTNode): ASTNode | null;
+/**
+ * Get the parent ES6 component node from the current scope
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @returns {ASTNode | null}
+ */
+export function getParentES6Component(context: Context, node: ASTNode): ASTNode | null;
+/**
+ * Check if the node is explicitly declared as a descendant of a React Component
+ * @param {any} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+export function isExplicitComponent(node: any, context: Context): boolean;
+/**
+ * Checks if a component extends React.PureComponent
+ * @param {ASTNode} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+export function isPureComponent(node: ASTNode, context: Context): boolean;
+/**
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+export function isStateMemberExpression(node: ASTNode): boolean;
+//# sourceMappingURL=componentUtil.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"componentUtil.d.ts","sourceRoot":"","sources":["componentUtil.js"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,qCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAmBnB;AAyCD;;;;GAIG;AACH,qCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAmBnB;AAED;;;;;GAKG;AACH,+CAJW,OAAO,QACP,OAAO,GACL,OAAO,GAAC,IAAI,CAaxB;AAED;;;;;GAKG;AACH,+CAJW,OAAO,QACP,OAAO,GACL,OAAO,GAAG,IAAI,CAY1B;AAlGD;;;;;GAKG;AACH,0CAJW,GAAG,WACH,OAAO,GACL,OAAO,CAiCnB;AA+DD;;;;;GAKG;AACH,sCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAQnB;AAED;;;GAGG;AACH,8CAHW,OAAO,GACL,OAAO,CAMnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/componentUtil.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,190 @@
+'use strict';
+
+const doctrine = require('doctrine');
+const pragmaUtil = require('./pragma');
+const eslintUtil = require('./eslint');
+
+const getScope = eslintUtil.getScope;
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * @template {(_: object) => any} T
+ * @param {T} fn
+ * @returns {T}
+ */
+function memoize(fn) {
+  const cache = new WeakMap();
+  // @ts-ignore
+  return function memoizedFn(arg) {
+    const cachedValue = cache.get(arg);
+    if (cachedValue !== undefined) {
+      return cachedValue;
+    }
+    const v = fn(arg);
+    cache.set(arg, v);
+    return v;
+  };
+}
+
+const getPragma = memoize(pragmaUtil.getFromContext);
+const getCreateClass = memoize(pragmaUtil.getCreateClassFromContext);
+
+/**
+ * @param {ASTNode} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+function isES5Component(node, context) {
+  const pragma = getPragma(context);
+  const createClass = getCreateClass(context);
+
+  if (!node.parent || !node.parent.callee) {
+    return false;
+  }
+  const callee = node.parent.callee;
+  // React.createClass({})
+  if (callee.type === 'MemberExpression') {
+    return callee.object.name === pragma && callee.property.name === createClass;
+  }
+  // createClass({})
+  if (callee.type === 'Identifier') {
+    return callee.name === createClass;
+  }
+  return false;
+}
+
+/**
+ * Check if the node is explicitly declared as a descendant of a React Component
+ * @param {any} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+function isExplicitComponent(node, context) {
+  const sourceCode = getSourceCode(context);
+  let comment;
+  // Sometimes the passed node may not have been parsed yet by eslint, and this function call crashes.
+  // Can be removed when eslint sets "parent" property for all nodes on initial AST traversal: https://github.com/eslint/eslint-scope/issues/27
+  // eslint-disable-next-line no-warning-comments
+  // FIXME: Remove try/catch when https://github.com/eslint/eslint-scope/issues/27 is implemented.
+  try {
+    comment = sourceCode.getJSDocComment(node);
+  } catch (e) {
+    comment = null;
+  }
+
+  if (comment === null) {
+    return false;
+  }
+
+  let commentAst;
+  try {
+    commentAst = doctrine.parse(comment.value, {
+      unwrap: true,
+      tags: ['extends', 'augments'],
+    });
+  } catch (e) {
+    // handle a bug in the archived `doctrine`, see #2596
+    return false;
+  }
+
+  const relevantTags = commentAst.tags.filter((tag) => tag.name === 'React.Component' || tag.name === 'React.PureComponent');
+
+  return relevantTags.length > 0;
+}
+
+/**
+ * @param {ASTNode} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+function isES6Component(node, context) {
+  const pragma = getPragma(context);
+  if (isExplicitComponent(node, context)) {
+    return true;
+  }
+
+  if (!node.superClass) {
+    return false;
+  }
+  if (node.superClass.type === 'MemberExpression') {
+    return node.superClass.object.name === pragma
+          && /^(Pure)?Component$/.test(node.superClass.property.name);
+  }
+  if (node.superClass.type === 'Identifier') {
+    return /^(Pure)?Component$/.test(node.superClass.name);
+  }
+  return false;
+}
+
+/**
+ * Get the parent ES5 component node from the current scope
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @returns {ASTNode|null}
+ */
+function getParentES5Component(context, node) {
+  let scope = getScope(context, node);
+  while (scope) {
+    // @ts-ignore
+    node = scope.block && scope.block.parent && scope.block.parent.parent;
+    if (node && isES5Component(node, context)) {
+      return node;
+    }
+    scope = scope.upper;
+  }
+  return null;
+}
+
+/**
+ * Get the parent ES6 component node from the current scope
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @returns {ASTNode | null}
+ */
+function getParentES6Component(context, node) {
+  let scope = getScope(context, node);
+  while (scope && scope.type !== 'class') {
+    scope = scope.upper;
+  }
+  node = scope && scope.block;
+  if (!node || !isES6Component(node, context)) {
+    return null;
+  }
+  return node;
+}
+
+/**
+ * Checks if a component extends React.PureComponent
+ * @param {ASTNode} node
+ * @param {Context} context
+ * @returns {boolean}
+ */
+function isPureComponent(node, context) {
+  const pragma = getPragma(context);
+  if (node.superClass) {
+    return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(getText(context, node.superClass));
+  }
+  return false;
+}
+
+/**
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function isStateMemberExpression(node) {
+  return node.type === 'MemberExpression'
+    && node.object.type === 'ThisExpression'
+    && node.property.name === 'state';
+}
+
+module.exports = {
+  isES5Component,
+  isES6Component,
+  getParentES5Component,
+  getParentES6Component,
+  isExplicitComponent,
+  isPureComponent,
+  isStateMemberExpression,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,8 @@
+declare function _exports(context: any, components: any, utils: any): {
+    MemberExpression(node: any): void;
+    MethodDefinition(node: any): void;
+    'ClassProperty, PropertyDefinition'(node: any): void;
+    ObjectExpression(node: any): void;
+};
+export = _exports;
+//# sourceMappingURL=defaultProps.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"defaultProps.d.ts","sourceRoot":"","sources":["defaultProps.js"],"names":[],"mappings":"AAgBiB;;;;;EA0PhB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,267 @@
+/**
+ * @fileoverview Common defaultProps detection functionality.
+ */
+
+'use strict';
+
+const fromEntries = require('object.fromentries');
+const astUtil = require('./ast');
+const componentUtil = require('./componentUtil');
+const propsUtil = require('./props');
+const variableUtil = require('./variable');
+const propWrapperUtil = require('./propWrapper');
+const getText = require('./eslint').getText;
+
+const QUOTES_REGEX = /^["']|["']$/g;
+
+module.exports = function defaultPropsInstructions(context, components, utils) {
+  /**
+   * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not
+   * an Identifier, then the node is simply returned.
+   * @param   {ASTNode} node The node to resolve.
+   * @returns {ASTNode|null} Return null if the value could not be resolved, ASTNode otherwise.
+   */
+  function resolveNodeValue(node) {
+    if (node.type === 'Identifier') {
+      return variableUtil.findVariableByName(context, node, node.name);
+    }
+    if (
+      astUtil.isCallExpression(node)
+      && propWrapperUtil.isPropWrapperFunction(context, node.callee.name)
+      && node.arguments && node.arguments[0]
+    ) {
+      return resolveNodeValue(node.arguments[0]);
+    }
+    return node;
+  }
+
+  /**
+   * Extracts a DefaultProp from an ObjectExpression node.
+   * @param   {ASTNode} objectExpression ObjectExpression node.
+   * @returns {Object|string}            Object representation of a defaultProp, to be consumed by
+   *                                     `addDefaultPropsToComponent`, or string "unresolved", if the defaultProps
+   *                                     from this ObjectExpression can't be resolved.
+   */
+  function getDefaultPropsFromObjectExpression(objectExpression) {
+    const hasSpread = objectExpression.properties.find((property) => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement');
+
+    if (hasSpread) {
+      return 'unresolved';
+    }
+
+    return objectExpression.properties.map((defaultProp) => ({
+      name: getText(context, defaultProp.key).replace(QUOTES_REGEX, ''),
+      node: defaultProp,
+    }));
+  }
+
+  /**
+   * Marks a component's DefaultProps declaration as "unresolved". A component's DefaultProps is
+   * marked as "unresolved" if we cannot safely infer the values of its defaultProps declarations
+   * without risking false negatives.
+   * @param   {Object} component The component to mark.
+   * @returns {void}
+   */
+  function markDefaultPropsAsUnresolved(component) {
+    components.set(component.node, {
+      defaultProps: 'unresolved',
+    });
+  }
+
+  /**
+   * Adds defaultProps to the component passed in.
+   * @param   {ASTNode}         component    The component to add the defaultProps to.
+   * @param   {Object[]|'unresolved'} defaultProps defaultProps to add to the component or the string "unresolved"
+   *                                         if this component has defaultProps that can't be resolved.
+   * @returns {void}
+   */
+  function addDefaultPropsToComponent(component, defaultProps) {
+    // Early return if this component's defaultProps is already marked as "unresolved".
+    if (component.defaultProps === 'unresolved') {
+      return;
+    }
+
+    if (defaultProps === 'unresolved') {
+      markDefaultPropsAsUnresolved(component);
+      return;
+    }
+
+    const defaults = component.defaultProps || {};
+    const newDefaultProps = Object.assign(
+      {},
+      defaults,
+      fromEntries(defaultProps.map((prop) => [prop.name, prop]))
+    );
+
+    components.set(component.node, {
+      defaultProps: newDefaultProps,
+    });
+  }
+
+  return {
+    MemberExpression(node) {
+      const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node);
+
+      if (!isDefaultProp) {
+        return;
+      }
+
+      // find component this defaultProps belongs to
+      const component = utils.getRelatedComponent(node);
+      if (!component) {
+        return;
+      }
+
+      // e.g.:
+      // MyComponent.propTypes = {
+      //   foo: React.PropTypes.string.isRequired,
+      //   bar: React.PropTypes.string
+      // };
+      //
+      // or:
+      //
+      // MyComponent.propTypes = myPropTypes;
+      if (node.parent.type === 'AssignmentExpression') {
+        const expression = resolveNodeValue(node.parent.right);
+        if (!expression || expression.type !== 'ObjectExpression') {
+          // If a value can't be found, we mark the defaultProps declaration as "unresolved", because
+          // we should ignore this component and not report any errors for it, to avoid false-positives
+          // with e.g. external defaultProps declarations.
+          if (isDefaultProp) {
+            markDefaultPropsAsUnresolved(component);
+          }
+
+          return;
+        }
+
+        addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
+
+        return;
+      }
+
+      // e.g.:
+      // MyComponent.propTypes.baz = React.PropTypes.string;
+      if (node.parent.type === 'MemberExpression' && node.parent.parent
+        && node.parent.parent.type === 'AssignmentExpression') {
+        addDefaultPropsToComponent(component, [{
+          name: node.parent.property.name,
+          node: node.parent.parent,
+        }]);
+      }
+    },
+
+    // e.g.:
+    // class Hello extends React.Component {
+    //   static get defaultProps() {
+    //     return {
+    //       name: 'Dean'
+    //     };
+    //   }
+    //   render() {
+    //     return <div>Hello {this.props.name}</div>;
+    //   }
+    // }
+    MethodDefinition(node) {
+      if (!node.static || node.kind !== 'get') {
+        return;
+      }
+
+      if (!propsUtil.isDefaultPropsDeclaration(node)) {
+        return;
+      }
+
+      // find component this propTypes/defaultProps belongs to
+      const component = components.get(componentUtil.getParentES6Component(context, node));
+      if (!component) {
+        return;
+      }
+
+      const returnStatement = utils.findReturnStatement(node);
+      if (!returnStatement) {
+        return;
+      }
+
+      const expression = resolveNodeValue(returnStatement.argument);
+      if (!expression || expression.type !== 'ObjectExpression') {
+        return;
+      }
+
+      addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
+    },
+
+    // e.g.:
+    // class Greeting extends React.Component {
+    //   render() {
+    //     return (
+    //       <h1>Hello, {this.props.foo} {this.props.bar}</h1>
+    //     );
+    //   }
+    //   static defaultProps = {
+    //     foo: 'bar',
+    //     bar: 'baz'
+    //   };
+    // }
+    'ClassProperty, PropertyDefinition'(node) {
+      if (!(node.static && node.value)) {
+        return;
+      }
+
+      const propName = astUtil.getPropertyName(node);
+      const isDefaultProp = propName === 'defaultProps' || propName === 'getDefaultProps';
+
+      if (!isDefaultProp) {
+        return;
+      }
+
+      // find component this propTypes/defaultProps belongs to
+      const component = components.get(componentUtil.getParentES6Component(context, node));
+      if (!component) {
+        return;
+      }
+
+      const expression = resolveNodeValue(node.value);
+      if (!expression || expression.type !== 'ObjectExpression') {
+        return;
+      }
+
+      addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
+    },
+
+    // e.g.:
+    // React.createClass({
+    //   render: function() {
+    //     return <div>{this.props.foo}</div>;
+    //   },
+    //   getDefaultProps: function() {
+    //     return {
+    //       foo: 'default'
+    //     };
+    //   }
+    // });
+    ObjectExpression(node) {
+      // find component this propTypes/defaultProps belongs to
+      const component = componentUtil.isES5Component(node, context) && components.get(node);
+      if (!component) {
+        return;
+      }
+
+      // Search for the proptypes declaration
+      node.properties.forEach((property) => {
+        if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
+          return;
+        }
+
+        const isDefaultProp = propsUtil.isDefaultPropsDeclaration(property);
+
+        if (isDefaultProp && property.value.type === 'FunctionExpression') {
+          const returnStatement = utils.findReturnStatement(property);
+          if (!returnStatement || returnStatement.argument.type !== 'ObjectExpression') {
+            return;
+          }
+
+          addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(returnStatement.argument));
+        }
+      });
+    },
+  };
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+export = docsUrl;
+declare function docsUrl(ruleName: any): string;
+//# sourceMappingURL=docsUrl.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"docsUrl.d.ts","sourceRoot":"","sources":["docsUrl.js"],"names":[],"mappings":";AAEA,gDAEC"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/docsUrl.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+'use strict';
+
+function docsUrl(ruleName) {
+  return `https://github.com/jsx-eslint/eslint-plugin-react/tree/master/docs/rules/${ruleName}.md`;
+}
+
+module.exports = docsUrl;
Index: frontend/node_modules/eslint-plugin-react/lib/util/error.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/error.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/error.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+export = error;
+/**
+ * Logs out a message if there is no format option set.
+ * @param {string} message - Message to log.
+ */
+declare function error(message: string): void;
+//# sourceMappingURL=error.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/error.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/error.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/error.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["error.js"],"names":[],"mappings":";AAEA;;;GAGG;AACH,gCAFW,MAAM,QAOhB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/error.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/error.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/error.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,14 @@
+'use strict';
+
+/**
+ * Logs out a message if there is no format option set.
+ * @param {string} message - Message to log.
+ */
+function error(message) {
+  if (!/=-(f|-format)=/.test(process.argv.join('='))) {
+    // eslint-disable-next-line no-console
+    console.error(message);
+  }
+}
+
+module.exports = error;
Index: frontend/node_modules/eslint-plugin-react/lib/util/eslint.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/eslint.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/eslint.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+export function getAncestors(context: any, node: any): any;
+export function getFirstTokens(context: any, node: any, count: any): any;
+export function getScope(context: any, node: any): any;
+export function getSourceCode(context: any): any;
+export function getText(context: any, ...args: any[]): any;
+export function markVariableAsUsed(name: any, node: any, context: any): any;
+//# sourceMappingURL=eslint.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/eslint.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/eslint.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/eslint.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["eslint.js"],"names":[],"mappings":"AAMA,2DAGC;AAkBD,yEAGC;AAnBD,uDAOC;AAhBD,iDAEC;AA4BD,2DAIC;AAhBD,4EAKC"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/eslint.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/eslint.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/eslint.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,46 @@
+'use strict';
+
+function getSourceCode(context) {
+  return context.getSourceCode ? context.getSourceCode() : context.sourceCode;
+}
+
+function getAncestors(context, node) {
+  const sourceCode = getSourceCode(context);
+  return sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors();
+}
+
+function getScope(context, node) {
+  const sourceCode = getSourceCode(context);
+  if (sourceCode.getScope) {
+    return sourceCode.getScope(node);
+  }
+
+  return context.getScope();
+}
+
+function markVariableAsUsed(name, node, context) {
+  const sourceCode = getSourceCode(context);
+  return sourceCode.markVariableAsUsed
+    ? sourceCode.markVariableAsUsed(name, node)
+    : context.markVariableAsUsed(name);
+}
+
+function getFirstTokens(context, node, count) {
+  const sourceCode = getSourceCode(context);
+  return sourceCode.getFirstTokens ? sourceCode.getFirstTokens(node, count) : context.getFirstTokens(node, count);
+}
+
+function getText(context) {
+  const sourceCode = getSourceCode(context);
+  const args = Array.prototype.slice.call(arguments, 1);
+  return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args);
+}
+
+module.exports = {
+  getAncestors,
+  getFirstTokens,
+  getScope,
+  getSourceCode,
+  getText,
+  markVariableAsUsed,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,8 @@
+export = getTokenBeforeClosingBracket;
+/**
+ * Find the token before the closing bracket.
+ * @param {ASTNode} node - The JSX element node.
+ * @returns {Token} The token before the closing bracket.
+ */
+declare function getTokenBeforeClosingBracket(node: ASTNode): Token;
+//# sourceMappingURL=getTokenBeforeClosingBracket.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"getTokenBeforeClosingBracket.d.ts","sourceRoot":"","sources":["getTokenBeforeClosingBracket.js"],"names":[],"mappings":";AAEA;;;;GAIG;AACH,oDAHW,OAAO,GACL,KAAK,CAQjB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+'use strict';
+
+/**
+ * Find the token before the closing bracket.
+ * @param {ASTNode} node - The JSX element node.
+ * @returns {Token} The token before the closing bracket.
+ */
+function getTokenBeforeClosingBracket(node) {
+  const attributes = node.attributes;
+  if (!attributes || attributes.length === 0) {
+    return node.name;
+  }
+  return attributes[attributes.length - 1];
+}
+
+module.exports = getTokenBeforeClosingBracket;
Index: frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare function _exports(node: ASTNode): boolean;
+export = _exports;
+//# sourceMappingURL=isCreateContext.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"isCreateContext.d.ts","sourceRoot":"","sources":["isCreateContext.js"],"names":[],"mappings":"AASiB,gCAHN,OAAO,GACL,OAAO,CA8CnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isCreateContext.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,54 @@
+'use strict';
+
+const astUtil = require('./ast');
+
+/**
+ * Checks if the node is a React.createContext call
+ * @param {ASTNode} node - The AST node being checked.
+ * @returns {boolean} - True if node is a React.createContext call, false if not.
+ */
+module.exports = function isCreateContext(node) {
+  if (
+    node.init
+    && node.init.callee
+  ) {
+    if (
+      astUtil.isCallExpression(node.init)
+      && node.init.callee.name === 'createContext'
+    ) {
+      return true;
+    }
+
+    if (
+      node.init.callee.type === 'MemberExpression'
+      && node.init.callee.property
+      && node.init.callee.property.name === 'createContext'
+    ) {
+      return true;
+    }
+  }
+
+  if (
+    node.expression
+    && node.expression.type === 'AssignmentExpression'
+    && node.expression.operator === '='
+    && astUtil.isCallExpression(node.expression.right)
+    && node.expression.right.callee
+  ) {
+    const right = node.expression.right;
+
+    if (right.callee.name === 'createContext') {
+      return true;
+    }
+
+    if (
+      right.callee.type === 'MemberExpression'
+      && right.callee.property
+      && right.callee.property.name === 'createContext'
+    ) {
+      return true;
+    }
+  }
+
+  return false;
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare function _exports(context: Context, node: ASTNode): boolean;
+export = _exports;
+//# sourceMappingURL=isCreateElement.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"isCreateElement.d.ts","sourceRoot":"","sources":["isCreateElement.js"],"names":[],"mappings":"AAWiB,mCAJN,OAAO,QACP,OAAO,GACL,OAAO,CAwBnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isCreateElement.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,34 @@
+'use strict';
+
+const pragmaUtil = require('./pragma');
+const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport');
+
+/**
+ * Checks if the node is a createElement call
+ * @param {Context} context - The AST node being checked.
+ * @param {ASTNode} node - The AST node being checked.
+ * @returns {boolean} - True if node is a createElement call object literal, False if not.
+*/
+module.exports = function isCreateElement(context, node) {
+  if (!node.callee) {
+    return false;
+  }
+
+  if (
+    node.callee.type === 'MemberExpression'
+    && node.callee.property.name === 'createElement'
+    && node.callee.object
+    && node.callee.object.name === pragmaUtil.getFromContext(context)
+  ) {
+    return true;
+  }
+
+  if (
+    node.callee.name === 'createElement'
+    && isDestructuredFromPragmaImport(context, node, 'createElement')
+  ) {
+    return true;
+  }
+
+  return false;
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare function _exports(context: Context, node: ASTNode, variable: string, ...args: any[]): boolean;
+export = _exports;
+//# sourceMappingURL=isDestructuredFromPragmaImport.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"isDestructuredFromPragmaImport.d.ts","sourceRoot":"","sources":["isDestructuredFromPragmaImport.js"],"names":[],"mappings":"AAciB,mCALN,OAAO,QACP,OAAO,YACP,MAAM,mBACJ,OAAO,CAmEnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,80 @@
+'use strict';
+
+const astUtil = require('./ast');
+const pragmaUtil = require('./pragma');
+const variableUtil = require('./variable');
+
+/**
+ * Check if variable is destructured from pragma import
+ *
+ * @param {Context} context eslint context
+ * @param {ASTNode} node The AST node to check
+ * @param {string} variable The variable name to check
+ * @returns {boolean} True if createElement is destructured from the pragma
+ */
+module.exports = function isDestructuredFromPragmaImport(context, node, variable) {
+  const pragma = pragmaUtil.getFromContext(context);
+  const variableInScope = variableUtil.getVariableFromContext(context, node, variable);
+  if (variableInScope) {
+    const latestDef = variableUtil.getLatestVariableDefinition(variableInScope);
+    if (latestDef) {
+      // check if latest definition is a variable declaration: 'variable = value'
+      if (latestDef.node.type === 'VariableDeclarator' && latestDef.node.init) {
+        // check for: 'variable = pragma.variable'
+        if (
+          latestDef.node.init.type === 'MemberExpression'
+          && latestDef.node.init.object.type === 'Identifier'
+          && latestDef.node.init.object.name === pragma
+        ) {
+          return true;
+        }
+        // check for: '{variable} = pragma'
+        if (
+          latestDef.node.init.type === 'Identifier'
+          && latestDef.node.init.name === pragma
+        ) {
+          return true;
+        }
+
+        // "require('react')"
+        let requireExpression = null;
+
+        // get "require('react')" from: "{variable} = require('react')"
+        if (astUtil.isCallExpression(latestDef.node.init)) {
+          requireExpression = latestDef.node.init;
+        }
+        // get "require('react')" from: "variable = require('react').variable"
+        if (
+          !requireExpression
+          && latestDef.node.init.type === 'MemberExpression'
+          && astUtil.isCallExpression(latestDef.node.init.object)
+        ) {
+          requireExpression = latestDef.node.init.object;
+        }
+
+        // check proper require.
+        if (
+          requireExpression
+          && requireExpression.callee
+          && requireExpression.callee.name === 'require'
+          && requireExpression.arguments[0]
+          && requireExpression.arguments[0].value === pragma.toLocaleLowerCase()
+        ) {
+          return true;
+        }
+
+        return false;
+      }
+
+      // latest definition is an import declaration: import {<variable>} from 'react'
+      if (
+        latestDef.parent
+        && latestDef.parent.type === 'ImportDeclaration'
+        && latestDef.parent.source.value === pragma.toLocaleLowerCase()
+      ) {
+        return true;
+      }
+    }
+  }
+  return false;
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare function _exports(word: string): boolean;
+export = _exports;
+//# sourceMappingURL=isFirstLetterCapitalized.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"isFirstLetterCapitalized.d.ts","sourceRoot":"","sources":["isFirstLetterCapitalized.js"],"names":[],"mappings":"AAOiB,gCAHN,MAAM,GACJ,OAAO,CAQnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,14 @@
+'use strict';
+
+/**
+ * Check if the first letter of a string is capitalized.
+ * @param {string} word String to check
+ * @returns {boolean} True if first letter is capitalized.
+ */
+module.exports = function isFirstLetterCapitalized(word) {
+  if (!word) {
+    return false;
+  }
+  const firstLetter = word.replace(/^_+/, '').charAt(0);
+  return firstLetter.toUpperCase() === firstLetter;
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/jsx.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/jsx.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/jsx.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,51 @@
+/**
+ * Checks if a node represents a DOM element according to React.
+ * @param {object} node - JSXOpeningElement to check.
+ * @returns {boolean} Whether or not the node corresponds to a DOM element.
+ */
+export function isDOMComponent(node: object): boolean;
+/**
+ * Test whether a JSXElement is a fragment
+ * @param {JSXElement} node
+ * @param {string} reactPragma
+ * @param {string} fragmentPragma
+ * @returns {boolean}
+ */
+export function isFragment(node: JSXElement, reactPragma: string, fragmentPragma: string): boolean;
+/**
+ * Checks if a node represents a JSX element or fragment.
+ * @param {object} node - node to check.
+ * @returns {boolean} Whether or not the node if a JSX element or fragment.
+ */
+export function isJSX(node: object): boolean;
+/**
+ * Check if node is like `key={...}` as in `<Foo key={...} />`
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+export function isJSXAttributeKey(node: ASTNode): boolean;
+/**
+ * Check if value has only whitespaces
+ * @param {unknown} value
+ * @returns {boolean}
+ */
+export function isWhiteSpaces(value: unknown): boolean;
+/**
+ * Check if the node is returning JSX or null
+ *
+ * @param {Context} context The context of `ASTNode`.
+ * @param {ASTNode} ASTnode The AST node being checked
+ * @param {boolean} [strict] If true, in a ternary condition the node must return JSX in both cases
+ * @param {boolean} [ignoreNull] If true, null return values will be ignored
+ * @returns {boolean} True if the node is returning JSX or null, false if not
+ */
+export function isReturningJSX(context: Context, ASTnode: ASTNode, strict?: boolean, ignoreNull?: boolean): boolean;
+/**
+ * Check if the node is returning only null values
+ *
+ * @param {ASTNode} ASTnode The AST node being checked
+ * @param {Context} context The context of `ASTNode`.
+ * @returns {boolean} True if the node is returning only null values
+ */
+export function isReturningOnlyNull(ASTnode: ASTNode, context: Context): boolean;
+//# sourceMappingURL=jsx.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/jsx.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/jsx.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/jsx.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"jsx.d.ts","sourceRoot":"","sources":["jsx.js"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,qCAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;;;GAMG;AACH,iCALW,UAAU,eACV,MAAM,kBACN,MAAM,GACJ,OAAO,CAsBnB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAInB;AAED;;;;GAIG;AACH,wCAHW,OAAO,GACL,OAAO,CAOnB;AAED;;;;GAIG;AACH,qCAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;;;;;GAQG;AACH,wCANW,OAAO,WACP,OAAO,WACP,OAAO,eACP,OAAO,GACL,OAAO,CAgDnB;AAED;;;;;;GAMG;AACH,6CAJW,OAAO,WACP,OAAO,GACL,OAAO,CAsCnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/jsx.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/jsx.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/jsx.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,196 @@
+/**
+ * @fileoverview Utility functions for JSX
+ */
+
+'use strict';
+
+const elementType = require('jsx-ast-utils/elementType');
+
+const astUtil = require('./ast');
+const isCreateElement = require('./isCreateElement');
+const variableUtil = require('./variable');
+
+// See https://github.com/babel/babel/blob/ce420ba51c68591e057696ef43e028f41c6e04cd/packages/babel-types/src/validators/react/isCompatTag.js
+// for why we only test for the first character
+const COMPAT_TAG_REGEX = /^[a-z]/;
+
+/**
+ * Checks if a node represents a DOM element according to React.
+ * @param {object} node - JSXOpeningElement to check.
+ * @returns {boolean} Whether or not the node corresponds to a DOM element.
+ */
+function isDOMComponent(node) {
+  const name = elementType(node);
+  return COMPAT_TAG_REGEX.test(name);
+}
+
+/**
+ * Test whether a JSXElement is a fragment
+ * @param {JSXElement} node
+ * @param {string} reactPragma
+ * @param {string} fragmentPragma
+ * @returns {boolean}
+ */
+function isFragment(node, reactPragma, fragmentPragma) {
+  const name = node.openingElement.name;
+
+  // <Fragment>
+  if (name.type === 'JSXIdentifier' && name.name === fragmentPragma) {
+    return true;
+  }
+
+  // <React.Fragment>
+  if (
+    name.type === 'JSXMemberExpression'
+    && name.object.type === 'JSXIdentifier'
+    && name.object.name === reactPragma
+    && name.property.type === 'JSXIdentifier'
+    && name.property.name === fragmentPragma
+  ) {
+    return true;
+  }
+
+  return false;
+}
+
+/**
+ * Checks if a node represents a JSX element or fragment.
+ * @param {object} node - node to check.
+ * @returns {boolean} Whether or not the node if a JSX element or fragment.
+ */
+function isJSX(node) {
+  return node && ['JSXElement', 'JSXFragment'].indexOf(node.type) >= 0;
+}
+
+/**
+ * Check if node is like `key={...}` as in `<Foo key={...} />`
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function isJSXAttributeKey(node) {
+  return node.type === 'JSXAttribute'
+    && node.name
+    && node.name.type === 'JSXIdentifier'
+    && node.name.name === 'key';
+}
+
+/**
+ * Check if value has only whitespaces
+ * @param {unknown} value
+ * @returns {boolean}
+ */
+function isWhiteSpaces(value) {
+  return typeof value === 'string' ? /^\s*$/.test(value) : false;
+}
+
+/**
+ * Check if the node is returning JSX or null
+ *
+ * @param {Context} context The context of `ASTNode`.
+ * @param {ASTNode} ASTnode The AST node being checked
+ * @param {boolean} [strict] If true, in a ternary condition the node must return JSX in both cases
+ * @param {boolean} [ignoreNull] If true, null return values will be ignored
+ * @returns {boolean} True if the node is returning JSX or null, false if not
+ */
+function isReturningJSX(context, ASTnode, strict, ignoreNull) {
+  const isJSXValue = (node) => {
+    if (!node) {
+      return false;
+    }
+    switch (node.type) {
+      case 'ConditionalExpression':
+        if (strict) {
+          return isJSXValue(node.consequent) && isJSXValue(node.alternate);
+        }
+        return isJSXValue(node.consequent) || isJSXValue(node.alternate);
+      case 'LogicalExpression':
+        if (strict) {
+          return isJSXValue(node.left) && isJSXValue(node.right);
+        }
+        return isJSXValue(node.left) || isJSXValue(node.right);
+      case 'SequenceExpression':
+        return isJSXValue(node.expressions[node.expressions.length - 1]);
+      case 'JSXElement':
+      case 'JSXFragment':
+        return true;
+      case 'CallExpression':
+        return isCreateElement(context, node);
+      case 'Literal':
+        if (!ignoreNull && node.value === null) {
+          return true;
+        }
+        return false;
+      case 'Identifier': {
+        const variable = variableUtil.findVariableByName(context, node, node.name);
+        return isJSX(variable);
+      }
+      default:
+        return false;
+    }
+  };
+
+  let found = false;
+  astUtil.traverseReturns(ASTnode, context, (node, breakTraverse) => {
+    if (isJSXValue(node)) {
+      found = true;
+      breakTraverse();
+    }
+  });
+
+  return found;
+}
+
+/**
+ * Check if the node is returning only null values
+ *
+ * @param {ASTNode} ASTnode The AST node being checked
+ * @param {Context} context The context of `ASTNode`.
+ * @returns {boolean} True if the node is returning only null values
+ */
+function isReturningOnlyNull(ASTnode, context) {
+  let found = false;
+  let foundSomethingElse = false;
+  astUtil.traverseReturns(ASTnode, context, (node) => {
+    // Traverse return statement
+    astUtil.traverse(node, {
+      enter(childNode) {
+        const setFound = () => {
+          found = true;
+          this.skip();
+        };
+        const setFoundSomethingElse = () => {
+          foundSomethingElse = true;
+          this.skip();
+        };
+        switch (childNode.type) {
+          case 'ReturnStatement':
+            break;
+          case 'ConditionalExpression':
+            if (childNode.consequent.value === null && childNode.alternate.value === null) {
+              setFound();
+            }
+            break;
+          case 'Literal':
+            if (childNode.value === null) {
+              setFound();
+            }
+            break;
+          default:
+            setFoundSomethingElse();
+        }
+      },
+    });
+  });
+
+  return found && !foundSomethingElse;
+}
+
+module.exports = {
+  isDOMComponent,
+  isFragment,
+  isJSX,
+  isJSXAttributeKey,
+  isWhiteSpaces,
+  isReturningJSX,
+  isReturningOnlyNull,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,6 @@
+declare const _exports: {
+    instance: string[];
+    static: string[];
+};
+export = _exports;
+//# sourceMappingURL=lifecycleMethods.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"lifecycleMethods.d.ts","sourceRoot":"","sources":["lifecycleMethods.js"],"names":[],"mappings":""}
Index: frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,30 @@
+/**
+ * @fileoverview lifecycle methods
+ * @author Tan Nguyen
+ */
+
+'use strict';
+
+module.exports = {
+  instance: [
+    'getDefaultProps',
+    'getInitialState',
+    'getChildContext',
+    'componentWillMount',
+    'UNSAFE_componentWillMount',
+    'componentDidMount',
+    'componentWillReceiveProps',
+    'UNSAFE_componentWillReceiveProps',
+    'shouldComponentUpdate',
+    'componentWillUpdate',
+    'UNSAFE_componentWillUpdate',
+    'getSnapshotBeforeUpdate',
+    'componentDidUpdate',
+    'componentDidCatch',
+    'componentWillUnmount',
+    'render',
+  ],
+  static: [
+    'getDerivedStateFromProps',
+  ],
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+export function getFormComponents(context: any): Map<any, any>;
+export function getLinkComponents(context: any): Map<any, any>;
+//# sourceMappingURL=linkComponents.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"linkComponents.d.ts","sourceRoot":"","sources":["linkComponents.js"],"names":[],"mappings":"AAmBA,+DAWC;AAED,+DAWC"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/linkComponents.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,49 @@
+/**
+ * @fileoverview Utility functions for propWrapperFunctions setting
+ */
+
+'use strict';
+
+const iterFrom = require('es-iterator-helpers/Iterator.from');
+const map = require('es-iterator-helpers/Iterator.prototype.map');
+
+/** TODO: type {(string | { name: string, linkAttribute: string })[]} */
+/** @type {any} */
+const DEFAULT_LINK_COMPONENTS = ['a'];
+const DEFAULT_LINK_ATTRIBUTE = 'href';
+
+/** TODO: type {(string | { name: string, formAttribute: string })[]} */
+/** @type {any} */
+const DEFAULT_FORM_COMPONENTS = ['form'];
+const DEFAULT_FORM_ATTRIBUTE = 'action';
+
+function getFormComponents(context) {
+  const settings = context.settings || {};
+  const formComponents = /** @type {typeof DEFAULT_FORM_COMPONENTS} */ (
+    DEFAULT_FORM_COMPONENTS.concat(settings.formComponents || [])
+  );
+  return new Map(map(iterFrom(formComponents), (value) => {
+    if (typeof value === 'string') {
+      return [value, [DEFAULT_FORM_ATTRIBUTE]];
+    }
+    return [value.name, [].concat(value.formAttribute)];
+  }));
+}
+
+function getLinkComponents(context) {
+  const settings = context.settings || {};
+  const linkComponents = /** @type {typeof DEFAULT_LINK_COMPONENTS} */ (
+    DEFAULT_LINK_COMPONENTS.concat(settings.linkComponents || [])
+  );
+  return new Map(map(iterFrom(linkComponents), (value) => {
+    if (typeof value === 'string') {
+      return [value, [DEFAULT_LINK_ATTRIBUTE]];
+    }
+    return [value.name, [].concat(value.linkAttribute)];
+  }));
+}
+
+module.exports = {
+  getFormComponents,
+  getLinkComponents,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/log.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/log.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/log.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+export = log;
+/**
+ * Logs out a message if there is no format option set.
+ * @param {string} message - Message to log.
+ */
+declare function log(message: string): void;
+//# sourceMappingURL=log.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/log.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/log.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/log.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["log.js"],"names":[],"mappings":";AAEA;;;GAGG;AACH,8BAFW,MAAM,QAOhB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/log.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/log.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/log.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,14 @@
+'use strict';
+
+/**
+ * Logs out a message if there is no format option set.
+ * @param {string} message - Message to log.
+ */
+function log(message) {
+  if (!/=-(f|-format)=/.test(process.argv.join('='))) {
+    // eslint-disable-next-line no-console
+    console.log(message);
+  }
+}
+
+module.exports = log;
Index: frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare function _exports(methodName: string, shouldCheckUnsafeCb?: (context: import('eslint').Rule.RuleContext) => boolean): import('eslint').Rule.RuleModule;
+export = _exports;
+//# sourceMappingURL=makeNoMethodSetStateRule.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"makeNoMethodSetStateRule.d.ts","sourceRoot":"","sources":["makeNoMethodSetStateRule.js"],"names":[],"mappings":"AAoDiB,sCAJN,MAAM,kCACI,OAAO,QAAQ,EAAE,IAAI,CAAC,WAAW,KAAK,OAAO,GACrD,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU,CA+E5C"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,130 @@
+/**
+ * @fileoverview Prevent usage of setState in lifecycle methods
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const findLast = require('array.prototype.findlast');
+
+const docsUrl = require('./docsUrl');
+const report = require('./report');
+const getAncestors = require('./eslint').getAncestors;
+const testReactVersion = require('./version').testReactVersion;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function mapTitle(methodName) {
+  const map = {
+    componentDidMount: 'did-mount',
+    componentDidUpdate: 'did-update',
+    componentWillUpdate: 'will-update',
+  };
+  const title = map[methodName];
+  if (!title) {
+    throw Error(`No docsUrl for '${methodName}'`);
+  }
+  return `no-${title}-set-state`;
+}
+
+const messages = {
+  noSetState: 'Do not use setState in {{name}}',
+};
+
+const methodNoopsAsOf = {
+  componentDidMount: '>= 16.3.0',
+  componentDidUpdate: '>= 16.3.0',
+};
+
+function shouldBeNoop(context, methodName) {
+  return methodName in methodNoopsAsOf
+    && testReactVersion(context, methodNoopsAsOf[methodName])
+    && !testReactVersion(context, '999.999.999'); // for when the version is not specified
+}
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * @param {string} methodName
+ * @param {(context: import('eslint').Rule.RuleContext) => boolean} [shouldCheckUnsafeCb]
+ * @returns {import('eslint').Rule.RuleModule}
+ */
+module.exports = function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) {
+  return {
+    meta: {
+      docs: {
+        description: `Disallow usage of setState in ${methodName}`,
+        category: 'Best Practices',
+        recommended: false,
+        url: docsUrl(mapTitle(methodName)),
+      },
+
+      messages,
+
+      schema: [{
+        enum: ['disallow-in-func'],
+      }],
+    },
+
+    create(context) {
+      const mode = context.options[0] || 'allow-in-func';
+
+      function nameMatches(name) {
+        if (name === methodName) {
+          return true;
+        }
+
+        if (typeof shouldCheckUnsafeCb === 'function' && shouldCheckUnsafeCb(context)) {
+          return name === `UNSAFE_${methodName}`;
+        }
+
+        return false;
+      }
+
+      if (shouldBeNoop(context, methodName)) {
+        return {};
+      }
+
+      // --------------------------------------------------------------------------
+      // Public
+      // --------------------------------------------------------------------------
+
+      return {
+        CallExpression(node) {
+          const callee = node.callee;
+          if (
+            callee.type !== 'MemberExpression'
+            || callee.object.type !== 'ThisExpression'
+            || !('name' in callee.property)
+            || callee.property.name !== 'setState'
+          ) {
+            return;
+          }
+          const ancestors = getAncestors(context, node);
+          let depth = 0;
+          findLast(ancestors, (ancestor) => {
+          // ancestors.some((ancestor) => {
+            if (/Function(Expression|Declaration)$/.test(ancestor.type)) {
+              depth += 1;
+            }
+            if (
+              (ancestor.type !== 'Property' && ancestor.type !== 'MethodDefinition' && ancestor.type !== 'ClassProperty' && ancestor.type !== 'PropertyDefinition')
+              || !nameMatches(ancestor.key.name)
+              || (mode !== 'disallow-in-func' && depth > 1)
+            ) {
+              return false;
+            }
+            report(context, messages.noSetState, 'noSetState', {
+              node: callee,
+              data: {
+                name: ancestor.key.name,
+              },
+            });
+            return true;
+          });
+        },
+      };
+    },
+  };
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/message.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/message.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/message.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,9 @@
+declare function _exports(messageId: any, message: any): {
+    messageId: any;
+    message?: undefined;
+} | {
+    message: any;
+    messageId?: undefined;
+};
+export = _exports;
+//# sourceMappingURL=message.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/message.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/message.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/message.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["message.js"],"names":[],"mappings":"AAKiB;;;;;;EAEhB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/message.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/message.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/message.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,8 @@
+'use strict';
+
+const semver = require('semver');
+const eslintPkg = require('eslint/package.json');
+
+module.exports = function getMessageData(messageId, message) {
+  return messageId && semver.satisfies(eslintPkg.version, '>= 4.15') ? { messageId } : { message };
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/pragma.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/pragma.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/pragma.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+/**
+ * @param {Context} context
+ * @returns {string}
+ */
+export function getCreateClassFromContext(context: Context): string;
+/**
+ * @param {Context} context
+ * @returns {string}
+ */
+export function getFragmentFromContext(context: Context): string;
+/**
+ * @param {Context} context
+ * @returns {string}
+ */
+export function getFromContext(context: Context): string;
+//# sourceMappingURL=pragma.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/pragma.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/pragma.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/pragma.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"pragma.d.ts","sourceRoot":"","sources":["pragma.js"],"names":[],"mappings":"AAaA;;;GAGG;AACH,mDAHW,OAAO,GACL,MAAM,CAYlB;AAED;;;GAGG;AACH,gDAHW,OAAO,GACL,MAAM,CAYlB;AAED;;;GAGG;AACH,wCAHW,OAAO,GACL,MAAM,CAqBlB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/pragma.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/pragma.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/pragma.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,75 @@
+/**
+ * @fileoverview Utility functions for React pragma configuration
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const getSourceCode = require('./eslint').getSourceCode;
+
+const JSX_ANNOTATION_REGEX = /@jsx\s+([^\s]+)/;
+// Does not check for reserved keywords or unicode characters
+const JS_IDENTIFIER_REGEX = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/;
+
+/**
+ * @param {Context} context
+ * @returns {string}
+ */
+function getCreateClassFromContext(context) {
+  let pragma = 'createReactClass';
+  // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+  if (context.settings.react && context.settings.react.createClass) {
+    pragma = context.settings.react.createClass;
+  }
+  if (!JS_IDENTIFIER_REGEX.test(pragma)) {
+    throw new Error(`createClass pragma ${pragma} is not a valid function name`);
+  }
+  return pragma;
+}
+
+/**
+ * @param {Context} context
+ * @returns {string}
+ */
+function getFragmentFromContext(context) {
+  let pragma = 'Fragment';
+  // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+  if (context.settings.react && context.settings.react.fragment) {
+    pragma = context.settings.react.fragment;
+  }
+  if (!JS_IDENTIFIER_REGEX.test(pragma)) {
+    throw new Error(`Fragment pragma ${pragma} is not a valid identifier`);
+  }
+  return pragma;
+}
+
+/**
+ * @param {Context} context
+ * @returns {string}
+ */
+function getFromContext(context) {
+  let pragma = 'React';
+
+  const sourceCode = getSourceCode(context);
+  const pragmaNode = sourceCode.getAllComments().find((node) => JSX_ANNOTATION_REGEX.test(node.value));
+
+  if (pragmaNode) {
+    const matches = JSX_ANNOTATION_REGEX.exec(pragmaNode.value);
+    pragma = matches[1].split('.')[0];
+    // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+  } else if (context.settings.react && context.settings.react.pragma) {
+    pragma = context.settings.react.pragma;
+  }
+
+  if (!JS_IDENTIFIER_REGEX.test(pragma)) {
+    console.warn(`React pragma ${pragma} is not a valid identifier`);
+    return 'React';
+  }
+  return pragma;
+}
+
+module.exports = {
+  getCreateClassFromContext,
+  getFragmentFromContext,
+  getFromContext,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+declare function _exports(context: any, components: any, utils: any): {
+    ClassExpression(node: any): void;
+    ClassDeclaration(node: any): void;
+    'ClassProperty, PropertyDefinition'(node: any): void;
+    ObjectExpression(node: any): void;
+    FunctionExpression(node: any): void;
+    ImportDeclaration(node: any): void;
+    FunctionDeclaration: (node: ASTNode, rootNode: ASTNode) => void;
+    ArrowFunctionExpression: (node: ASTNode, rootNode: ASTNode) => void;
+    MemberExpression(node: any): void;
+    MethodDefinition(node: any): void;
+    TypeAlias(node: any): void;
+    TypeParameterDeclaration(node: any): void;
+    Program(): void;
+    BlockStatement(): void;
+    'BlockStatement:exit'(): void;
+    'Program:exit'(): void;
+};
+export = _exports;
+//# sourceMappingURL=propTypes.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"propTypes.d.ts","sourceRoot":"","sources":["propTypes.js"],"names":[],"mappings":"AAoGiB;;;;;;;gCA06BJ,OAAO,YAEP,OAAO;oCAFP,OAAO,YAEP,OAAO;;;;;;;;;EAqQnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/propTypes.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propTypes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propTypes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1302 @@
+/**
+ * @fileoverview Common propTypes detection functionality.
+ */
+
+'use strict';
+
+const flatMap = require('array.prototype.flatmap');
+
+const annotations = require('./annotations');
+const propsUtil = require('./props');
+const variableUtil = require('./variable');
+const testFlowVersion = require('./version').testFlowVersion;
+const propWrapperUtil = require('./propWrapper');
+const astUtil = require('./ast');
+const isFirstLetterCapitalized = require('./isFirstLetterCapitalized');
+const eslintUtil = require('./eslint');
+
+const getFirstTokens = eslintUtil.getFirstTokens;
+const getScope = eslintUtil.getScope;
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+/**
+ * Check if node is function type.
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function isFunctionType(node) {
+  if (!node) return false;
+  const nodeType = node.type;
+  return nodeType === 'FunctionDeclaration'
+    || nodeType === 'FunctionExpression'
+    || nodeType === 'ArrowFunctionExpression';
+}
+
+/**
+ * Checks if we are declaring a props as a generic type in a flow-annotated class.
+ *
+ * @param {ASTNode} node  the AST node being checked.
+ * @returns {boolean} True if the node is a class with generic prop types, false if not.
+ */
+function isSuperTypeParameterPropsDeclaration(node) {
+  if (node && (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')) {
+    const parameters = propsUtil.getSuperTypeArguments(node);
+    if (parameters && parameters.params.length > 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Iterates through a properties node, like a customized forEach.
+ * @param {Object} context Array of properties to iterate.
+ * @param {Object[]} properties Array of properties to iterate.
+ * @param {Function} fn Function to call on each property, receives property key
+    and property value. (key, value) => void
+  * @param {Function} [handleSpreadFn] Function to call on each ObjectTypeSpreadProperty, receives the
+    argument
+ */
+function iterateProperties(context, properties, fn, handleSpreadFn) {
+  if (properties && properties.length && typeof fn === 'function') {
+    for (let i = 0, j = properties.length; i < j; i++) {
+      const node = properties[i];
+      const key = astUtil.getKeyValue(context, node);
+
+      if (node.type === 'ObjectTypeSpreadProperty' && typeof handleSpreadFn === 'function') {
+        handleSpreadFn(node.argument);
+      }
+
+      const value = node.value;
+      fn(key, value, node);
+    }
+  }
+}
+
+/**
+ * Checks if a node is inside a class body.
+ *
+ * @param {ASTNode} node the AST node being checked.
+ * @returns {boolean} True if the node has a ClassBody ancestor, false if not.
+ */
+function isInsideClassBody(node) {
+  let parent = node.parent;
+  while (parent) {
+    if (parent.type === 'ClassBody') {
+      return true;
+    }
+    parent = parent.parent;
+  }
+  return false;
+}
+
+function startWithCapitalizedLetter(node) {
+  return (
+    node.parent.type === 'VariableDeclarator'
+    && !isFirstLetterCapitalized(node.parent.id.name)
+  );
+}
+
+module.exports = function propTypesInstructions(context, components, utils) {
+  // Used to track the type annotations in scope.
+  // Necessary because babel's scopes do not track type annotations.
+  let stack = null;
+
+  const classExpressions = [];
+  const defaults = { customValidators: [] };
+  const configuration = Object.assign({}, defaults, context.options[0] || {});
+  const customValidators = configuration.customValidators;
+  const allowedGenericTypes = new Set(['ComponentProps', 'ComponentPropsWithRef', 'ComponentPropsWithoutRef', 'forwardRef', 'ForwardRefRenderFunction', 'VFC', 'VoidFunctionComponent', 'PropsWithChildren', 'SFC', 'StatelessComponent', 'FunctionComponent', 'FC']);
+  const genericTypeParamIndexWherePropsArePresent = {
+    ComponentProps: 0,
+    ComponentPropsWithRef: 0,
+    ComponentPropsWithoutRef: 0,
+    ForwardRefRenderFunction: 1,
+    forwardRef: 1,
+    VoidFunctionComponent: 0,
+    VFC: 0,
+    PropsWithChildren: 0,
+    SFC: 0,
+    StatelessComponent: 0,
+    FunctionComponent: 0,
+    FC: 0,
+  };
+  const genericReactTypesImport = new Set();
+  // import { FC as X } from 'react' -> localToImportedMap = { x: FC }
+  const localToImportedMap = {};
+
+  /**
+   * Returns the full scope.
+   * @returns {Object} The whole scope.
+   */
+  function typeScope() {
+    return stack[stack.length - 1];
+  }
+
+  /**
+   * Gets a node from the scope.
+   * @param {string} key The name of the identifier to access.
+   * @returns {ASTNode} The ASTNode associated with the given identifier.
+   */
+  function getInTypeScope(key) {
+    return stack[stack.length - 1][key];
+  }
+
+  /**
+   * Sets the new value in the scope.
+   * @param {string} key The name of the identifier to access
+   * @param {ASTNode} value The new value for the identifier.
+   * @returns {ASTNode} The ASTNode associated with the given identifier.
+   */
+  function setInTypeScope(key, value) {
+    stack[stack.length - 1][key] = value;
+    return value;
+  }
+
+  /**
+   * Checks if prop should be validated by plugin-react-proptypes
+   * @param {string} validator Name of validator to check.
+   * @returns {boolean} True if validator should be checked by custom validator.
+   */
+  function hasCustomValidator(validator) {
+    return customValidators.indexOf(validator) !== -1;
+  }
+
+  /* eslint-disable no-use-before-define */
+  /** @type {TypeDeclarationBuilders} */
+  const typeDeclarationBuilders = {
+    GenericTypeAnnotation(annotation, parentName, seen) {
+      if (getInTypeScope(annotation.id.name)) {
+        return buildTypeAnnotationDeclarationTypes(getInTypeScope(annotation.id.name), parentName, seen);
+      }
+      return {};
+    },
+
+    ObjectTypeAnnotation(annotation, parentName, seen) {
+      let containsUnresolvedObjectTypeSpread = false;
+      let containsSpread = false;
+      const containsIndexers = !!annotation.indexers && annotation.indexers.length > 0;
+      const shapeTypeDefinition = {
+        type: 'shape',
+        children: {},
+      };
+      iterateProperties(
+        context,
+        annotation.properties,
+        (childKey, childValue, propNode) => {
+          const fullName = [parentName, childKey].join('.');
+          if (childKey || childValue) {
+            const types = buildTypeAnnotationDeclarationTypes(childValue, fullName, seen);
+            types.fullName = fullName;
+            types.name = childKey;
+            types.node = propNode;
+            types.isRequired = !childValue.optional;
+            shapeTypeDefinition.children[childKey] = types;
+          }
+        },
+        (spreadNode) => {
+          const key = astUtil.getKeyValue(context, spreadNode);
+          const types = buildTypeAnnotationDeclarationTypes(spreadNode, key, seen);
+          if (!types.children) {
+            containsUnresolvedObjectTypeSpread = true;
+          } else {
+            Object.assign(shapeTypeDefinition, types.children);
+          }
+          containsSpread = true;
+        }
+      );
+
+      // Mark if this shape has spread or an indexer. We will know to consider all props from this shape as having propTypes,
+      // but still have the ability to detect unused children of this shape.
+      shapeTypeDefinition.containsUnresolvedSpread = containsUnresolvedObjectTypeSpread;
+      shapeTypeDefinition.containsIndexers = containsIndexers;
+      // Deprecated: containsSpread is not used anymore in the codebase, ensure to keep API backward compatibility
+      shapeTypeDefinition.containsSpread = containsSpread;
+
+      return shapeTypeDefinition;
+    },
+
+    UnionTypeAnnotation(annotation, parentName, seen) {
+      /** @type {UnionTypeDefinition} */
+      const unionTypeDefinition = {
+        type: 'union',
+        children: annotation.types.map((type) => buildTypeAnnotationDeclarationTypes(type, parentName, seen)),
+      };
+      if (unionTypeDefinition.children.length === 0) {
+        // no complex type found, simply accept everything
+        return {};
+      }
+      return unionTypeDefinition;
+    },
+
+    ArrayTypeAnnotation(annotation, parentName, seen) {
+      const fullName = [parentName, '*'].join('.');
+      const child = buildTypeAnnotationDeclarationTypes(annotation.elementType, fullName, seen);
+      child.fullName = fullName;
+      child.name = '__ANY_KEY__';
+      child.node = annotation;
+      return {
+        type: 'object',
+        children: {
+          __ANY_KEY__: child,
+        },
+      };
+    },
+  };
+  /* eslint-enable no-use-before-define */
+
+  /**
+   * Resolve the type annotation for a given node.
+   * Flow annotations are sometimes wrapped in outer `TypeAnnotation`
+   * and `NullableTypeAnnotation` nodes which obscure the annotation we're
+   * interested in.
+   * This method also resolves type aliases where possible.
+   *
+   * @param {ASTNode} node The annotation or a node containing the type annotation.
+   * @returns {ASTNode} The resolved type annotation for the node.
+   */
+  function resolveTypeAnnotation(node) {
+    let annotation = (node.left && node.left.typeAnnotation) || node.typeAnnotation || node;
+    while (annotation && (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation')) {
+      annotation = annotation.typeAnnotation;
+    }
+    if (annotation.type === 'GenericTypeAnnotation' && getInTypeScope(annotation.id.name)) {
+      return getInTypeScope(annotation.id.name);
+    }
+    return annotation;
+  }
+
+  /**
+   * Creates the representation of the React props type annotation for the component.
+   * The representation is used to verify nested used properties.
+   * @param {ASTNode} annotation Type annotation for the props class property.
+   * @param {string} parentName
+   * @param {Set<ASTNode>} [seen]
+   * @return {Object} The representation of the declaration, empty object means
+   *    the property is declared without the need for further analysis.
+   */
+  function buildTypeAnnotationDeclarationTypes(annotation, parentName, seen) {
+    if (typeof seen === 'undefined') {
+      // Keeps track of annotations we've already seen to
+      // prevent problems with recursive types.
+      seen = new Set();
+    }
+    if (seen.has(annotation)) {
+      // This must be a recursive type annotation, so just accept anything.
+      return {};
+    }
+    seen.add(annotation);
+
+    if (annotation.type in typeDeclarationBuilders) {
+      return typeDeclarationBuilders[annotation.type](annotation, parentName, seen);
+    }
+    return {};
+  }
+
+  /**
+   * Marks all props found inside ObjectTypeAnnotation as declared.
+   *
+   * Modifies the declaredProperties object
+   * @param {ASTNode} propTypes
+   * @param {Object} declaredPropTypes
+   * @returns {boolean} True if propTypes should be ignored (e.g. when a type can't be resolved, when it is imported)
+   */
+  function declarePropTypesForObjectTypeAnnotation(propTypes, declaredPropTypes) {
+    let ignorePropsValidation = false;
+
+    iterateProperties(context, propTypes.properties, (key, value, propNode) => {
+      if (!value) {
+        ignorePropsValidation = ignorePropsValidation || propNode.type !== 'ObjectTypeSpreadProperty';
+        return;
+      }
+
+      const types = buildTypeAnnotationDeclarationTypes(value, key);
+      types.fullName = key;
+      types.name = key;
+      types.node = propNode;
+      types.isRequired = !propNode.optional;
+      declaredPropTypes[key] = types;
+    }, (spreadNode) => {
+      const key = astUtil.getKeyValue(context, spreadNode);
+      const spreadAnnotation = getInTypeScope(key);
+      if (!spreadAnnotation) {
+        ignorePropsValidation = true;
+      } else {
+        const spreadIgnoreValidation = declarePropTypesForObjectTypeAnnotation(spreadAnnotation, declaredPropTypes);
+        ignorePropsValidation = ignorePropsValidation || spreadIgnoreValidation;
+      }
+    });
+
+    return ignorePropsValidation;
+  }
+
+  /**
+   * Marks all props found inside IntersectionTypeAnnotation as declared.
+   * Since InterSectionTypeAnnotations can be nested, this handles recursively.
+   *
+   * Modifies the declaredPropTypes object
+   * @param {ASTNode} propTypes
+   * @param {Object} declaredPropTypes
+   * @returns {boolean} True if propTypes should be ignored (e.g. when a type can't be resolved, when it is imported)
+   */
+  function declarePropTypesForIntersectionTypeAnnotation(propTypes, declaredPropTypes) {
+    return propTypes.types.some((annotation) => {
+      if (annotation.type === 'ObjectTypeAnnotation') {
+        return declarePropTypesForObjectTypeAnnotation(annotation, declaredPropTypes);
+      }
+
+      if (annotation.type === 'UnionTypeAnnotation') {
+        return true;
+      }
+
+      // Type can't be resolved
+      if (!annotation.id) {
+        return true;
+      }
+
+      const typeNode = getInTypeScope(annotation.id.name);
+
+      if (!typeNode) {
+        return true;
+      }
+      if (typeNode.type === 'IntersectionTypeAnnotation') {
+        return declarePropTypesForIntersectionTypeAnnotation(typeNode, declaredPropTypes);
+      }
+
+      return declarePropTypesForObjectTypeAnnotation(typeNode, declaredPropTypes);
+    });
+  }
+
+  /**
+   * Resolve node of type Identifier when building declaration types.
+   * @param {ASTNode} node
+   * @param {ASTNode} rootNode
+   * @param {Function} callback called with the resolved value only if resolved.
+   */
+  function resolveValueForIdentifierNode(node, rootNode, callback) {
+    if (
+      rootNode
+      && node
+      && node.type === 'Identifier'
+    ) {
+      const scope = getScope(context, rootNode);
+      const identVariable = scope.variableScope.variables.find(
+        (variable) => variable.name === node.name
+      );
+      if (identVariable) {
+        const definition = identVariable.defs[identVariable.defs.length - 1];
+        callback(definition.node.init);
+      }
+    }
+  }
+
+  /**
+   * Creates the representation of the React propTypes for the component.
+   * The representation is used to verify nested used properties.
+   * @param {ASTNode} value Node of the PropTypes for the desired property
+   * @param {string} parentName
+   * @param {ASTNode} rootNode
+   * @return {Object} The representation of the declaration, empty object means
+   *    the property is declared without the need for further analysis.
+   */
+  function buildReactDeclarationTypes(value, parentName, rootNode) {
+    if (
+      value
+      && value.callee
+      && value.callee.object
+      && hasCustomValidator(value.callee.object.name)
+    ) {
+      return {};
+    }
+
+    let identNodeResolved = false;
+    // Resolve identifier node for cases where isRequired is set in
+    // the variable declaration or not at all.
+    // const variableType = PropTypes.shape({ foo: ... }).isRequired
+    // propTypes = {
+    //   example: variableType
+    // }
+    // --------
+    // const variableType = PropTypes.shape({ foo: ... })
+    // propTypes = {
+    //   example: variableType
+    // }
+    resolveValueForIdentifierNode(value, rootNode, (newValue) => {
+      identNodeResolved = true;
+      value = newValue;
+    });
+
+    if (
+      value
+      && value.type === 'MemberExpression'
+      && value.property
+      && value.property.name === 'isRequired'
+    ) {
+      value = value.object;
+    }
+
+    // Resolve identifier node for cases where isRequired is set in
+    // the prop types.
+    // const variableType = PropTypes.shape({ foo: ... })
+    // propTypes = {
+    //   example: variableType.isRequired
+    // }
+    if (!identNodeResolved) {
+      resolveValueForIdentifierNode(value, rootNode, (newValue) => {
+        value = newValue;
+      });
+    }
+
+    // Verify PropTypes that are functions
+    if (
+      astUtil.isCallExpression(value)
+      && value.callee
+      && value.callee.property
+      && value.callee.property.name
+      && value.arguments
+      && value.arguments.length > 0
+    ) {
+      const callName = value.callee.property.name;
+      const argument = value.arguments[0];
+      switch (callName) {
+        case 'shape':
+        case 'exact': {
+          if (argument.type !== 'ObjectExpression') {
+            // Invalid proptype or cannot analyse statically
+            return {};
+          }
+          const shapeTypeDefinition = {
+            type: callName,
+            children: {},
+          };
+          iterateProperties(context, argument.properties, (childKey, childValue, propNode) => {
+            if (childValue) { // skip spread propTypes
+              const fullName = [parentName, childKey].join('.');
+              const types = buildReactDeclarationTypes(childValue, fullName, rootNode);
+              types.fullName = fullName;
+              types.name = childKey;
+              types.node = propNode;
+              shapeTypeDefinition.children[childKey] = types;
+            }
+          });
+          return shapeTypeDefinition;
+        }
+        case 'arrayOf':
+        case 'objectOf': {
+          const fullName = [parentName, '*'].join('.');
+          const child = buildReactDeclarationTypes(argument, fullName, rootNode);
+          child.fullName = fullName;
+          child.name = '__ANY_KEY__';
+          child.node = argument;
+          return {
+            type: 'object',
+            children: {
+              __ANY_KEY__: child,
+            },
+          };
+        }
+        case 'oneOfType': {
+          if (
+            !argument.elements
+            || argument.elements.length === 0
+          ) {
+            // Invalid proptype or cannot analyse statically
+            return {};
+          }
+
+          /** @type {UnionTypeDefinition} */
+          const unionTypeDefinition = {
+            type: 'union',
+            children: argument.elements.map((element) => buildReactDeclarationTypes(element, parentName, rootNode)),
+          };
+          if (unionTypeDefinition.children.length === 0) {
+            // no complex type found, simply accept everything
+            return {};
+          }
+          return unionTypeDefinition;
+        }
+        default:
+          return {};
+      }
+    }
+    // Unknown property or accepts everything (any, object, ...)
+    return {};
+  }
+
+  function isValidReactGenericTypeAnnotation(annotation) {
+    if (annotation.typeName) {
+      if (annotation.typeName.name) { // if FC<Props>
+        const typeName = annotation.typeName.name;
+        if (!genericReactTypesImport.has(typeName)) {
+          return false;
+        }
+      } else if (annotation.typeName.right.name) { // if React.FC<Props>
+        const right = annotation.typeName.right.name;
+        const left = annotation.typeName.left.name;
+
+        if (!genericReactTypesImport.has(left) || !allowedGenericTypes.has(right)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns the left most typeName of a node, e.g: FC<Props>, React.FC<Props>
+   * The representation is used to verify nested used properties.
+   * @param {ASTNode} node
+   * @return {string | undefined}
+   */
+  function getLeftMostTypeName(node) {
+    if (node.name) return node.name;
+    if (node.left) return getLeftMostTypeName(node.left);
+  }
+
+  function getRightMostTypeName(node) {
+    if (node.name) return node.name;
+    if (node.right) return getRightMostTypeName(node.right);
+  }
+
+  /**
+   * Returns true if the node is either a interface or type alias declaration
+   * @param {ASTNode} node
+   * @return {boolean}
+   */
+  function filterInterfaceOrTypeAlias(node) {
+    return (
+      astUtil.isTSInterfaceDeclaration(node) || astUtil.isTSTypeAliasDeclaration(node)
+    );
+  }
+
+  /**
+   * Returns true if the interface or type alias declaration node name matches the type-name str
+   * @param {ASTNode} node
+   * @param {string} typeName
+   * @return {boolean}
+   */
+  function filterInterfaceOrAliasByName(node, typeName) {
+    return (
+      node.id
+      && node.id.name === typeName
+    ) || (
+      node.declaration
+      && node.declaration.id
+      && node.declaration.id.name === typeName
+    );
+  }
+
+  class DeclarePropTypesForTSTypeAnnotation {
+    constructor(propTypes, declaredPropTypes, rootNode) {
+      this.propTypes = propTypes;
+      this.declaredPropTypes = declaredPropTypes;
+      this.foundDeclaredPropertiesList = [];
+      this.referenceNameMap = new Set();
+      this.sourceCode = getSourceCode(context);
+      this.shouldIgnorePropTypes = false;
+      this.rootNode = rootNode;
+      this.visitTSNode(this.propTypes);
+      this.endAndStructDeclaredPropTypes();
+    }
+
+    /**
+     * The node will be distribute to different function.
+     * @param {ASTNode} node
+     */
+    visitTSNode(node) {
+      if (!node) return;
+      if (astUtil.isTSTypeAnnotation(node)) {
+        const typeAnnotation = node.typeAnnotation;
+        this.visitTSNode(typeAnnotation);
+      } else if (astUtil.isTSTypeReference(node)) {
+        this.searchDeclarationByName(node);
+      } else if (astUtil.isTSInterfaceHeritage(node)) {
+        this.searchDeclarationByName(node);
+      } else if (astUtil.isTSTypeLiteral(node)) {
+        // Check node is an object literal
+        if (Array.isArray(node.members)) {
+          this.foundDeclaredPropertiesList = this.foundDeclaredPropertiesList.concat(node.members);
+        }
+      } else if (astUtil.isTSIntersectionType(node)) {
+        this.convertIntersectionTypeToPropTypes(node);
+      } else if (astUtil.isTSParenthesizedType(node)) {
+        const typeAnnotation = node.typeAnnotation;
+        this.visitTSNode(typeAnnotation);
+      } else if (astUtil.isTSTypeParameterInstantiation(node)) {
+        if (Array.isArray(node.params)) {
+          node.params.forEach((x) => this.visitTSNode(x));
+        }
+      } else {
+        this.shouldIgnorePropTypes = true;
+      }
+    }
+
+    /**
+     * Search TSInterfaceDeclaration or TSTypeAliasDeclaration,
+     * by using TSTypeReference and TSInterfaceHeritage name.
+     * @param {ASTNode} node
+     */
+    searchDeclarationByName(node) {
+      let typeName;
+      if (astUtil.isTSTypeReference(node)) {
+        typeName = node.typeName.name;
+        const leftMostName = getLeftMostTypeName(node.typeName);
+        const shouldTraverseTypeParams = genericReactTypesImport.has(leftMostName);
+        const nodeTypeArguments = propsUtil.getTypeArguments(node);
+        if (shouldTraverseTypeParams && nodeTypeArguments && nodeTypeArguments.length !== 0) {
+          // All react Generic types are derived from:
+          // type PropsWithChildren<P> = P & { children?: ReactNode | undefined }
+          // So we should construct an optional children prop
+          this.shouldSpecifyOptionalChildrenProps = true;
+
+          const rightMostName = getRightMostTypeName(node.typeName);
+          if (
+            leftMostName === 'React'
+            && (
+              rightMostName === 'HTMLAttributes'
+              || rightMostName === 'HTMLElement'
+              || rightMostName === 'HTMLProps'
+            )
+          ) {
+            this.shouldSpecifyClassNameProp = true;
+          }
+
+          const importedName = localToImportedMap[rightMostName];
+          const idx = genericTypeParamIndexWherePropsArePresent[
+            leftMostName !== rightMostName ? rightMostName : importedName
+          ];
+          const nextNode = nodeTypeArguments.params[idx];
+          this.visitTSNode(nextNode);
+          return;
+        }
+      } else if (astUtil.isTSInterfaceHeritage(node)) {
+        if (!node.expression && node.id) {
+          typeName = node.id.name;
+        } else {
+          typeName = node.expression.name;
+        }
+      }
+      if (!typeName) {
+        this.shouldIgnorePropTypes = true;
+        return;
+      }
+      if (typeName === 'ReturnType') {
+        this.convertReturnTypeToPropTypes(node, this.rootNode);
+        return;
+      }
+      // Prevent recursive inheritance will cause maximum callstack.
+      if (this.referenceNameMap.has(typeName)) {
+        this.shouldIgnorePropTypes = true;
+        return;
+      }
+      // Add typeName to Set and consider it as traversed.
+      this.referenceNameMap.add(typeName);
+
+      /**
+       * From line 577 to line 581, and line 588 to line 590 are trying to handle typescript-eslint-parser
+       * Need to be deprecated after remove typescript-eslint-parser support.
+       */
+      const candidateTypes = this.sourceCode.ast.body.filter((item) => astUtil.isTSTypeDeclaration(item));
+
+      const declarations = flatMap(
+        candidateTypes,
+        (type) => (
+          type.declarations
+          || (
+            type.declaration
+            && type.declaration.declarations
+          )
+          || type.declaration
+        )
+      );
+
+      // we tried to find either an interface or a type with the TypeReference name
+      const typeDeclaration = declarations.filter((dec) => dec.id.name === typeName);
+
+      const interfaceDeclarations = this.sourceCode.ast.body
+        .filter(filterInterfaceOrTypeAlias)
+        .filter((item) => filterInterfaceOrAliasByName(item, typeName))
+        .map((item) => (item.declaration || item));
+
+      if (typeDeclaration.length !== 0) {
+        typeDeclaration.map((t) => t.init || t.typeAnnotation).forEach(this.visitTSNode, this);
+      } else if (interfaceDeclarations.length !== 0) {
+        interfaceDeclarations.forEach(this.traverseDeclaredInterfaceOrTypeAlias, this);
+      } else {
+        this.shouldIgnorePropTypes = true;
+      }
+    }
+
+    /**
+     * Traverse TSInterfaceDeclaration and TSTypeAliasDeclaration
+     * which retrieve from function searchDeclarationByName;
+     * @param {ASTNode} node
+     */
+    traverseDeclaredInterfaceOrTypeAlias(node) {
+      if (astUtil.isTSInterfaceDeclaration(node)) {
+        // Handle TSInterfaceDeclaration interface Props { name: string, id: number}, should put in properties list directly;
+        this.foundDeclaredPropertiesList = this.foundDeclaredPropertiesList.concat(node.body.body);
+      }
+      // Handle TSTypeAliasDeclaration type Props = {name:string}
+      if (astUtil.isTSTypeAliasDeclaration(node)) {
+        const typeAnnotation = node.typeAnnotation;
+        this.visitTSNode(typeAnnotation);
+      }
+      if (Array.isArray(node.extends)) {
+        node.extends.forEach((x) => this.visitTSNode(x));
+        // This line is trying to handle typescript-eslint-parser
+        // typescript-eslint-parser extension is name as heritage
+      } else if (Array.isArray(node.heritage)) {
+        node.heritage.forEach((x) => this.visitTSNode(x));
+      }
+    }
+
+    convertIntersectionTypeToPropTypes(node) {
+      if (!node) return;
+      if (Array.isArray(node.types)) {
+        node.types.forEach((x) => this.visitTSNode(x));
+      } else {
+        this.shouldIgnorePropTypes = true;
+      }
+    }
+
+    convertReturnTypeToPropTypes(node, rootNode) {
+      // ReturnType<T> should always have one parameter
+      const nodeTypeArguments = propsUtil.getTypeArguments(node);
+      if (nodeTypeArguments) {
+        if (nodeTypeArguments.params.length === 1) {
+          let returnType = nodeTypeArguments.params[0];
+          // This line is trying to handle typescript-eslint-parser
+          // typescript-eslint-parser TSTypeQuery is wrapped by TSTypeReference
+          if (astUtil.isTSTypeReference(returnType)) {
+            returnType = returnType.typeName;
+          }
+          // Handle ReturnType<typeof mapStateToProps>
+          if (astUtil.isTSTypeQuery(returnType)) {
+            const returnTypeFunction = flatMap(this.sourceCode.ast.body
+              .filter((item) => item.type === 'VariableDeclaration'
+                && item.declarations.find((dec) => dec.id.name === returnType.exprName.name)
+              ), (type) => type.declarations).map((dec) => dec.init);
+
+            if (Array.isArray(returnTypeFunction)) {
+              if (returnTypeFunction.length === 0) {
+                // Cannot find identifier in current scope. It might be an exported type.
+                this.shouldIgnorePropTypes = true;
+                return;
+              }
+              returnTypeFunction.forEach((func) => {
+                if (isFunctionType(func)) {
+                  let res = func.body;
+                  if (res.type === 'BlockStatement') {
+                    res = astUtil.findReturnStatement(func);
+                    if (res) {
+                      res = res.argument;
+                    }
+                  }
+                  switch (res.type) {
+                    case 'ObjectExpression':
+                      iterateProperties(context, res.properties, (key, value, propNode) => {
+                        if (propNode && astUtil.isCallExpression(propNode.argument)) {
+                          const propNodeTypeArguments = propsUtil.getTypeArguments(propNode.argument);
+                          if (propNodeTypeArguments) {
+                            this.visitTSNode(propNodeTypeArguments);
+                          } else {
+                            // Ignore this CallExpression return value since it doesn't have any typeParameters to let us know it's types.
+                            this.shouldIgnorePropTypes = true;
+                            return;
+                          }
+                        }
+                        if (!value) {
+                          this.shouldIgnorePropTypes = true;
+                          return;
+                        }
+                        const types = buildReactDeclarationTypes(value, key, rootNode);
+                        types.fullName = key;
+                        types.name = key;
+                        types.node = propNode;
+                        types.isRequired = propsUtil.isRequiredPropType(value);
+                        this.declaredPropTypes[key] = types;
+                      });
+                      break;
+                    case 'CallExpression':
+                      if (propsUtil.getTypeArguments(res)) {
+                        this.visitTSNode(propsUtil.getTypeArguments(res));
+                      } else {
+                        // Ignore this CallExpression return value since it doesn't have any typeParameters to let us know it's types.
+                        this.shouldIgnorePropTypes = true;
+                      }
+                      break;
+                    default:
+                  }
+                }
+              });
+              return;
+            }
+          }
+          // Handle ReturnType<()=>returnType>
+          if (astUtil.isTSFunctionType(returnType)) {
+            if (astUtil.isTSTypeAnnotation(returnType.returnType)) {
+              this.visitTSNode(returnType.returnType);
+              return;
+            }
+            // This line is trying to handle typescript-eslint-parser
+            // typescript-eslint-parser TSFunction name returnType as typeAnnotation
+            if (astUtil.isTSTypeAnnotation(returnType.typeAnnotation)) {
+              this.visitTSNode(returnType.typeAnnotation);
+              return;
+            }
+          }
+        }
+      }
+      this.shouldIgnorePropTypes = true;
+    }
+
+    endAndStructDeclaredPropTypes() {
+      if (this.shouldSpecifyOptionalChildrenProps) {
+        this.declaredPropTypes.children = {
+          fullName: 'children',
+          name: 'children',
+          isRequired: false,
+        };
+      }
+      if (this.shouldSpecifyClassNameProp) {
+        this.declaredPropTypes.className = {
+          fullName: 'className',
+          name: 'className',
+          isRequired: false,
+        };
+      }
+
+      this.foundDeclaredPropertiesList.forEach((tsInterfaceBody) => {
+        if (tsInterfaceBody && (tsInterfaceBody.type === 'TSPropertySignature' || tsInterfaceBody.type === 'TSMethodSignature')) {
+          let accessor = 'name';
+          if (tsInterfaceBody.key.type === 'Literal') {
+            if (typeof tsInterfaceBody.key.value === 'number') {
+              accessor = 'raw';
+            } else {
+              accessor = 'value';
+            }
+          }
+          this.declaredPropTypes[tsInterfaceBody.key[accessor]] = {
+            fullName: tsInterfaceBody.key[accessor],
+            name: tsInterfaceBody.key[accessor],
+            node: tsInterfaceBody,
+            isRequired: !tsInterfaceBody.optional,
+          };
+        }
+      });
+    }
+  }
+
+  /**
+   * Mark a prop type as declared
+   * @param {ASTNode} node The AST node being checked.
+   * @param {ASTNode} propTypes The AST node containing the proptypes
+   * @param {ASTNode} rootNode
+   */
+  function markPropTypesAsDeclared(node, propTypes, rootNode) {
+    let componentNode = node;
+    while (componentNode && !components.get(componentNode)) {
+      componentNode = componentNode.parent;
+    }
+    const component = components.get(componentNode);
+    let declaredPropTypes = (component && component.declaredPropTypes) || {};
+    let ignorePropsValidation = (component && component.ignorePropsValidation) || false;
+    switch (propTypes && propTypes.type) {
+      case 'ObjectTypeAnnotation':
+        ignorePropsValidation = declarePropTypesForObjectTypeAnnotation(propTypes, declaredPropTypes);
+        break;
+      case 'ObjectExpression':
+        iterateProperties(context, propTypes.properties, (key, value, propNode) => {
+          if (!value) {
+            ignorePropsValidation = true;
+            return;
+          }
+          const types = buildReactDeclarationTypes(value, key, rootNode);
+          types.fullName = key;
+          types.name = key;
+          types.node = propNode;
+          types.isRequired = propsUtil.isRequiredPropType(value);
+          declaredPropTypes[key] = types;
+        });
+        break;
+      case 'MemberExpression': {
+        let curDeclaredPropTypes = declaredPropTypes;
+        // Walk the list of properties, until we reach the assignment
+        // ie: ClassX.propTypes.a.b.c = ...
+        while (
+          propTypes
+          && propTypes.parent
+          && propTypes.parent.type !== 'AssignmentExpression'
+          && propTypes.property
+          && curDeclaredPropTypes
+        ) {
+          const propName = propTypes.property.name;
+          if (propName in curDeclaredPropTypes) {
+            curDeclaredPropTypes = curDeclaredPropTypes[propName].children;
+            propTypes = propTypes.parent;
+          } else {
+            // This will crash at runtime because we haven't seen this key before
+            // stop this and do not declare it
+            propTypes = null;
+          }
+        }
+        if (propTypes && propTypes.parent && propTypes.property) {
+          if (!(propTypes === propTypes.parent.left && propTypes.parent.left.object)) {
+            ignorePropsValidation = true;
+            break;
+          }
+          const parentProp = getText(context, propTypes.parent.left.object).replace(/^.*\.propTypes\./, '');
+          const types = buildReactDeclarationTypes(
+            propTypes.parent.right,
+            parentProp,
+            rootNode
+          );
+
+          types.name = propTypes.property.name;
+          types.fullName = [parentProp, propTypes.property.name].join('.');
+          types.node = propTypes.parent;
+          types.isRequired = propsUtil.isRequiredPropType(propTypes.parent.right);
+          curDeclaredPropTypes[propTypes.property.name] = types;
+        } else {
+          let isUsedInPropTypes = false;
+          let n = propTypes;
+          while (n) {
+            if (((n.type === 'AssignmentExpression') && propsUtil.isPropTypesDeclaration(n.left))
+              || ((n.type === 'ClassProperty' || n.type === 'PropertyDefinition' || n.type === 'Property') && propsUtil.isPropTypesDeclaration(n))) {
+              // Found a propType used inside of another propType. This is not considered usage, we'll still validate
+              // this component.
+              isUsedInPropTypes = true;
+              break;
+            }
+            n = n.parent;
+          }
+          if (!isUsedInPropTypes) {
+            ignorePropsValidation = true;
+          }
+        }
+        break;
+      }
+      case 'Identifier': {
+        const firstMatchingVariable = variableUtil.getVariableFromContext(context, node, propTypes.name);
+        if (firstMatchingVariable) {
+          const defInScope = firstMatchingVariable.defs[firstMatchingVariable.defs.length - 1];
+          markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init, rootNode);
+          return;
+        }
+        ignorePropsValidation = true;
+        break;
+      }
+      case 'CallExpression': {
+        if (
+          propWrapperUtil.isPropWrapperFunction(
+            context,
+            getText(context, propTypes.callee)
+          )
+          && propTypes.arguments && propTypes.arguments[0]
+        ) {
+          markPropTypesAsDeclared(node, propTypes.arguments[0], rootNode);
+          return;
+        }
+        break;
+      }
+      case 'IntersectionTypeAnnotation':
+        ignorePropsValidation = declarePropTypesForIntersectionTypeAnnotation(propTypes, declaredPropTypes);
+        break;
+      case 'GenericTypeAnnotation':
+        if (propTypes.id.name === '$ReadOnly') {
+          const propTypeArguments = propsUtil.getTypeArguments(propTypes);
+          ignorePropsValidation = declarePropTypesForObjectTypeAnnotation(
+            propTypeArguments.params[0],
+            declaredPropTypes
+          );
+        } else {
+          ignorePropsValidation = true;
+        }
+        break;
+      case 'TSTypeReference':
+      case 'TSTypeAnnotation': {
+        const tsTypeAnnotation = new DeclarePropTypesForTSTypeAnnotation(propTypes, declaredPropTypes, rootNode);
+        ignorePropsValidation = tsTypeAnnotation.shouldIgnorePropTypes;
+        declaredPropTypes = tsTypeAnnotation.declaredPropTypes;
+      }
+        break;
+      case null:
+        break;
+      default:
+        ignorePropsValidation = true;
+        break;
+    }
+
+    components.set(node, {
+      declaredPropTypes,
+      ignorePropsValidation,
+    });
+  }
+
+  /**
+   * @param {ASTNode} node We expect either an ArrowFunctionExpression,
+   *   FunctionDeclaration, or FunctionExpression
+   * @param {ASTNode} rootNode
+   */
+  function markAnnotatedFunctionArgumentsAsDeclared(node, rootNode) {
+    if (!node.params || !node.params.length) {
+      return;
+    }
+
+    let propTypesArguments = null;
+    if (node.parent) {
+      propTypesArguments = propsUtil.getTypeArguments(node.parent);
+    }
+
+    if (
+      node.parent
+      && node.parent.callee
+      && propTypesArguments
+      && propTypesArguments.params
+      && (
+        node.parent.callee.name === 'forwardRef' || (
+          node.parent.callee.object
+          && node.parent.callee.property
+          && node.parent.callee.object.name === 'React'
+          && node.parent.callee.property.name === 'forwardRef'
+        )
+      )
+    ) {
+      const declaredPropTypes = {};
+      const obj = new DeclarePropTypesForTSTypeAnnotation(propTypesArguments.params[1], declaredPropTypes, rootNode);
+      components.set(node, {
+        declaredPropTypes: obj.declaredPropTypes,
+        ignorePropsValidation: obj.shouldIgnorePropTypes,
+      });
+      return;
+    }
+
+    const siblingIdentifier = node.parent && node.parent.id;
+    const siblingHasTypeAnnotation = siblingIdentifier && siblingIdentifier.typeAnnotation;
+    const isNodeAnnotated = annotations.isAnnotatedFunctionPropsDeclaration(node, context);
+
+    if (!isNodeAnnotated && !siblingHasTypeAnnotation) {
+      return;
+    }
+
+    // https://github.com/jsx-eslint/eslint-plugin-react/issues/2784
+    if (isInsideClassBody(node) && !astUtil.isFunction(node)) {
+      return;
+    }
+
+    // Should ignore function that not return JSXElement
+    if (!utils.isReturningJSXOrNull(node) || startWithCapitalizedLetter(node)) {
+      return;
+    }
+
+    if (isNodeAnnotated) {
+      const param = node.params[0];
+      if (param.typeAnnotation && param.typeAnnotation.typeAnnotation && param.typeAnnotation.typeAnnotation.type === 'UnionTypeAnnotation') {
+        param.typeAnnotation.typeAnnotation.types.forEach((annotation) => {
+          if (annotation.type === 'GenericTypeAnnotation') {
+            markPropTypesAsDeclared(node, resolveTypeAnnotation(annotation), rootNode);
+          } else {
+            markPropTypesAsDeclared(node, annotation, rootNode);
+          }
+        });
+      } else {
+        markPropTypesAsDeclared(node, resolveTypeAnnotation(param), rootNode);
+      }
+    } else {
+      // implements what's discussed here: https://github.com/jsx-eslint/eslint-plugin-react/issues/2777#issuecomment-683944481
+      const annotation = siblingIdentifier.typeAnnotation.typeAnnotation;
+
+      if (
+        annotation
+        && annotation.type !== 'TSTypeReference'
+        && propsUtil.getTypeArguments(annotation) == null
+      ) {
+        return;
+      }
+
+      if (!isValidReactGenericTypeAnnotation(annotation)) return;
+
+      markPropTypesAsDeclared(node, resolveTypeAnnotation(siblingIdentifier), rootNode);
+    }
+  }
+
+  /**
+   * Resolve the type annotation for a given class declaration node.
+   *
+   * @param {ASTNode} node The annotation or a node containing the type annotation.
+   * @returns {ASTNode} The resolved type annotation for the node.
+   */
+  function resolveSuperParameterPropsType(node) {
+    let propsParameterPosition;
+    const parameters = propsUtil.getSuperTypeArguments(node);
+
+    try {
+      // Flow <=0.52 had 3 required TypedParameters of which the second one is the Props.
+      // Flow >=0.53 has 2 optional TypedParameters of which the first one is the Props.
+      propsParameterPosition = testFlowVersion(context, '>= 0.53.0') ? 0 : 1;
+    } catch (e) {
+      // In case there is no flow version defined, we can safely assume that when there are 3 Props we are dealing with version <= 0.52
+      propsParameterPosition = parameters.params.length <= 2 ? 0 : 1;
+    }
+
+    let annotation = parameters.params[propsParameterPosition];
+    while (annotation && (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation')) {
+      annotation = annotation.typeAnnotation;
+    }
+
+    if (annotation && annotation.type === 'GenericTypeAnnotation' && getInTypeScope(annotation.id.name)) {
+      return getInTypeScope(annotation.id.name);
+    }
+    return annotation;
+  }
+
+  /**
+   * Checks if we are declaring a `props` class property with a flow type annotation.
+   * @param {ASTNode} node The AST node being checked.
+   * @returns {boolean} True if the node is a type annotated props declaration, false if not.
+   */
+  function isAnnotatedClassPropsDeclaration(node) {
+    if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) {
+      const tokens = getFirstTokens(context, node, 2);
+      if (
+        node.typeAnnotation && (
+          tokens[0].value === 'props'
+          || (tokens[1] && tokens[1].value === 'props')
+        )
+      ) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  return {
+    ClassExpression(node) {
+      // TypeParameterDeclaration need to be added to typeScope in order to handle ClassExpressions.
+      // This visitor is executed before TypeParameterDeclaration are scoped, therefore we postpone
+      // processing class expressions until when the program exists.
+      classExpressions.push(node);
+    },
+
+    ClassDeclaration(node) {
+      if (isSuperTypeParameterPropsDeclaration(node)) {
+        markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node), node);
+      }
+    },
+
+    'ClassProperty, PropertyDefinition'(node) {
+      if (isAnnotatedClassPropsDeclaration(node)) {
+        markPropTypesAsDeclared(node, resolveTypeAnnotation(node), node);
+      } else if (propsUtil.isPropTypesDeclaration(node)) {
+        markPropTypesAsDeclared(node, node.value, node);
+      }
+    },
+
+    ObjectExpression(node) {
+      // Search for the proptypes declaration
+      node.properties.forEach((property) => {
+        if (!propsUtil.isPropTypesDeclaration(property)) {
+          return;
+        }
+        markPropTypesAsDeclared(node, property.value, node);
+      });
+    },
+
+    FunctionExpression(node) {
+      if (node.parent.type !== 'MethodDefinition') {
+        markAnnotatedFunctionArgumentsAsDeclared(node, node);
+      }
+    },
+
+    ImportDeclaration(node) {
+      // parse `import ... from 'react`
+      if (node.source.value === 'react') {
+        node.specifiers.forEach((specifier) => {
+          if (
+            // handles import * as X from 'react'
+            specifier.type === 'ImportNamespaceSpecifier'
+            // handles import React from 'react'
+            || specifier.type === 'ImportDefaultSpecifier'
+          ) {
+            genericReactTypesImport.add(specifier.local.name);
+          }
+
+          // handles import { FC } from 'react' or import { FC as X } from 'react'
+          if (specifier.type === 'ImportSpecifier' && allowedGenericTypes.has(specifier.imported.name)) {
+            genericReactTypesImport.add(specifier.local.name);
+            localToImportedMap[specifier.local.name] = specifier.imported.name;
+          }
+        });
+      }
+    },
+
+    FunctionDeclaration: markAnnotatedFunctionArgumentsAsDeclared,
+
+    ArrowFunctionExpression: markAnnotatedFunctionArgumentsAsDeclared,
+
+    MemberExpression(node) {
+      if (propsUtil.isPropTypesDeclaration(node)) {
+        const component = utils.getRelatedComponent(node);
+        if (!component) {
+          return;
+        }
+        try {
+          markPropTypesAsDeclared(component.node, node.parent.right || node.parent, node);
+        } catch (e) {
+          if (e.constructor !== RangeError) { throw e; }
+        }
+      }
+    },
+
+    MethodDefinition(node) {
+      if (!node.static || node.kind !== 'get' || !propsUtil.isPropTypesDeclaration(node)) {
+        return;
+      }
+
+      let i = node.value.body.body.length - 1;
+      for (; i >= 0; i--) {
+        if (node.value.body.body[i].type === 'ReturnStatement') {
+          break;
+        }
+      }
+
+      if (i >= 0) {
+        markPropTypesAsDeclared(node, node.value.body.body[i].argument, node);
+      }
+    },
+
+    TypeAlias(node) {
+      setInTypeScope(node.id.name, node.right);
+    },
+
+    TypeParameterDeclaration(node) {
+      const identifier = node.params[0];
+
+      if (identifier.typeAnnotation) {
+        setInTypeScope(identifier.name, identifier.typeAnnotation.typeAnnotation);
+      }
+    },
+
+    Program() {
+      stack = [{}];
+    },
+
+    BlockStatement() {
+      stack.push(Object.create(typeScope()));
+    },
+
+    'BlockStatement:exit'() {
+      stack.pop();
+    },
+
+    'Program:exit'() {
+      classExpressions.forEach((node) => {
+        if (isSuperTypeParameterPropsDeclaration(node)) {
+          markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node), node);
+        }
+      });
+    },
+  };
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,40 @@
+/**
+ * Fixes sort order of prop types.
+ *
+ * @param {Context} context the second element to compare.
+ * @param {Fixer} fixer the first element to compare.
+ * @param {Array} declarations The context of the two nodes.
+ * @param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements.
+ * @param {boolean=} requiredFirst whether or not to sort required elements first.
+ * @param {boolean=} callbacksLast whether or not to sort callbacks after everything else.
+ * @param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements.
+ * @param {boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape.
+ * @param {boolean=} checkTypes whether or not sorting of prop type definitions are checked.
+ * @returns {Object|*|{range, text}} the sort order of the two elements.
+ */
+export function fixPropTypesSort(context: Context, fixer: Fixer, declarations: any[], ignoreCase?: boolean | undefined, requiredFirst?: boolean | undefined, callbacksLast?: boolean | undefined, noSortAlphabetically?: boolean | undefined, sortShapeProp?: boolean | undefined, checkTypes?: boolean | undefined): any | any | {
+    range;
+    text;
+};
+/**
+ * Checks if the proptype is a callback by checking if it starts with 'on'.
+ *
+ * @param {string} propName the name of the proptype to check.
+ * @returns {boolean} true if the proptype is a callback.
+ */
+export function isCallbackPropName(propName: string): boolean;
+/**
+ * Checks if the prop is required or not.
+ *
+ * @param {ASTNode} node the prop to check.
+ * @returns {boolean} true if the prop is required.
+ */
+export function isRequiredProp(node: ASTNode): boolean;
+/**
+ * Checks if the prop is PropTypes.shape.
+ *
+ * @param {ASTNode} node the prop to check.
+ * @returns {boolean} true if the prop is PropTypes.shape.
+ */
+export function isShapeProp(node: ASTNode): boolean;
+//# sourceMappingURL=propTypesSort.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"propTypesSort.d.ts","sourceRoot":"","sources":["propTypesSort.js"],"names":[],"mappings":"AA4HA;;;;;;;;;;;;;GAaG;AACH,0CAXW,OAAO,SACP,KAAK,oCAEL,OAAO,8BACP,OAAO,8BACP,OAAO,qCACP,OAAO,8BACP,OAAO,2BACP,OAAO,eACL,YAAS;IAAC,KAAK,CAAC;IAAC,IAAI,CAAA;CAAC,CAyFlC;AA7LD;;;;;GAKG;AACH,6CAHW,MAAM,GACJ,OAAO,CAInB;AAlBD;;;;;GAKG;AACH,qCAHW,OAAO,GACL,OAAO,CAInB;AAYD;;;;;GAKG;AACH,kCAHW,OAAO,GACL,OAAO,CASnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propTypesSort.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,233 @@
+/**
+ * @fileoverview Common propTypes sorting functionality.
+ */
+
+'use strict';
+
+const toSorted = require('array.prototype.tosorted');
+
+const astUtil = require('./ast');
+const eslintUtil = require('./eslint');
+
+const getSourceCode = eslintUtil.getSourceCode;
+const getText = eslintUtil.getText;
+
+/**
+ * Returns the value name of a node.
+ *
+ * @param {ASTNode} node the node to check.
+ * @returns {string} The name of the node.
+ */
+function getValueName(node) {
+  return node.type === 'Property'
+    && node.value.property
+    && node.value.property.name;
+}
+
+/**
+ * Checks if the prop is required or not.
+ *
+ * @param {ASTNode} node the prop to check.
+ * @returns {boolean} true if the prop is required.
+ */
+function isRequiredProp(node) {
+  return getValueName(node) === 'isRequired';
+}
+
+/**
+ * Checks if the proptype is a callback by checking if it starts with 'on'.
+ *
+ * @param {string} propName the name of the proptype to check.
+ * @returns {boolean} true if the proptype is a callback.
+ */
+function isCallbackPropName(propName) {
+  return /^on[A-Z]/.test(propName);
+}
+
+/**
+ * Checks if the prop is PropTypes.shape.
+ *
+ * @param {ASTNode} node the prop to check.
+ * @returns {boolean} true if the prop is PropTypes.shape.
+ */
+function isShapeProp(node) {
+  return !!(
+    node
+    && node.callee
+    && node.callee.property
+    && node.callee.property.name === 'shape'
+  );
+}
+
+/**
+ * Returns the properties of a PropTypes.shape.
+ *
+ * @param {ASTNode} node the prop to check.
+ * @returns {Array} the properties of the PropTypes.shape node.
+ */
+function getShapeProperties(node) {
+  return node.arguments
+    && node.arguments[0]
+    && node.arguments[0].properties;
+}
+
+/**
+ * Compares two elements.
+ *
+ * @param {ASTNode} a the first element to compare.
+ * @param {ASTNode} b the second element to compare.
+ * @param {Context} context The context of the two nodes.
+ * @param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements.
+ * @param {boolean=} requiredFirst whether or not to sort required elements first.
+ * @param {boolean=} callbacksLast whether or not to sort callbacks after everything else.
+ * @param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements.
+ * @returns {number} the sort order of the two elements.
+ */
+function sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically) {
+  const aKey = String(astUtil.getKeyValue(context, a));
+  const bKey = String(astUtil.getKeyValue(context, b));
+
+  if (requiredFirst) {
+    if (isRequiredProp(a) && !isRequiredProp(b)) {
+      return -1;
+    }
+    if (!isRequiredProp(a) && isRequiredProp(b)) {
+      return 1;
+    }
+  }
+
+  if (callbacksLast) {
+    if (isCallbackPropName(aKey) && !isCallbackPropName(bKey)) {
+      return 1;
+    }
+    if (!isCallbackPropName(aKey) && isCallbackPropName(bKey)) {
+      return -1;
+    }
+  }
+
+  if (!noSortAlphabetically) {
+    if (ignoreCase) {
+      return aKey.localeCompare(bKey);
+    }
+
+    if (aKey < bKey) {
+      return -1;
+    }
+    if (aKey > bKey) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start and end range
+
+/**
+ * Fixes sort order of prop types.
+ *
+ * @param {Context} context the second element to compare.
+ * @param {Fixer} fixer the first element to compare.
+ * @param {Array} declarations The context of the two nodes.
+ * @param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements.
+ * @param {boolean=} requiredFirst whether or not to sort required elements first.
+ * @param {boolean=} callbacksLast whether or not to sort callbacks after everything else.
+ * @param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements.
+ * @param {boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape.
+ * @param {boolean=} checkTypes whether or not sorting of prop type definitions are checked.
+ * @returns {Object|*|{range, text}} the sort order of the two elements.
+ */
+function fixPropTypesSort(
+  context,
+  fixer,
+  declarations,
+  ignoreCase,
+  requiredFirst,
+  callbacksLast,
+  noSortAlphabetically,
+  sortShapeProp,
+  checkTypes
+) {
+  function sortInSource(allNodes, source) {
+    const originalSource = source;
+    const sourceCode = getSourceCode(context);
+    for (let i = 0; i < allNodes.length; i++) {
+      const node = allNodes[i];
+      let commentAfter = [];
+      let commentBefore = [];
+      let newStart = 0;
+      let newEnd = 0;
+      try {
+        commentBefore = sourceCode.getCommentsBefore(node);
+        commentAfter = sourceCode.getCommentsAfter(node);
+      } catch (e) { /**/ }
+
+      if (commentAfter.length === 0 || commentBefore.length === 0) {
+        newStart = node.range[0];
+        newEnd = node.range[1];
+      }
+
+      const firstCommentBefore = commentBefore[0];
+      if (commentBefore.length >= 1) {
+        newStart = firstCommentBefore.range[0];
+      }
+      const lastCommentAfter = commentAfter[commentAfter.length - 1];
+      if (commentAfter.length >= 1) {
+        newEnd = lastCommentAfter.range[1];
+      }
+      commentnodeMap.set(node, { start: newStart, end: newEnd, hasComment: true });
+    }
+    const nodeGroups = allNodes.reduce((acc, curr) => {
+      if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') {
+        acc.push([]);
+      } else {
+        acc[acc.length - 1].push(curr);
+      }
+      return acc;
+    }, [[]]);
+
+    nodeGroups.forEach((nodes) => {
+      const sortedAttributes = toSorted(
+        nodes,
+        (a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically)
+      );
+
+      const sourceCodeText = getText(context);
+      let separator = '';
+      source = nodes.reduceRight((acc, attr, index) => {
+        const sortedAttr = sortedAttributes[index];
+        const commentNode = commentnodeMap.get(sortedAttr);
+        let sortedAttrText = sourceCodeText.slice(commentNode.start, commentNode.end);
+        const sortedAttrTextLastChar = sortedAttrText[sortedAttrText.length - 1];
+        if (!separator && [';', ','].some((allowedSep) => sortedAttrTextLastChar === allowedSep)) {
+          separator = sortedAttrTextLastChar;
+        }
+        if (sortShapeProp && isShapeProp(sortedAttr.value)) {
+          const shape = getShapeProperties(sortedAttr.value);
+          if (shape) {
+            const attrSource = sortInSource(
+              shape,
+              originalSource
+            );
+            sortedAttrText = attrSource.slice(sortedAttr.range[0], sortedAttr.range[1]);
+          }
+        }
+        const sortedAttrTextVal = checkTypes && !sortedAttrText.endsWith(separator) ? `${sortedAttrText}${separator}` : sortedAttrText;
+        return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrTextVal}${acc.slice(commentnodeMap.get(attr).end)}`;
+      }, source);
+    });
+    return source;
+  }
+
+  const source = sortInSource(declarations, getText(context));
+
+  const rangeStart = commentnodeMap.get(declarations[0]).start;
+  const rangeEnd = commentnodeMap.get(declarations[declarations.length - 1]).end;
+  return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd));
+}
+
+module.exports = {
+  fixPropTypesSort,
+  isCallbackPropName,
+  isRequiredProp,
+  isShapeProp,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,6 @@
+export function formatPropWrapperFunctions(propWrapperFunctions: any): string;
+export function getExactPropWrapperFunctions(context: any): Set<any>;
+export function getPropWrapperFunctions(context: any): Set<any>;
+export function isExactPropWrapperFunction(context: any, name: any): any;
+export function isPropWrapperFunction(context: any, name: any): any;
+//# sourceMappingURL=propWrapper.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"propWrapper.d.ts","sourceRoot":"","sources":["propWrapper.js"],"names":[],"mappings":"AA0CA,8EAUC;AArBD,qEAIC;AAhBD,gEAEC;AAgBD,yEAGC;AAjBD,oEAMC"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/propWrapper.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview Utility functions for propWrapperFunctions setting
+ */
+
+'use strict';
+
+const filter = require('es-iterator-helpers/Iterator.prototype.filter');
+const some = require('es-iterator-helpers/Iterator.prototype.some');
+
+function searchPropWrapperFunctions(name, propWrapperFunctions) {
+  const splitName = name.split('.');
+  return some(propWrapperFunctions.values(), (func) => {
+    if (splitName.length === 2 && func.object === splitName[0] && func.property === splitName[1]) {
+      return true;
+    }
+    return name === func || func.property === name;
+  });
+}
+
+function getPropWrapperFunctions(context) {
+  return new Set(context.settings.propWrapperFunctions || []);
+}
+
+function isPropWrapperFunction(context, name) {
+  if (typeof name !== 'string') {
+    return false;
+  }
+  const propWrapperFunctions = getPropWrapperFunctions(context);
+  return searchPropWrapperFunctions(name, propWrapperFunctions);
+}
+
+function getExactPropWrapperFunctions(context) {
+  const propWrapperFunctions = getPropWrapperFunctions(context);
+  const exactPropWrappers = filter(propWrapperFunctions.values(), (func) => func.exact === true);
+  return new Set(exactPropWrappers);
+}
+
+function isExactPropWrapperFunction(context, name) {
+  const exactPropWrappers = getExactPropWrapperFunctions(context);
+  return searchPropWrapperFunctions(name, exactPropWrappers);
+}
+
+function formatPropWrapperFunctions(propWrapperFunctions) {
+  return Array.from(propWrapperFunctions, (func) => {
+    if (func.object && func.property) {
+      return `'${func.object}.${func.property}'`;
+    }
+    if (func.property) {
+      return `'${func.property}'`;
+    }
+    return `'${func}'`;
+  }).join(', ');
+}
+
+module.exports = {
+  formatPropWrapperFunctions,
+  getExactPropWrapperFunctions,
+  getPropWrapperFunctions,
+  isExactPropWrapperFunction,
+  isPropWrapperFunction,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/props.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/props.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,55 @@
+/**
+ * Checks if the Identifier node passed in looks like a propTypes declaration.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @returns {boolean} `true` if the node is a propTypes declaration, `false` if not
+ */
+export function isPropTypesDeclaration(node: ASTNode): boolean;
+/**
+ * Checks if the node passed in looks like a contextTypes declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a contextTypes declaration, `false` if not
+ */
+export function isContextTypesDeclaration(node: ASTNode): boolean;
+/**
+ * Checks if the node passed in looks like a contextType declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a contextType declaration, `false` if not
+ */
+export function isContextTypeDeclaration(node: ASTNode): boolean;
+/**
+ * Checks if the node passed in looks like a childContextTypes declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a childContextTypes declaration, `false` if not
+ */
+export function isChildContextTypesDeclaration(node: ASTNode): boolean;
+/**
+ * Checks if the Identifier node passed in looks like a defaultProps declaration.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @returns {boolean} `true` if the node is a defaultProps declaration, `false` if not
+ */
+export function isDefaultPropsDeclaration(node: ASTNode): boolean;
+/**
+ * Checks if we are declaring a display name
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {boolean} True if we are declaring a display name, false if not.
+ */
+export function isDisplayNameDeclaration(node: ASTNode): boolean;
+/**
+ * Checks if the PropTypes MemberExpression node passed in declares a required propType.
+ * @param {ASTNode} propTypeExpression node to check. Must be a `PropTypes` MemberExpression.
+ * @returns {boolean} `true` if this PropType is required, `false` if not.
+ */
+export function isRequiredPropType(propTypeExpression: ASTNode): boolean;
+/**
+ * Returns the type arguments of a node or type parameters if type arguments are not available.
+ * @param {ASTNode} node The node to get the type arguments from.
+ * @returns {ASTNode} The type arguments or type parameters of the node.
+ */
+export function getTypeArguments(node: ASTNode): ASTNode;
+/**
+ * Returns the super type arguments of a node or super type parameters if type arguments are not available.
+ * @param {ASTNode} node The node to get the super type arguments from.
+ * @returns {ASTNode} The super type arguments or parameters of the node.
+ */
+export function getSuperTypeArguments(node: ASTNode): ASTNode;
+//# sourceMappingURL=props.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/props.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/props.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["props.js"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,6CAHW,OAAO,GACL,OAAO,CAUnB;AAED;;;;GAIG;AACH,gDAHW,OAAO,GACL,OAAO,CAUnB;AAED;;;;GAIG;AACH,+CAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;GAIG;AACH,qDAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;GAIG;AACH,gDAHW,OAAO,GACL,OAAO,CAKnB;AAED;;;;GAIG;AACH,+CAHW,OAAO,GACL,OAAO,CAcnB;AAED;;;;GAIG;AACH,uDAHW,OAAO,GACL,OAAO,CAKnB;AAED;;;;GAIG;AACH,uCAHW,OAAO,GACL,OAAO,CAOnB;AAED;;;;GAIG;AACH,4CAHW,OAAO,GACL,OAAO,CAOnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/props.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/props.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,130 @@
+/**
+ * @fileoverview Utility functions for props
+ */
+
+'use strict';
+
+const astUtil = require('./ast');
+
+/**
+ * Checks if the Identifier node passed in looks like a propTypes declaration.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @returns {boolean} `true` if the node is a propTypes declaration, `false` if not
+ */
+function isPropTypesDeclaration(node) {
+  if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) {
+    // Flow support
+    if (node.typeAnnotation && node.key.name === 'props') {
+      return true;
+    }
+  }
+  return astUtil.getPropertyName(node) === 'propTypes';
+}
+
+/**
+ * Checks if the node passed in looks like a contextTypes declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a contextTypes declaration, `false` if not
+ */
+function isContextTypesDeclaration(node) {
+  if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) {
+    // Flow support
+    if (node.typeAnnotation && node.key.name === 'context') {
+      return true;
+    }
+  }
+  return astUtil.getPropertyName(node) === 'contextTypes';
+}
+
+/**
+ * Checks if the node passed in looks like a contextType declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a contextType declaration, `false` if not
+ */
+function isContextTypeDeclaration(node) {
+  return astUtil.getPropertyName(node) === 'contextType';
+}
+
+/**
+ * Checks if the node passed in looks like a childContextTypes declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} `true` if the node is a childContextTypes declaration, `false` if not
+ */
+function isChildContextTypesDeclaration(node) {
+  return astUtil.getPropertyName(node) === 'childContextTypes';
+}
+
+/**
+ * Checks if the Identifier node passed in looks like a defaultProps declaration.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @returns {boolean} `true` if the node is a defaultProps declaration, `false` if not
+ */
+function isDefaultPropsDeclaration(node) {
+  const propName = astUtil.getPropertyName(node);
+  return (propName === 'defaultProps' || propName === 'getDefaultProps');
+}
+
+/**
+ * Checks if we are declaring a display name
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {boolean} True if we are declaring a display name, false if not.
+ */
+function isDisplayNameDeclaration(node) {
+  switch (node.type) {
+    case 'ClassProperty':
+    case 'PropertyDefinition':
+      return node.key && node.key.name === 'displayName';
+    case 'Identifier':
+      return node.name === 'displayName';
+    case 'Literal':
+      return node.value === 'displayName';
+    default:
+      return false;
+  }
+}
+
+/**
+ * Checks if the PropTypes MemberExpression node passed in declares a required propType.
+ * @param {ASTNode} propTypeExpression node to check. Must be a `PropTypes` MemberExpression.
+ * @returns {boolean} `true` if this PropType is required, `false` if not.
+ */
+function isRequiredPropType(propTypeExpression) {
+  return propTypeExpression.type === 'MemberExpression'
+    && propTypeExpression.property.name === 'isRequired';
+}
+
+/**
+ * Returns the type arguments of a node or type parameters if type arguments are not available.
+ * @param {ASTNode} node The node to get the type arguments from.
+ * @returns {ASTNode} The type arguments or type parameters of the node.
+ */
+function getTypeArguments(node) {
+  if ('typeArguments' in node) {
+    return node.typeArguments;
+  }
+  return node.typeParameters;
+}
+
+/**
+ * Returns the super type arguments of a node or super type parameters if type arguments are not available.
+ * @param {ASTNode} node The node to get the super type arguments from.
+ * @returns {ASTNode} The super type arguments or parameters of the node.
+ */
+function getSuperTypeArguments(node) {
+  if ('superTypeArguments' in node) {
+    return node.superTypeArguments;
+  }
+  return node.superTypeParameters;
+}
+
+module.exports = {
+  isPropTypesDeclaration,
+  isContextTypesDeclaration,
+  isContextTypeDeclaration,
+  isChildContextTypesDeclaration,
+  isDefaultPropsDeclaration,
+  isDisplayNameDeclaration,
+  isRequiredPropType,
+  getTypeArguments,
+  getSuperTypeArguments,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/report.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/report.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/report.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+declare function _exports(context: any, message: any, messageId: any, data: any): void;
+export = _exports;
+//# sourceMappingURL=report.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/report.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/report.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/report.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["report.js"],"names":[],"mappings":"AAIiB,uFAOhB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/report.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/report.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/report.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,12 @@
+'use strict';
+
+const getMessageData = require('./message');
+
+module.exports = function report(context, message, messageId, data) {
+  context.report(
+    Object.assign(
+      getMessageData(messageId, message),
+      data
+    )
+  );
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,15 @@
+declare function _exports(context: any, components: any, utils: any): {
+    VariableDeclarator(node: any): void;
+    FunctionDeclaration: (node: ASTNode) => void;
+    ArrowFunctionExpression: (node: ASTNode) => void;
+    FunctionExpression: (node: ASTNode) => void;
+    'FunctionDeclaration:exit': () => void;
+    'ArrowFunctionExpression:exit': () => void;
+    'FunctionExpression:exit': () => void;
+    JSXSpreadAttribute(node: any): void;
+    'MemberExpression, OptionalMemberExpression'(node: any): void;
+    ObjectPattern(node: any): void;
+    'Program:exit'(): void;
+};
+export = _exports;
+//# sourceMappingURL=usedPropTypes.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"usedPropTypes.d.ts","sourceRoot":"","sources":["usedPropTypes.js"],"names":[],"mappings":"AAiTiB;;gCA2JJ,OAAO;oCAAP,OAAO;+BAAP,OAAO;;;;;;;;EA2HnB"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,584 @@
+/**
+ * @fileoverview Common used propTypes detection functionality.
+ */
+
+'use strict';
+
+const values = require('object.values');
+
+const astUtil = require('./ast');
+const componentUtil = require('./componentUtil');
+const testReactVersion = require('./version').testReactVersion;
+const ast = require('./ast');
+const eslintUtil = require('./eslint');
+
+const getScope = eslintUtil.getScope;
+const getSourceCode = eslintUtil.getSourceCode;
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+const LIFE_CYCLE_METHODS = ['componentWillReceiveProps', 'shouldComponentUpdate', 'componentWillUpdate', 'componentDidUpdate'];
+const ASYNC_SAFE_LIFE_CYCLE_METHODS = ['getDerivedStateFromProps', 'getSnapshotBeforeUpdate', 'UNSAFE_componentWillReceiveProps', 'UNSAFE_componentWillUpdate'];
+
+function createPropVariables() {
+  /** @type {Map<string, string[]>} Maps the variable to its definition. `props.a.b` is stored as `['a', 'b']` */
+  let propVariables = new Map();
+  let hasBeenWritten = false;
+  const stack = [{ propVariables, hasBeenWritten }];
+  return {
+    pushScope() {
+      // popVariables is not copied until first write.
+      stack.push({ propVariables, hasBeenWritten: false });
+    },
+    popScope() {
+      stack.pop();
+      propVariables = stack[stack.length - 1].propVariables;
+      hasBeenWritten = stack[stack.length - 1].hasBeenWritten;
+    },
+    /**
+     * Add a variable name to the current scope
+     * @param {string} name
+     * @param {string[]} allNames Example: `props.a.b` should be formatted as `['a', 'b']`
+     * @returns {Map<string, string[]>}
+     */
+    set(name, allNames) {
+      if (!hasBeenWritten) {
+        // copy on write
+        propVariables = new Map(propVariables);
+        Object.assign(stack[stack.length - 1], { propVariables, hasBeenWritten: true });
+        stack[stack.length - 1].hasBeenWritten = true;
+      }
+      return propVariables.set(name, allNames);
+    },
+    /**
+     * Get the definition of a variable.
+     * @param {string} name
+     * @returns {string[]} Example: `props.a.b` is represented by `['a', 'b']`
+     */
+    get(name) {
+      return propVariables.get(name);
+    },
+  };
+}
+
+/**
+ * Checks if the string is one of `props`, `nextProps`, or `prevProps`
+ * @param {string} name The AST node being checked.
+ * @returns {boolean} True if the prop name matches
+ */
+function isCommonVariableNameForProps(name) {
+  return name === 'props' || name === 'nextProps' || name === 'prevProps';
+}
+
+/**
+ * Checks if the component must be validated
+ * @param {Object} component The component to process
+ * @returns {boolean} True if the component must be validated, false if not.
+ */
+function mustBeValidated(component) {
+  return !!(component && !component.ignorePropsValidation);
+}
+
+/**
+ * Check if we are in a lifecycle method
+ * @param {object} context
+ * @param {ASTNode} node The AST node being checked.
+ * @param {boolean} checkAsyncSafeLifeCycles
+ * @return {boolean} true if we are in a class constructor, false if not
+ */
+function inLifeCycleMethod(context, node, checkAsyncSafeLifeCycles) {
+  let scope = getScope(context, node);
+  while (scope) {
+    if (scope.block && scope.block.parent && scope.block.parent.key) {
+      const name = scope.block.parent.key.name;
+
+      if (LIFE_CYCLE_METHODS.indexOf(name) >= 0) {
+        return true;
+      }
+      if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(name) >= 0) {
+        return true;
+      }
+    }
+    scope = scope.upper;
+  }
+  return false;
+}
+
+/**
+ * Returns true if the given node is a React Component lifecycle method
+ * @param {ASTNode} node The AST node being checked.
+ * @param {boolean} checkAsyncSafeLifeCycles
+ * @return {boolean} True if the node is a lifecycle method
+ */
+function isNodeALifeCycleMethod(node, checkAsyncSafeLifeCycles) {
+  if (node.key) {
+    if (node.kind === 'constructor') {
+      return true;
+    }
+
+    const nodeKeyName = node.key.name;
+
+    if (typeof nodeKeyName !== 'string') {
+      return false;
+    }
+
+    if (LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) {
+      return true;
+    }
+    if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/**
+ * Returns true if the given node is inside a React Component lifecycle
+ * method.
+ * @param {ASTNode} node The AST node being checked.
+ * @param {boolean} checkAsyncSafeLifeCycles
+ * @return {boolean} True if the node is inside a lifecycle method
+ */
+function isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) {
+  if (
+    (node.type === 'MethodDefinition' || node.type === 'Property')
+    && isNodeALifeCycleMethod(node, checkAsyncSafeLifeCycles)
+  ) {
+    return true;
+  }
+
+  if (node.parent) {
+    return isInLifeCycleMethod(node.parent, checkAsyncSafeLifeCycles);
+  }
+
+  return false;
+}
+
+/**
+ * Check if a function node is a setState updater
+ * @param {ASTNode} node a function node
+ * @return {boolean}
+ */
+function isSetStateUpdater(node) {
+  const unwrappedParentCalleeNode = astUtil.isCallExpression(node.parent)
+    && ast.unwrapTSAsExpression(node.parent.callee);
+
+  return unwrappedParentCalleeNode
+    && unwrappedParentCalleeNode.property
+    && unwrappedParentCalleeNode.property.name === 'setState'
+    // Make sure we are in the updater not the callback
+    && node.parent.arguments[0] === node;
+}
+
+function isPropArgumentInSetStateUpdater(context, node, name) {
+  if (typeof name !== 'string') {
+    return;
+  }
+  let scope = getScope(context, node);
+  while (scope) {
+    const unwrappedParentCalleeNode = scope.block
+      && astUtil.isCallExpression(scope.block.parent)
+      && ast.unwrapTSAsExpression(scope.block.parent.callee);
+    if (
+      unwrappedParentCalleeNode
+      && unwrappedParentCalleeNode.property
+      && unwrappedParentCalleeNode.property.name === 'setState'
+      // Make sure we are in the updater not the callback
+      && scope.block.parent.arguments[0].range[0] === scope.block.range[0]
+      && scope.block.parent.arguments[0].params
+      && scope.block.parent.arguments[0].params.length > 1
+    ) {
+      return scope.block.parent.arguments[0].params[1].name === name;
+    }
+    scope = scope.upper;
+  }
+  return false;
+}
+
+/**
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @returns {boolean}
+ */
+function isInClassComponent(context, node) {
+  return !!(componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node));
+}
+
+/**
+ * Checks if the node is `this.props`
+ * @param {ASTNode|undefined} node
+ * @returns {boolean}
+ */
+function isThisDotProps(node) {
+  return !!node
+    && node.type === 'MemberExpression'
+    && ast.unwrapTSAsExpression(node.object).type === 'ThisExpression'
+    && node.property.name === 'props';
+}
+
+/**
+ * Checks if the prop has spread operator.
+ * @param {object} context
+ * @param {ASTNode} node The AST node being marked.
+ * @returns {boolean} True if the prop has spread operator, false if not.
+ */
+function hasSpreadOperator(context, node) {
+  const tokens = getSourceCode(context).getTokens(node);
+  return tokens.length && tokens[0].value === '...';
+}
+
+/**
+ * Checks if the node is a propTypes usage of the form `this.props.*`, `props.*`, `prevProps.*`, or `nextProps.*`.
+ * @param {Context} context
+ * @param {ASTNode} node
+ * @param {Object} utils
+ * @param {boolean} checkAsyncSafeLifeCycles
+ * @returns {boolean}
+ */
+function isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles) {
+  const unwrappedObjectNode = ast.unwrapTSAsExpression(node.object);
+
+  if (isInClassComponent(context, node)) {
+    // this.props.*
+    if (isThisDotProps(unwrappedObjectNode)) {
+      return true;
+    }
+    // props.* or prevProps.* or nextProps.*
+    if (
+      isCommonVariableNameForProps(unwrappedObjectNode.name)
+      && (inLifeCycleMethod(context, node, checkAsyncSafeLifeCycles) || astUtil.inConstructor(context, node))
+    ) {
+      return true;
+    }
+    // this.setState((_, props) => props.*))
+    if (isPropArgumentInSetStateUpdater(context, node, unwrappedObjectNode.name)) {
+      return true;
+    }
+    return false;
+  }
+  // props.* in function component
+  return unwrappedObjectNode.name === 'props' && !ast.isAssignmentLHS(node);
+}
+
+/**
+ * Retrieve the name of a property node
+ * @param {Context} context
+ * @param {ASTNode} node The AST node with the property.
+ * @param {Object} utils
+ * @param {boolean} checkAsyncSafeLifeCycles
+ * @return {string|undefined} the name of the property or undefined if not found
+ */
+function getPropertyName(context, node, utils, checkAsyncSafeLifeCycles) {
+  const property = node.property;
+  if (property) {
+    switch (property.type) {
+      case 'Identifier':
+        if (node.computed) {
+          return '__COMPUTED_PROP__';
+        }
+        return property.name;
+      case 'MemberExpression':
+        return;
+      case 'Literal':
+        // Accept computed properties that are literal strings
+        if (typeof property.value === 'string') {
+          return property.value;
+        }
+        // Accept number as well but only accept props[123]
+        if (typeof property.value === 'number') {
+          if (isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles)) {
+            return property.raw;
+          }
+        }
+        // falls through
+      default:
+        if (node.computed) {
+          return '__COMPUTED_PROP__';
+        }
+        break;
+    }
+  }
+}
+
+module.exports = function usedPropTypesInstructions(context, components, utils) {
+  const checkAsyncSafeLifeCycles = testReactVersion(context, '>= 16.3.0');
+
+  const propVariables = createPropVariables();
+  const pushScope = propVariables.pushScope;
+  const popScope = propVariables.popScope;
+
+  /**
+   * Mark a prop type as used
+   * @param {ASTNode} node The AST node being marked.
+   * @param {string[]} [parentNames]
+   */
+  function markPropTypesAsUsed(node, parentNames) {
+    parentNames = parentNames || [];
+    let type;
+    let name;
+    let allNames;
+    let properties;
+    switch (node.type) {
+      case 'OptionalMemberExpression':
+      case 'MemberExpression':
+        name = getPropertyName(context, node, utils, checkAsyncSafeLifeCycles);
+        if (name) {
+          allNames = parentNames.concat(name);
+          if (
+            // Match props.foo.bar, don't match bar[props.foo]
+            node.parent.type === 'MemberExpression'
+            && node.parent.object === node
+          ) {
+            markPropTypesAsUsed(node.parent, allNames);
+          }
+          // Handle the destructuring part of `const {foo} = props.a.b`
+          if (
+            node.parent.type === 'VariableDeclarator'
+            && node.parent.id.type === 'ObjectPattern'
+          ) {
+            node.parent.id.parent = node.parent; // patch for bug in eslint@4 in which ObjectPattern has no parent
+            markPropTypesAsUsed(node.parent.id, allNames);
+          }
+
+          // const a = props.a
+          if (
+            node.parent.type === 'VariableDeclarator'
+            && node.parent.id.type === 'Identifier'
+          ) {
+            propVariables.set(node.parent.id.name, allNames);
+          }
+          // Do not mark computed props as used.
+          type = name !== '__COMPUTED_PROP__' ? 'direct' : null;
+        }
+        break;
+      case 'ArrowFunctionExpression':
+      case 'FunctionDeclaration':
+      case 'FunctionExpression': {
+        if (node.params.length === 0) {
+          break;
+        }
+        type = 'destructuring';
+        const propParam = isSetStateUpdater(node) ? node.params[1] : node.params[0];
+        properties = propParam.type === 'AssignmentPattern'
+          ? propParam.left.properties
+          : propParam.properties;
+        break;
+      }
+      case 'ObjectPattern':
+        type = 'destructuring';
+        properties = node.properties;
+        break;
+      case 'TSEmptyBodyFunctionExpression':
+        break;
+      default:
+        throw new Error(`${node.type} ASTNodes are not handled by markPropTypesAsUsed`);
+    }
+
+    const component = components.get(utils.getParentComponent(node));
+    const usedPropTypes = (component && component.usedPropTypes) || [];
+    let ignoreUnusedPropTypesValidation = (component && component.ignoreUnusedPropTypesValidation) || false;
+
+    switch (type) {
+      case 'direct': {
+        // Ignore Object methods
+        if (name in Object.prototype) {
+          break;
+        }
+
+        const reportedNode = node.property;
+        usedPropTypes.push({
+          name,
+          allNames,
+          node: reportedNode,
+        });
+        break;
+      }
+      case 'destructuring': {
+        for (let k = 0, l = (properties || []).length; k < l; k++) {
+          if (hasSpreadOperator(context, properties[k]) || properties[k].computed) {
+            ignoreUnusedPropTypesValidation = true;
+            break;
+          }
+          const propName = ast.getKeyValue(context, properties[k]);
+
+          if (!propName || properties[k].type !== 'Property') {
+            break;
+          }
+
+          usedPropTypes.push({
+            allNames: parentNames.concat([propName]),
+            name: propName,
+            node: properties[k],
+          });
+
+          if (properties[k].value.type === 'ObjectPattern') {
+            markPropTypesAsUsed(properties[k].value, parentNames.concat([propName]));
+          } else if (properties[k].value.type === 'Identifier') {
+            propVariables.set(properties[k].value.name, parentNames.concat(propName));
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+
+    components.set(component ? component.node : node, {
+      usedPropTypes,
+      ignoreUnusedPropTypesValidation,
+    });
+  }
+
+  /**
+   * @param {ASTNode} node We expect either an ArrowFunctionExpression,
+   *   FunctionDeclaration, or FunctionExpression
+   */
+  function markDestructuredFunctionArgumentsAsUsed(node) {
+    const param = node.params && isSetStateUpdater(node) ? node.params[1] : node.params[0];
+
+    const destructuring = param && (
+      param.type === 'ObjectPattern'
+      || ((param.type === 'AssignmentPattern') && (param.left.type === 'ObjectPattern'))
+    );
+
+    if (destructuring && (components.get(node) || components.get(node.parent))) {
+      markPropTypesAsUsed(node);
+    }
+  }
+
+  function handleSetStateUpdater(node) {
+    if (!node.params || node.params.length < 2 || !isSetStateUpdater(node)) {
+      return;
+    }
+    markPropTypesAsUsed(node);
+  }
+
+  /**
+   * Handle both stateless functions and setState updater functions.
+   * @param {ASTNode} node We expect either an ArrowFunctionExpression,
+   *   FunctionDeclaration, or FunctionExpression
+   */
+  function handleFunctionLikeExpressions(node) {
+    pushScope();
+    handleSetStateUpdater(node);
+    markDestructuredFunctionArgumentsAsUsed(node);
+  }
+
+  function handleCustomValidators(component) {
+    const propTypes = component.declaredPropTypes;
+    if (!propTypes) {
+      return;
+    }
+
+    Object.keys(propTypes).forEach((key) => {
+      const node = propTypes[key].node;
+
+      if (node && node.value && astUtil.isFunctionLikeExpression(node.value)) {
+        markPropTypesAsUsed(node.value);
+      }
+    });
+  }
+
+  return {
+    VariableDeclarator(node) {
+      const unwrappedInitNode = ast.unwrapTSAsExpression(node.init);
+
+      // let props = this.props
+      if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node) && node.id.type === 'Identifier') {
+        propVariables.set(node.id.name, []);
+      }
+
+      // Only handles destructuring
+      if (node.id.type !== 'ObjectPattern' || !unwrappedInitNode) {
+        return;
+      }
+
+      // let {props: {firstname}} = this
+      const propsProperty = node.id.properties.find((property) => (
+        property.key
+        && (property.key.name === 'props' || property.key.value === 'props')
+      ));
+
+      if (unwrappedInitNode.type === 'ThisExpression' && propsProperty && propsProperty.value.type === 'ObjectPattern') {
+        markPropTypesAsUsed(propsProperty.value);
+        return;
+      }
+
+      // let {props} = this
+      if (unwrappedInitNode.type === 'ThisExpression' && propsProperty && propsProperty.value.name === 'props') {
+        propVariables.set('props', []);
+        return;
+      }
+
+      // let {firstname} = props
+      if (
+        isCommonVariableNameForProps(unwrappedInitNode.name)
+        && (utils.getParentStatelessComponent(node) || isInLifeCycleMethod(node, checkAsyncSafeLifeCycles))
+      ) {
+        markPropTypesAsUsed(node.id);
+        return;
+      }
+
+      // let {firstname} = this.props
+      if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node)) {
+        markPropTypesAsUsed(node.id);
+        return;
+      }
+
+      // let {firstname} = thing, where thing is defined by const thing = this.props.**.*
+      if (propVariables.get(unwrappedInitNode.name)) {
+        markPropTypesAsUsed(node.id, propVariables.get(unwrappedInitNode.name));
+      }
+    },
+
+    FunctionDeclaration: handleFunctionLikeExpressions,
+
+    ArrowFunctionExpression: handleFunctionLikeExpressions,
+
+    FunctionExpression: handleFunctionLikeExpressions,
+
+    'FunctionDeclaration:exit': popScope,
+
+    'ArrowFunctionExpression:exit': popScope,
+
+    'FunctionExpression:exit': popScope,
+
+    JSXSpreadAttribute(node) {
+      const component = components.get(utils.getParentComponent(node));
+      components.set(component ? component.node : node, {
+        ignoreUnusedPropTypesValidation: node.argument.type !== 'ObjectExpression',
+      });
+    },
+
+    'MemberExpression, OptionalMemberExpression'(node) {
+      if (isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles)) {
+        markPropTypesAsUsed(node);
+        return;
+      }
+
+      const propVariable = propVariables.get(ast.unwrapTSAsExpression(node.object).name);
+      if (propVariable) {
+        markPropTypesAsUsed(node, propVariable);
+      }
+    },
+
+    ObjectPattern(node) {
+      // If the object pattern is a destructured props object in a lifecycle
+      // method -- mark it for used props.
+      if (isNodeALifeCycleMethod(node.parent.parent, checkAsyncSafeLifeCycles) && node.properties.length > 0) {
+        markPropTypesAsUsed(node.parent);
+      }
+    },
+
+    'Program:exit'() {
+      values(components.list())
+        .filter((component) => mustBeValidated(component))
+        .forEach((component) => {
+          handleCustomValidators(component);
+        });
+    },
+  };
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/variable.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/variable.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/variable.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,38 @@
+/**
+ * Search a particular variable in a list
+ * @param {Array} variables The variables list.
+ * @param {string} name The name of the variable to search.
+ * @returns {boolean} True if the variable was found, false if not.
+ */
+export function findVariable(variables: any[], name: string): boolean;
+/**
+ * Find a variable by name in the current scope.
+ * @param {Object} context The current rule context.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @param  {string} name Name of the variable to look for.
+ * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+ */
+export function findVariableByName(context: any, node: ASTNode, name: string): ASTNode | null;
+/**
+ * Find and return a particular variable in a list
+ * @param {Array} variables The variables list.
+ * @param {string} name The name of the variable to search.
+ * @returns {Object} Variable if the variable was found, null if not.
+ */
+export function getVariable(variables: any[], name: string): any;
+/**
+ * Searches for a variable in the given scope.
+ *
+ * @param {Object} context The current rule context.
+ * @param {ASTNode} node The node to start looking from.
+ * @param {string} name The name of the variable to search.
+ * @returns {Object | undefined} Variable if the variable was found, undefined if not.
+ */
+export function getVariableFromContext(context: any, node: ASTNode, name: string): any | undefined;
+/**
+ * Returns the latest definition of the variable.
+ * @param {Object} variable
+ * @returns {Object | undefined} The latest variable definition or undefined.
+ */
+export function getLatestVariableDefinition(variable: any): any | undefined;
+//# sourceMappingURL=variable.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/variable.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/variable.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/variable.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"variable.d.ts","sourceRoot":"","sources":["variable.js"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,qDAHW,MAAM,GACJ,OAAO,CAInB;AA0CD;;;;;;GAMG;AACH,uDAJW,OAAO,QACN,MAAM,GACL,OAAO,GAAC,IAAI,CAkBxB;AA/DD;;;;;GAKG;AACH,oDAHW,MAAM,OAKhB;AAED;;;;;;;GAOG;AACH,2DAJW,OAAO,QACP,MAAM,GACJ,MAAS,SAAS,CAsB9B;AA2BD;;;;GAIG;AACH,4DAFa,MAAS,SAAS,CAI9B"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/variable.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/variable.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/variable.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,100 @@
+/**
+ * @fileoverview Utility functions for React components detection
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const getScope = require('./eslint').getScope;
+
+/**
+ * Search a particular variable in a list
+ * @param {Array} variables The variables list.
+ * @param {string} name The name of the variable to search.
+ * @returns {boolean} True if the variable was found, false if not.
+ */
+function findVariable(variables, name) {
+  return variables.some((variable) => variable.name === name);
+}
+
+/**
+ * Find and return a particular variable in a list
+ * @param {Array} variables The variables list.
+ * @param {string} name The name of the variable to search.
+ * @returns {Object} Variable if the variable was found, null if not.
+ */
+function getVariable(variables, name) {
+  return variables.find((variable) => variable.name === name);
+}
+
+/**
+ * Searches for a variable in the given scope.
+ *
+ * @param {Object} context The current rule context.
+ * @param {ASTNode} node The node to start looking from.
+ * @param {string} name The name of the variable to search.
+ * @returns {Object | undefined} Variable if the variable was found, undefined if not.
+ */
+function getVariableFromContext(context, node, name) {
+  let scope = getScope(context, node);
+
+  while (scope) {
+    let variable = getVariable(scope.variables, name);
+
+    if (!variable && scope.childScopes.length) {
+      variable = getVariable(scope.childScopes[0].variables, name);
+
+      if (!variable && scope.childScopes[0].childScopes.length) {
+        variable = getVariable(scope.childScopes[0].childScopes[0].variables, name);
+      }
+    }
+
+    if (variable) {
+      return variable;
+    }
+    scope = scope.upper;
+  }
+  return undefined;
+}
+
+/**
+ * Find a variable by name in the current scope.
+ * @param {Object} context The current rule context.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @param  {string} name Name of the variable to look for.
+ * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+ */
+function findVariableByName(context, node, name) {
+  const variable = getVariableFromContext(context, node, name);
+
+  if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+    return null;
+  }
+
+  if (variable.defs[0].node.type === 'TypeAlias') {
+    return variable.defs[0].node.right;
+  }
+
+  if (variable.defs[0].type === 'ImportBinding') {
+    return variable.defs[0].node;
+  }
+
+  return variable.defs[0].node.init;
+}
+
+/**
+ * Returns the latest definition of the variable.
+ * @param {Object} variable
+ * @returns {Object | undefined} The latest variable definition or undefined.
+ */
+function getLatestVariableDefinition(variable) {
+  return variable.defs[variable.defs.length - 1];
+}
+
+module.exports = {
+  findVariable,
+  findVariableByName,
+  getVariable,
+  getVariableFromContext,
+  getLatestVariableDefinition,
+};
Index: frontend/node_modules/eslint-plugin-react/lib/util/version.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/version.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/version.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,6 @@
+export function testReactVersion(context: any, semverRange: any): boolean;
+export function testFlowVersion(context: any, semverRange: any): boolean;
+export function resetWarningFlag(): void;
+export function resetDetectedVersion(): void;
+export function resetDefaultVersion(): void;
+//# sourceMappingURL=version.d.ts.map
Index: frontend/node_modules/eslint-plugin-react/lib/util/version.d.ts.map
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/version.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/version.d.ts.map	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["version.js"],"names":[],"mappings":"AAmLA,0EAEC;AAED,yEAEC;AAvKD,yCAEC;AAID,6CAEC;AA6BD,4CAEC"}
Index: frontend/node_modules/eslint-plugin-react/lib/util/version.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/lib/util/version.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/lib/util/version.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,194 @@
+/**
+ * @fileoverview Utility functions for React and Flow version configuration
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+
+const resolve = require('resolve');
+const semver = require('semver');
+const error = require('./error');
+
+const ULTIMATE_LATEST_SEMVER = '999.999.999';
+
+let warnedForMissingVersion = false;
+
+function resetWarningFlag() {
+  warnedForMissingVersion = false;
+}
+
+let cachedDetectedReactVersion;
+
+function resetDetectedVersion() {
+  cachedDetectedReactVersion = undefined;
+}
+
+function resolveBasedir(contextOrFilename) {
+  if (contextOrFilename) {
+    const filename = typeof contextOrFilename === 'string' ? contextOrFilename : contextOrFilename.getFilename();
+    const dirname = path.dirname(filename);
+    try {
+      if (fs.statSync(filename).isFile()) {
+        // dirname must be dir here
+        return dirname;
+      }
+    } catch (err) {
+      // https://github.com/eslint/eslint/issues/11989
+      if (err.code === 'ENOTDIR') {
+        // virtual filename could be recursive
+        return resolveBasedir(dirname);
+      }
+    }
+  }
+  return process.cwd();
+}
+
+function convertConfVerToSemver(confVer) {
+  const fullSemverString = /^[0-9]+\.[0-9]+$/.test(confVer) ? `${confVer}.0` : confVer;
+  return semver.coerce(fullSemverString.split('.').map((part) => Number(part)).join('.'));
+}
+
+let defaultVersion = ULTIMATE_LATEST_SEMVER;
+
+function resetDefaultVersion() {
+  defaultVersion = ULTIMATE_LATEST_SEMVER;
+}
+
+function readDefaultReactVersionFromContext(context) {
+  // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+  if (context.settings && context.settings.react && context.settings.react.defaultVersion) {
+    let settingsDefaultVersion = context.settings.react.defaultVersion;
+    if (typeof settingsDefaultVersion !== 'string') {
+      error(`Warning: default React version specified in eslint-pluigin-react-settings must be a string; got "${typeof settingsDefaultVersion}"`);
+    }
+    settingsDefaultVersion = String(settingsDefaultVersion);
+    const result = convertConfVerToSemver(settingsDefaultVersion);
+    if (result) {
+      defaultVersion = result.version;
+    } else {
+      error(`Warning: React version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${settingsDefaultVersion}”. Falling back to latest version as default.`);
+    }
+  } else {
+    defaultVersion = ULTIMATE_LATEST_SEMVER;
+  }
+}
+
+// TODO, semver-major: remove context fallback
+function detectReactVersion(context) {
+  if (cachedDetectedReactVersion) {
+    return cachedDetectedReactVersion;
+  }
+
+  const basedir = resolveBasedir(context);
+
+  try {
+    const reactPath = resolve.sync('react', { basedir });
+    const react = require(reactPath); // eslint-disable-line global-require, import/no-dynamic-require
+    cachedDetectedReactVersion = react.version;
+    return cachedDetectedReactVersion;
+  } catch (e) {
+    if (e.code === 'MODULE_NOT_FOUND') {
+      if (!warnedForMissingVersion) {
+        let sentence2 = 'Assuming latest React version for linting.';
+        if (defaultVersion !== ULTIMATE_LATEST_SEMVER) {
+          sentence2 = `Assuming default React version for linting: "${defaultVersion}".`;
+        }
+        error(`Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. ${sentence2}`);
+        warnedForMissingVersion = true;
+      }
+      cachedDetectedReactVersion = defaultVersion;
+      return cachedDetectedReactVersion;
+    }
+    throw e;
+  }
+}
+
+function getReactVersionFromContext(context) {
+  readDefaultReactVersionFromContext(context);
+  let confVer = defaultVersion;
+  // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+  if (context.settings && context.settings.react && context.settings.react.version) {
+    let settingsVersion = context.settings.react.version;
+    if (settingsVersion === 'detect') {
+      settingsVersion = detectReactVersion(context);
+    }
+    if (typeof settingsVersion !== 'string') {
+      error(`Warning: React version specified in eslint-plugin-react-settings must be a string; got “${typeof settingsVersion}”`);
+    }
+    confVer = String(settingsVersion);
+  } else if (!warnedForMissingVersion) {
+    error('Warning: React version not specified in eslint-plugin-react settings. See https://github.com/jsx-eslint/eslint-plugin-react#configuration .');
+    warnedForMissingVersion = true;
+  }
+
+  const result = convertConfVerToSemver(confVer);
+  if (!result) {
+    error(`Warning: React version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${confVer}”`);
+  }
+  return result ? result.version : defaultVersion;
+}
+
+// TODO, semver-major: remove context fallback
+function detectFlowVersion(context) {
+  const basedir = resolveBasedir(context);
+
+  try {
+    const flowPackageJsonPath = resolve.sync('flow-bin/package.json', { basedir });
+    const flowPackageJson = require(flowPackageJsonPath); // eslint-disable-line global-require, import/no-dynamic-require
+    return flowPackageJson.version;
+  } catch (e) {
+    if (e.code === 'MODULE_NOT_FOUND') {
+      error('Warning: Flow version was set to "detect" in eslint-plugin-react settings, '
+        + 'but the "flow-bin" package is not installed. Assuming latest Flow version for linting.');
+      return ULTIMATE_LATEST_SEMVER;
+    }
+    throw e;
+  }
+}
+
+function getFlowVersionFromContext(context) {
+  let confVer = defaultVersion;
+  // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+  if (context.settings.react && context.settings.react.flowVersion) {
+    let flowVersion = context.settings.react.flowVersion;
+    if (flowVersion === 'detect') {
+      flowVersion = detectFlowVersion(context);
+    }
+    if (typeof flowVersion !== 'string') {
+      error('Warning: Flow version specified in eslint-plugin-react-settings must be a string; '
+        + `got “${typeof flowVersion}”`);
+    }
+    confVer = String(flowVersion);
+  } else {
+    throw 'Could not retrieve flowVersion from settings'; // eslint-disable-line no-throw-literal
+  }
+
+  const result = convertConfVerToSemver(confVer);
+  if (!result) {
+    error(`Warning: Flow version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${confVer}”`);
+  }
+  return result ? result.version : defaultVersion;
+}
+
+function test(semverRange, confVer) {
+  return semver.satisfies(confVer, semverRange);
+}
+
+function testReactVersion(context, semverRange) {
+  return test(semverRange, getReactVersionFromContext(context));
+}
+
+function testFlowVersion(context, semverRange) {
+  return test(semverRange, getFlowVersionFromContext(context));
+}
+
+module.exports = {
+  testReactVersion,
+  testFlowVersion,
+  resetWarningFlag,
+  resetDetectedVersion,
+  resetDefaultVersion,
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*)
+        if command -v cygpath > /dev/null 2>&1; then
+            basedir=`cygpath -w "$basedir"`
+        fi
+    ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../resolve/bin/resolve" "$@"
+else 
+  exec node  "$basedir/../resolve/bin/resolve" "$@"
+fi
Index: frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve.cmd
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve.cmd	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve.cmd	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\resolve\bin\resolve" %*
Index: frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve.ps1
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve.ps1	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/.bin/resolve.ps1	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../resolve/bin/resolve" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../resolve/bin/resolve" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../resolve/bin/resolve" $args
+  } else {
+    & "node$exe"  "$basedir/../resolve/bin/resolve" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret
Index: frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*)
+        if command -v cygpath > /dev/null 2>&1; then
+            basedir=`cygpath -w "$basedir"`
+        fi
+    ;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../semver/bin/semver.js" "$@"
+else 
+  exec node  "$basedir/../semver/bin/semver.js" "$@"
+fi
Index: frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver.cmd
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver.cmd	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver.cmd	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\semver\bin\semver.js" %*
Index: frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver.ps1
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver.ps1	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/.bin/semver.ps1	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../semver/bin/semver.js" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../semver/bin/semver.js" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../semver/bin/semver.js" $args
+  } else {
+    & "node$exe"  "$basedir/../semver/bin/semver.js" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/CHANGELOG.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/CHANGELOG.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/CHANGELOG.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,94 @@
+v2.1.0 - January 6, 2018
+
+* 827f314 Update: support node ranges (fixes #89) (#190) (Teddy Katz)
+
+v2.0.2 - November 25, 2017
+
+* 5049ee3 Fix: Remove redundant LICENSE/README names from files (#203) (Kevin Partington)
+
+v2.0.1 - November 10, 2017
+
+* 009f33d Fix: Making sure union type stringification respects compact flag (#199) (Mitermayer Reis)
+* 19da935 Use native String.prototype.trim instead of a custom implementation. (#201) (Rouven Weßling)
+* e3a011b chore: add mocha.opts to restore custom mocha config (Jason Kurian)
+* d888200 chore: adds nyc and a newer version of mocha to accurately report coverage (Jason Kurian)
+* 6b210a8 fix: support type expression for @this tag (fixes #181) (#182) (Frédéric Junod)
+* 1c4a4c7 fix: Allow array indexes in names (#193) (Tom MacWright)
+* 9aed54d Fix incorrect behavior when arrow functions are used as default values (#189) (Gaurab Paul)
+* 9efb6ca Upgrade: Use Array.isArray instead of isarray package (#195) (medanat)
+
+v2.0.0 - November 15, 2016
+
+* 7d7c5f1 Breaking: Re-license to Apache 2 (fixes #176) (#178) (Nicholas C. Zakas)
+* 5496132 Docs: Update license copyright (Nicholas C. Zakas)
+
+v1.5.0 - October 13, 2016
+
+* e33c6bb Update: Add support for BooleanLiteralType (#173) (Erik Arvidsson)
+
+v1.4.0 - September 13, 2016
+
+* d7426e5 Update: add ability to parse optional properties in typedefs (refs #5) (#174) (ikokostya)
+
+v1.3.0 - August 22, 2016
+
+* 12c7ad9 Update: Add support for numeric and string literal types (fixes #156) (#172) (Andrew Walter)
+
+v1.2.3 - August 16, 2016
+
+* b96a884 Build: Add CI release script (Nicholas C. Zakas)
+* 8d9b3c7 Upgrade: Upgrade esutils to v2.0.2 (fixes #170) (#171) (Emeegeemee)
+
+v1.2.2 - May 19, 2016
+
+* ebe0b08 Fix: Support case insensitive tags (fixes #163) (#164) (alberto)
+* 8e6d81e Chore: Remove copyright and license from headers (Nicholas C. Zakas)
+* 79035c6 Chore: Include jQuery Foundation copyright (Nicholas C. Zakas)
+* 06910a7 Fix: Preserve whitespace in default param string values (fixes #157) (Kai Cataldo)
+
+v1.2.1 - March 29, 2016
+
+* 1f54014 Fix: allow hyphens in names (fixes #116) (Kai Cataldo)
+* bbee469 Docs: Add issue template (Nicholas C. Zakas)
+
+v1.2.0 - February 19, 2016
+
+* 18136c5 Build: Cleanup build system (Nicholas C. Zakas)
+* b082f85 Update: Add support for slash in namepaths (fixes #100) (Ryan Duffy)
+* def53a2 Docs: Fix typo in option lineNumbers (Daniel Tschinder)
+* e2cbbc5 Update: Bump isarray to v1.0.0 (Shinnosuke Watanabe)
+* ae07aa8 Fix: Allow whitespace in optional param with default value (fixes #141) (chris)
+
+v1.1.0 - January 6, 2016
+
+* Build: Switch to Makefile.js (Nicholas C. Zakas)
+* New: support name expression for @this tag (fixes #143) (Tim Schaub)
+* Build: Update ESLint settings (Nicholas C. Zakas)
+
+v1.0.0 - December 21, 2015
+
+* New: parse caption tags in examples into separate property. (fixes #131) (Tom MacWright)
+
+v0.7.2 - November 27, 2015
+
+* Fix: Line numbers for some tags (fixes #138) Fixing issue where input was not consumed via advance() but was skipped when parsing tags resulting in sometimes incorrect reported lineNumber. (TEHEK)
+* Build: Add missing linefix package (Nicholas C. Zakas)
+
+v0.7.1 - November 13, 2015
+
+* Update: Begin switch to Makefile.js (Nicholas C. Zakas)
+* Fix: permit return tag without type (fixes #136) (Tom MacWright)
+* Fix: package.json homepage field (Bogdan Chadkin)
+* Fix: Parse array default syntax. Fixes #133 (Tom MacWright)
+* Fix: Last tag always has \n in the description (fixes #87) (Burak Yigit Kaya)
+* Docs: Add changelog (Nicholas C. Zakas)
+
+v0.7.0 - September 21, 2015
+
+* Docs: Update README with new info (fixes #127) (Nicholas C. Zakas)
+* Fix: Parsing fix for param with arrays and properties (fixes #111) (Gyandeep Singh)
+* Build: Add travis build (fixes #123) (Gyandeep Singh)
+* Fix: Parsing of parameter name without a type (fixes #120) (Gyandeep Singh)
+* New: added preserveWhitespace option (Aleks Totic)
+* New: Add "files" entry to only deploy select files (Rob Loach)
+* New: Add support and tests for typedefs. Refs #5 (Tom MacWright)
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,177 @@
+
+                             Apache License
+                       Version 2.0, January 2004
+                    http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+  "License" shall mean the terms and conditions for use, reproduction,
+  and distribution as defined by Sections 1 through 9 of this document.
+
+  "Licensor" shall mean the copyright owner or entity authorized by
+  the copyright owner that is granting the License.
+
+  "Legal Entity" shall mean the union of the acting entity and all
+  other entities that control, are controlled by, or are under common
+  control with that entity. For the purposes of this definition,
+  "control" means (i) the power, direct or indirect, to cause the
+  direction or management of such entity, whether by contract or
+  otherwise, or (ii) ownership of fifty percent (50%) or more of the
+  outstanding shares, or (iii) beneficial ownership of such entity.
+
+  "You" (or "Your") shall mean an individual or Legal Entity
+  exercising permissions granted by this License.
+
+  "Source" form shall mean the preferred form for making modifications,
+  including but not limited to software source code, documentation
+  source, and configuration files.
+
+  "Object" form shall mean any form resulting from mechanical
+  transformation or translation of a Source form, including but
+  not limited to compiled object code, generated documentation,
+  and conversions to other media types.
+
+  "Work" shall mean the work of authorship, whether in Source or
+  Object form, made available under the License, as indicated by a
+  copyright notice that is included in or attached to the work
+  (an example is provided in the Appendix below).
+
+  "Derivative Works" shall mean any work, whether in Source or Object
+  form, that is based on (or derived from) the Work and for which the
+  editorial revisions, annotations, elaborations, or other modifications
+  represent, as a whole, an original work of authorship. For the purposes
+  of this License, Derivative Works shall not include works that remain
+  separable from, or merely link (or bind by name) to the interfaces of,
+  the Work and Derivative Works thereof.
+
+  "Contribution" shall mean any work of authorship, including
+  the original version of the Work and any modifications or additions
+  to that Work or Derivative Works thereof, that is intentionally
+  submitted to Licensor for inclusion in the Work by the copyright owner
+  or by an individual or Legal Entity authorized to submit on behalf of
+  the copyright owner. For the purposes of this definition, "submitted"
+  means any form of electronic, verbal, or written communication sent
+  to the Licensor or its representatives, including but not limited to
+  communication on electronic mailing lists, source code control systems,
+  and issue tracking systems that are managed by, or on behalf of, the
+  Licensor for the purpose of discussing and improving the Work, but
+  excluding communication that is conspicuously marked or otherwise
+  designated in writing by the copyright owner as "Not a Contribution."
+
+  "Contributor" shall mean Licensor and any individual or Legal Entity
+  on behalf of whom a Contribution has been received by Licensor and
+  subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  copyright license to reproduce, prepare Derivative Works of,
+  publicly display, publicly perform, sublicense, and distribute the
+  Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  (except as stated in this section) patent license to make, have made,
+  use, offer to sell, sell, import, and otherwise transfer the Work,
+  where such license applies only to those patent claims licensable
+  by such Contributor that are necessarily infringed by their
+  Contribution(s) alone or by combination of their Contribution(s)
+  with the Work to which such Contribution(s) was submitted. If You
+  institute patent litigation against any entity (including a
+  cross-claim or counterclaim in a lawsuit) alleging that the Work
+  or a Contribution incorporated within the Work constitutes direct
+  or contributory patent infringement, then any patent licenses
+  granted to You under this License for that Work shall terminate
+  as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+  Work or Derivative Works thereof in any medium, with or without
+  modifications, and in Source or Object form, provided that You
+  meet the following conditions:
+
+  (a) You must give any other recipients of the Work or
+      Derivative Works a copy of this License; and
+
+  (b) You must cause any modified files to carry prominent notices
+      stating that You changed the files; and
+
+  (c) You must retain, in the Source form of any Derivative Works
+      that You distribute, all copyright, patent, trademark, and
+      attribution notices from the Source form of the Work,
+      excluding those notices that do not pertain to any part of
+      the Derivative Works; and
+
+  (d) If the Work includes a "NOTICE" text file as part of its
+      distribution, then any Derivative Works that You distribute must
+      include a readable copy of the attribution notices contained
+      within such NOTICE file, excluding those notices that do not
+      pertain to any part of the Derivative Works, in at least one
+      of the following places: within a NOTICE text file distributed
+      as part of the Derivative Works; within the Source form or
+      documentation, if provided along with the Derivative Works; or,
+      within a display generated by the Derivative Works, if and
+      wherever such third-party notices normally appear. The contents
+      of the NOTICE file are for informational purposes only and
+      do not modify the License. You may add Your own attribution
+      notices within Derivative Works that You distribute, alongside
+      or as an addendum to the NOTICE text from the Work, provided
+      that such additional attribution notices cannot be construed
+      as modifying the License.
+
+  You may add Your own copyright statement to Your modifications and
+  may provide additional or different license terms and conditions
+  for use, reproduction, or distribution of Your modifications, or
+  for any such Derivative Works as a whole, provided Your use,
+  reproduction, and distribution of the Work otherwise complies with
+  the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+  any Contribution intentionally submitted for inclusion in the Work
+  by You to the Licensor shall be under the terms and conditions of
+  this License, without any additional terms or conditions.
+  Notwithstanding the above, nothing herein shall supersede or modify
+  the terms of any separate license agreement you may have executed
+  with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+  names, trademarks, service marks, or product names of the Licensor,
+  except as required for reasonable and customary use in describing the
+  origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+  agreed to in writing, Licensor provides the Work (and each
+  Contributor provides its Contributions) on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+  implied, including, without limitation, any warranties or conditions
+  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+  PARTICULAR PURPOSE. You are solely responsible for determining the
+  appropriateness of using or redistributing the Work and assume any
+  risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+  whether in tort (including negligence), contract, or otherwise,
+  unless required by applicable law (such as deliberate and grossly
+  negligent acts) or agreed to in writing, shall any Contributor be
+  liable to You for damages, including any direct, indirect, special,
+  incidental, or consequential damages of any character arising as a
+  result of this License or out of the use or inability to use the
+  Work (including but not limited to damages for loss of goodwill,
+  work stoppage, computer failure or malfunction, or any and all
+  other commercial damages or losses), even if such Contributor
+  has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+  the Work or Derivative Works thereof, You may choose to offer,
+  and charge a fee for, acceptance of support, warranty, indemnity,
+  or other liability obligations and/or rights consistent with this
+  License. However, in accepting such obligations, You may act only
+  on Your own behalf and on Your sole responsibility, not on behalf
+  of any other Contributor, and only if You agree to indemnify,
+  defend, and hold each Contributor harmless for any liability
+  incurred by, or claims asserted against, such Contributor by reason
+  of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE.closure-compiler
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE.closure-compiler	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE.closure-compiler	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE.esprima
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE.esprima	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/LICENSE.esprima	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,19 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/README.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,165 @@
+[![NPM version][npm-image]][npm-url]
+[![build status][travis-image]][travis-url]
+[![Test coverage][coveralls-image]][coveralls-url]
+[![Downloads][downloads-image]][downloads-url]
+[![Join the chat at https://gitter.im/eslint/doctrine](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/eslint/doctrine?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+# Doctrine
+
+Doctrine is a [JSDoc](http://usejsdoc.org) parser that parses documentation comments from JavaScript (you need to pass in the comment, not a whole JavaScript file).
+
+## Installation
+
+You can install Doctrine using [npm](https://npmjs.com):
+
+```
+$ npm install doctrine --save-dev
+```
+
+Doctrine can also be used in web browsers using [Browserify](http://browserify.org).
+
+## Usage
+
+Require doctrine inside of your JavaScript:
+
+```js
+var doctrine = require("doctrine");
+```
+
+### parse()
+
+The primary method is `parse()`, which accepts two arguments: the JSDoc comment to parse and an optional options object. The available options are:
+
+* `unwrap` - set to `true` to delete the leading `/**`, any `*` that begins a line, and the trailing `*/` from the source text. Default: `false`.
+* `tags` - an array of tags to return. When specified, Doctrine returns only tags in this array. For example, if `tags` is `["param"]`, then only `@param` tags will be returned. Default: `null`.
+* `recoverable` - set to `true` to keep parsing even when syntax errors occur. Default: `false`.
+* `sloppy` - set to `true` to allow optional parameters to be specified in brackets (`@param {string} [foo]`). Default: `false`.
+* `lineNumbers` - set to `true` to add `lineNumber` to each node, specifying the line on which the node is found in the source. Default: `false`.
+* `range` - set to `true` to add `range` to each node, specifying the start and end index of the node in the original comment. Default: `false`.
+
+Here's a simple example:
+
+```js
+var ast = doctrine.parse(
+    [
+        "/**",
+        " * This function comment is parsed by doctrine",
+        " * @param {{ok:String}} userName",
+        "*/"
+    ].join('\n'), { unwrap: true });
+```
+
+This example returns the following AST:
+
+    {
+        "description": "This function comment is parsed by doctrine",
+        "tags": [
+            {
+                "title": "param",
+                "description": null,
+                "type": {
+                    "type": "RecordType",
+                    "fields": [
+                        {
+                            "type": "FieldType",
+                            "key": "ok",
+                            "value": {
+                                "type": "NameExpression",
+                                "name": "String"
+                            }
+                        }
+                    ]
+                },
+                "name": "userName"
+            }
+        ]
+    }
+
+See the [demo page](http://eslint.org/doctrine/demo/) more detail.
+
+## Team
+
+These folks keep the project moving and are resources for help:
+
+* Nicholas C. Zakas ([@nzakas](https://github.com/nzakas)) - project lead
+* Yusuke Suzuki ([@constellation](https://github.com/constellation)) - reviewer
+
+## Contributing
+
+Issues and pull requests will be triaged and responded to as quickly as possible. We operate under the [ESLint Contributor Guidelines](http://eslint.org/docs/developer-guide/contributing), so please be sure to read them before contributing. If you're not sure where to dig in, check out the [issues](https://github.com/eslint/doctrine/issues).
+
+## Frequently Asked Questions
+
+### Can I pass a whole JavaScript file to Doctrine?
+
+No. Doctrine can only parse JSDoc comments, so you'll need to pass just the JSDoc comment to Doctrine in order to work.
+
+
+### License
+
+#### doctrine
+
+Copyright JS Foundation and other contributors, https://js.foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+#### esprima
+
+some of functions is derived from esprima
+
+Copyright (C) 2012, 2011 [Ariya Hidayat](http://ariya.ofilabs.com/about)
+ (twitter: [@ariyahidayat](http://twitter.com/ariyahidayat)) and other contributors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#### closure-compiler
+
+some of extensions is derived from closure-compiler
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+
+### Where to ask for help?
+
+Join our [Chatroom](https://gitter.im/eslint/doctrine)
+
+[npm-image]: https://img.shields.io/npm/v/doctrine.svg?style=flat-square
+[npm-url]: https://www.npmjs.com/package/doctrine
+[travis-image]: https://img.shields.io/travis/eslint/doctrine/master.svg?style=flat-square
+[travis-url]: https://travis-ci.org/eslint/doctrine
+[coveralls-image]: https://img.shields.io/coveralls/eslint/doctrine/master.svg?style=flat-square
+[coveralls-url]: https://coveralls.io/r/eslint/doctrine?branch=master
+[downloads-image]: http://img.shields.io/npm/dm/doctrine.svg?style=flat-square
+[downloads-url]: https://www.npmjs.com/package/doctrine
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/doctrine.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/doctrine.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/doctrine.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,899 @@
+/*
+ * @fileoverview Main Doctrine object
+ * @author Yusuke Suzuki <utatane.tea@gmail.com>
+ * @author Dan Tao <daniel.tao@gmail.com>
+ * @author Andrew Eisenberg <andrew@eisenberg.as>
+ */
+
+(function () {
+    'use strict';
+
+    var typed,
+        utility,
+        jsdoc,
+        esutils,
+        hasOwnProperty;
+
+    esutils = require('esutils');
+    typed = require('./typed');
+    utility = require('./utility');
+
+    function sliceSource(source, index, last) {
+        return source.slice(index, last);
+    }
+
+    hasOwnProperty = (function () {
+        var func = Object.prototype.hasOwnProperty;
+        return function hasOwnProperty(obj, name) {
+            return func.call(obj, name);
+        };
+    }());
+
+    function shallowCopy(obj) {
+        var ret = {}, key;
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                ret[key] = obj[key];
+            }
+        }
+        return ret;
+    }
+
+    function isASCIIAlphanumeric(ch) {
+        return (ch >= 0x61  /* 'a' */ && ch <= 0x7A  /* 'z' */) ||
+            (ch >= 0x41  /* 'A' */ && ch <= 0x5A  /* 'Z' */) ||
+            (ch >= 0x30  /* '0' */ && ch <= 0x39  /* '9' */);
+    }
+
+    function isParamTitle(title) {
+        return title === 'param' || title === 'argument' || title === 'arg';
+    }
+
+    function isReturnTitle(title) {
+        return title === 'return' || title === 'returns';
+    }
+
+    function isProperty(title) {
+        return title === 'property' || title === 'prop';
+    }
+
+    function isNameParameterRequired(title) {
+        return isParamTitle(title) || isProperty(title) ||
+            title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
+    }
+
+    function isAllowedName(title) {
+        return isNameParameterRequired(title) || title === 'const' || title === 'constant';
+    }
+
+    function isAllowedNested(title) {
+        return isProperty(title) || isParamTitle(title);
+    }
+
+    function isAllowedOptional(title) {
+        return isProperty(title) || isParamTitle(title);
+    }
+
+    function isTypeParameterRequired(title) {
+        return isParamTitle(title) || isReturnTitle(title) ||
+            title === 'define' || title === 'enum' ||
+            title === 'implements' || title === 'this' ||
+            title === 'type' || title === 'typedef' || isProperty(title);
+    }
+
+    // Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
+    // This would require changes to 'parseType'
+    function isAllowedType(title) {
+        return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
+            title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
+            title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
+            title === 'public' || title === 'private' || title === 'protected';
+    }
+
+    // A regex character class that contains all whitespace except linebreak characters (\r, \n, \u2028, \u2029)
+    var WHITESPACE = '[ \\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]';
+
+    var STAR_MATCHER = '(' + WHITESPACE + '*(?:\\*' + WHITESPACE + '?)?)(.+|[\r\n\u2028\u2029])';
+
+    function unwrapComment(doc) {
+        // JSDoc comment is following form
+        //   /**
+        //    * .......
+        //    */
+
+        return doc.
+            // remove /**
+            replace(/^\/\*\*?/, '').
+            // remove */
+            replace(/\*\/$/, '').
+            // remove ' * ' at the beginning of a line
+            replace(new RegExp(STAR_MATCHER, 'g'), '$2').
+            // remove trailing whitespace
+            replace(/\s*$/, '');
+    }
+
+    /**
+     * Converts an index in an "unwrapped" JSDoc comment to the corresponding index in the original "wrapped" version
+     * @param {string} originalSource The original wrapped comment
+     * @param {number} unwrappedIndex The index of a character in the unwrapped string
+     * @returns {number} The index of the corresponding character in the original wrapped string
+     */
+    function convertUnwrappedCommentIndex(originalSource, unwrappedIndex) {
+        var replacedSource = originalSource.replace(/^\/\*\*?/, '');
+        var numSkippedChars = 0;
+        var matcher = new RegExp(STAR_MATCHER, 'g');
+        var match;
+
+        while ((match = matcher.exec(replacedSource))) {
+            numSkippedChars += match[1].length;
+
+            if (match.index + match[0].length > unwrappedIndex + numSkippedChars) {
+                return unwrappedIndex + numSkippedChars + originalSource.length - replacedSource.length;
+            }
+        }
+
+        return originalSource.replace(/\*\/$/, '').replace(/\s*$/, '').length;
+    }
+
+    // JSDoc Tag Parser
+
+    (function (exports) {
+        var Rules,
+            index,
+            lineNumber,
+            length,
+            source,
+            originalSource,
+            recoverable,
+            sloppy,
+            strict;
+
+        function advance() {
+            var ch = source.charCodeAt(index);
+            index += 1;
+            if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(index) === 0x0A  /* '\n' */)) {
+                lineNumber += 1;
+            }
+            return String.fromCharCode(ch);
+        }
+
+        function scanTitle() {
+            var title = '';
+            // waste '@'
+            advance();
+
+            while (index < length && isASCIIAlphanumeric(source.charCodeAt(index))) {
+                title += advance();
+            }
+
+            return title;
+        }
+
+        function seekContent() {
+            var ch, waiting, last = index;
+
+            waiting = false;
+            while (last < length) {
+                ch = source.charCodeAt(last);
+                if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(last + 1) === 0x0A  /* '\n' */)) {
+                    waiting = true;
+                } else if (waiting) {
+                    if (ch === 0x40  /* '@' */) {
+                        break;
+                    }
+                    if (!esutils.code.isWhiteSpace(ch)) {
+                        waiting = false;
+                    }
+                }
+                last += 1;
+            }
+            return last;
+        }
+
+        // type expression may have nest brace, such as,
+        // { { ok: string } }
+        //
+        // therefore, scanning type expression with balancing braces.
+        function parseType(title, last, addRange) {
+            var ch, brace, type, startIndex, direct = false;
+
+
+            // search '{'
+            while (index < last) {
+                ch = source.charCodeAt(index);
+                if (esutils.code.isWhiteSpace(ch)) {
+                    advance();
+                } else if (ch === 0x7B  /* '{' */) {
+                    advance();
+                    break;
+                } else {
+                    // this is direct pattern
+                    direct = true;
+                    break;
+                }
+            }
+
+
+            if (direct) {
+                return null;
+            }
+
+            // type expression { is found
+            brace = 1;
+            type = '';
+            while (index < last) {
+                ch = source.charCodeAt(index);
+                if (esutils.code.isLineTerminator(ch)) {
+                    advance();
+                } else {
+                    if (ch === 0x7D  /* '}' */) {
+                        brace -= 1;
+                        if (brace === 0) {
+                            advance();
+                            break;
+                        }
+                    } else if (ch === 0x7B  /* '{' */) {
+                        brace += 1;
+                    }
+                    if (type === '') {
+                        startIndex = index;
+                    }
+                    type += advance();
+                }
+            }
+
+            if (brace !== 0) {
+                // braces is not balanced
+                return utility.throwError('Braces are not balanced');
+            }
+
+            if (isAllowedOptional(title)) {
+                return typed.parseParamType(type, {startIndex: convertIndex(startIndex), range: addRange});
+            }
+
+            return typed.parseType(type, {startIndex: convertIndex(startIndex), range: addRange});
+        }
+
+        function scanIdentifier(last) {
+            var identifier;
+            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index)) && !source[index].match(/[0-9]/)) {
+                return null;
+            }
+            identifier = advance();
+            while (index < last && esutils.code.isIdentifierPartES5(source.charCodeAt(index))) {
+                identifier += advance();
+            }
+            return identifier;
+        }
+
+        function skipWhiteSpace(last) {
+            while (index < last && (esutils.code.isWhiteSpace(source.charCodeAt(index)) || esutils.code.isLineTerminator(source.charCodeAt(index)))) {
+                advance();
+            }
+        }
+
+        function parseName(last, allowBrackets, allowNestedParams) {
+            var name = '',
+                useBrackets,
+                insideString;
+
+
+            skipWhiteSpace(last);
+
+            if (index >= last) {
+                return null;
+            }
+
+            if (source.charCodeAt(index) === 0x5B  /* '[' */) {
+                if (allowBrackets) {
+                    useBrackets = true;
+                    name = advance();
+                } else {
+                    return null;
+                }
+            }
+
+            name += scanIdentifier(last);
+
+            if (allowNestedParams) {
+                if (source.charCodeAt(index) === 0x3A /* ':' */ && (
+                        name === 'module' ||
+                        name === 'external' ||
+                        name === 'event')) {
+                    name += advance();
+                    name += scanIdentifier(last);
+
+                }
+                if(source.charCodeAt(index) === 0x5B  /* '[' */ && source.charCodeAt(index + 1) === 0x5D  /* ']' */){
+                    name += advance();
+                    name += advance();
+                }
+                while (source.charCodeAt(index) === 0x2E  /* '.' */ ||
+                        source.charCodeAt(index) === 0x2F  /* '/' */ ||
+                        source.charCodeAt(index) === 0x23  /* '#' */ ||
+                        source.charCodeAt(index) === 0x2D  /* '-' */ ||
+                        source.charCodeAt(index) === 0x7E  /* '~' */) {
+                    name += advance();
+                    name += scanIdentifier(last);
+                }
+            }
+
+            if (useBrackets) {
+                skipWhiteSpace(last);
+                // do we have a default value for this?
+                if (source.charCodeAt(index) === 0x3D  /* '=' */) {
+                    // consume the '='' symbol
+                    name += advance();
+                    skipWhiteSpace(last);
+
+                    var ch;
+                    var bracketDepth = 1;
+
+                    // scan in the default value
+                    while (index < last) {
+                        ch = source.charCodeAt(index);
+
+                        if (esutils.code.isWhiteSpace(ch)) {
+                            if (!insideString) {
+                                skipWhiteSpace(last);
+                                ch = source.charCodeAt(index);
+                            }
+                        }
+
+                        if (ch === 0x27 /* ''' */) {
+                            if (!insideString) {
+                                insideString = '\'';
+                            } else {
+                                if (insideString === '\'') {
+                                    insideString = '';
+                                }
+                            }
+                        }
+
+                        if (ch === 0x22 /* '"' */) {
+                            if (!insideString) {
+                                insideString = '"';
+                            } else {
+                                if (insideString === '"') {
+                                    insideString = '';
+                                }
+                            }
+                        }
+
+                        if (ch === 0x5B /* '[' */) {
+                            bracketDepth++;
+                        } else if (ch === 0x5D  /* ']' */ &&
+                            --bracketDepth === 0) {
+                            break;
+                        }
+
+                        name += advance();
+                    }
+                }
+
+                skipWhiteSpace(last);
+
+                if (index >= last || source.charCodeAt(index) !== 0x5D  /* ']' */) {
+                    // we never found a closing ']'
+                    return null;
+                }
+
+                // collect the last ']'
+                name += advance();
+            }
+
+            return name;
+        }
+
+        function skipToTag() {
+            while (index < length && source.charCodeAt(index) !== 0x40  /* '@' */) {
+                advance();
+            }
+            if (index >= length) {
+                return false;
+            }
+            utility.assert(source.charCodeAt(index) === 0x40  /* '@' */);
+            return true;
+        }
+
+        function convertIndex(rangeIndex) {
+            if (source === originalSource) {
+                return rangeIndex;
+            }
+            return convertUnwrappedCommentIndex(originalSource, rangeIndex);
+        }
+
+        function TagParser(options, title) {
+            this._options = options;
+            this._title = title.toLowerCase();
+            this._tag = {
+                title: title,
+                description: null
+            };
+            if (this._options.lineNumbers) {
+                this._tag.lineNumber = lineNumber;
+            }
+            this._first = index - title.length - 1;
+            this._last = 0;
+            // space to save special information for title parsers.
+            this._extra = { };
+        }
+
+        // addError(err, ...)
+        TagParser.prototype.addError = function addError(errorText) {
+            var args = Array.prototype.slice.call(arguments, 1),
+                msg = errorText.replace(
+                    /%(\d)/g,
+                    function (whole, index) {
+                        utility.assert(index < args.length, 'Message reference must be in range');
+                        return args[index];
+                    }
+                );
+
+            if (!this._tag.errors) {
+                this._tag.errors = [];
+            }
+            if (strict) {
+                utility.throwError(msg);
+            }
+            this._tag.errors.push(msg);
+            return recoverable;
+        };
+
+        TagParser.prototype.parseType = function () {
+            // type required titles
+            if (isTypeParameterRequired(this._title)) {
+                try {
+                    this._tag.type = parseType(this._title, this._last, this._options.range);
+                    if (!this._tag.type) {
+                        if (!isParamTitle(this._title) && !isReturnTitle(this._title)) {
+                            if (!this.addError('Missing or invalid tag type')) {
+                                return false;
+                            }
+                        }
+                    }
+                } catch (error) {
+                    this._tag.type = null;
+                    if (!this.addError(error.message)) {
+                        return false;
+                    }
+                }
+            } else if (isAllowedType(this._title)) {
+                // optional types
+                try {
+                    this._tag.type = parseType(this._title, this._last, this._options.range);
+                } catch (e) {
+                    //For optional types, lets drop the thrown error when we hit the end of the file
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype._parseNamePath = function (optional) {
+            var name;
+            name = parseName(this._last, sloppy && isAllowedOptional(this._title), true);
+            if (!name) {
+                if (!optional) {
+                    if (!this.addError('Missing or invalid tag name')) {
+                        return false;
+                    }
+                }
+            }
+            this._tag.name = name;
+            return true;
+        };
+
+        TagParser.prototype.parseNamePath = function () {
+            return this._parseNamePath(false);
+        };
+
+        TagParser.prototype.parseNamePathOptional = function () {
+            return this._parseNamePath(true);
+        };
+
+
+        TagParser.prototype.parseName = function () {
+            var assign, name;
+
+            // param, property requires name
+            if (isAllowedName(this._title)) {
+                this._tag.name = parseName(this._last, sloppy && isAllowedOptional(this._title), isAllowedNested(this._title));
+                if (!this._tag.name) {
+                    if (!isNameParameterRequired(this._title)) {
+                        return true;
+                    }
+
+                    // it's possible the name has already been parsed but interpreted as a type
+                    // it's also possible this is a sloppy declaration, in which case it will be
+                    // fixed at the end
+                    if (isParamTitle(this._title) && this._tag.type && this._tag.type.name) {
+                        this._extra.name = this._tag.type;
+                        this._tag.name = this._tag.type.name;
+                        this._tag.type = null;
+                    } else {
+                        if (!this.addError('Missing or invalid tag name')) {
+                            return false;
+                        }
+                    }
+                } else {
+                    name = this._tag.name;
+                    if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
+                        // extract the default value if there is one
+                        // example: @param {string} [somebody=John Doe] description
+                        assign = name.substring(1, name.length - 1).split('=');
+                        if (assign.length > 1) {
+                            this._tag['default'] = assign.slice(1).join('=');
+                        }
+                        this._tag.name = assign[0];
+
+                        // convert to an optional type
+                        if (this._tag.type && this._tag.type.type !== 'OptionalType') {
+                            this._tag.type = {
+                                type: 'OptionalType',
+                                expression: this._tag.type
+                            };
+                        }
+                    }
+                }
+            }
+
+
+            return true;
+        };
+
+        TagParser.prototype.parseDescription = function parseDescription() {
+            var description = sliceSource(source, index, this._last).trim();
+            if (description) {
+                if ((/^-\s+/).test(description)) {
+                    description = description.substring(2);
+                }
+                this._tag.description = description;
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseCaption = function parseDescription() {
+            var description = sliceSource(source, index, this._last).trim();
+            var captionStartTag = '<caption>';
+            var captionEndTag = '</caption>';
+            var captionStart = description.indexOf(captionStartTag);
+            var captionEnd = description.indexOf(captionEndTag);
+            if (captionStart >= 0 && captionEnd >= 0) {
+                this._tag.caption = description.substring(
+                    captionStart + captionStartTag.length, captionEnd).trim();
+                this._tag.description = description.substring(captionEnd + captionEndTag.length).trim();
+            } else {
+                this._tag.description = description;
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseKind = function parseKind() {
+            var kind, kinds;
+            kinds = {
+                'class': true,
+                'constant': true,
+                'event': true,
+                'external': true,
+                'file': true,
+                'function': true,
+                'member': true,
+                'mixin': true,
+                'module': true,
+                'namespace': true,
+                'typedef': true
+            };
+            kind = sliceSource(source, index, this._last).trim();
+            this._tag.kind = kind;
+            if (!hasOwnProperty(kinds, kind)) {
+                if (!this.addError('Invalid kind name \'%0\'', kind)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseAccess = function parseAccess() {
+            var access;
+            access = sliceSource(source, index, this._last).trim();
+            this._tag.access = access;
+            if (access !== 'private' && access !== 'protected' && access !== 'public') {
+                if (!this.addError('Invalid access name \'%0\'', access)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseThis = function parseThis() {
+            // this name may be a name expression (e.g. {foo.bar}),
+            // an union (e.g. {foo.bar|foo.baz}) or a name path (e.g. foo.bar)
+            var value = sliceSource(source, index, this._last).trim();
+            if (value && value.charAt(0) === '{') {
+                var gotType = this.parseType();
+                if (gotType && this._tag.type.type === 'NameExpression' || this._tag.type.type === 'UnionType') {
+                    this._tag.name = this._tag.type.name;
+                    return true;
+                } else {
+                    return this.addError('Invalid name for this');
+                }
+            } else {
+                return this.parseNamePath();
+            }
+        };
+
+        TagParser.prototype.parseVariation = function parseVariation() {
+            var variation, text;
+            text = sliceSource(source, index, this._last).trim();
+            variation = parseFloat(text, 10);
+            this._tag.variation = variation;
+            if (isNaN(variation)) {
+                if (!this.addError('Invalid variation \'%0\'', text)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.ensureEnd = function () {
+            var shouldBeEmpty = sliceSource(source, index, this._last).trim();
+            if (shouldBeEmpty) {
+                if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.epilogue = function epilogue() {
+            var description;
+
+            description = this._tag.description;
+            // un-fix potentially sloppy declaration
+            if (isAllowedOptional(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
+                this._tag.type = this._extra.name;
+                if (!this._tag.name) {
+                    this._tag.name = undefined;
+                }
+
+                if (!sloppy) {
+                    if (!this.addError('Missing or invalid tag name')) {
+                        return false;
+                    }
+                }
+            }
+
+            return true;
+        };
+
+        Rules = {
+            // http://usejsdoc.org/tags-access.html
+            'access': ['parseAccess'],
+            // http://usejsdoc.org/tags-alias.html
+            'alias': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-augments.html
+            'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-constructor.html
+            'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-constructor.html
+            'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-extends.html
+            'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-example.html
+            'example': ['parseCaption'],
+            // http://usejsdoc.org/tags-deprecated.html
+            'deprecated': ['parseDescription'],
+            // http://usejsdoc.org/tags-global.html
+            'global': ['ensureEnd'],
+            // http://usejsdoc.org/tags-inner.html
+            'inner': ['ensureEnd'],
+            // http://usejsdoc.org/tags-instance.html
+            'instance': ['ensureEnd'],
+            // http://usejsdoc.org/tags-kind.html
+            'kind': ['parseKind'],
+            // http://usejsdoc.org/tags-mixes.html
+            'mixes': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-mixin.html
+            'mixin': ['parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-member.html
+            'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-method.html
+            'method': ['parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-module.html
+            'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-method.html
+            'func': ['parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-method.html
+            'function': ['parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-member.html
+            'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-name.html
+            'name': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-namespace.html
+            'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-private.html
+            'private': ['parseType', 'parseDescription'],
+            // http://usejsdoc.org/tags-protected.html
+            'protected': ['parseType', 'parseDescription'],
+            // http://usejsdoc.org/tags-public.html
+            'public': ['parseType', 'parseDescription'],
+            // http://usejsdoc.org/tags-readonly.html
+            'readonly': ['ensureEnd'],
+            // http://usejsdoc.org/tags-requires.html
+            'requires': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-since.html
+            'since': ['parseDescription'],
+            // http://usejsdoc.org/tags-static.html
+            'static': ['ensureEnd'],
+            // http://usejsdoc.org/tags-summary.html
+            'summary': ['parseDescription'],
+            // http://usejsdoc.org/tags-this.html
+            'this': ['parseThis', 'ensureEnd'],
+            // http://usejsdoc.org/tags-todo.html
+            'todo': ['parseDescription'],
+            // http://usejsdoc.org/tags-typedef.html
+            'typedef': ['parseType', 'parseNamePathOptional'],
+            // http://usejsdoc.org/tags-variation.html
+            'variation': ['parseVariation'],
+            // http://usejsdoc.org/tags-version.html
+            'version': ['parseDescription']
+        };
+
+        TagParser.prototype.parse = function parse() {
+            var i, iz, sequences, method;
+
+
+            // empty title
+            if (!this._title) {
+                if (!this.addError('Missing or invalid title')) {
+                    return null;
+                }
+            }
+
+            // Seek to content last index.
+            this._last = seekContent(this._title);
+
+            if (this._options.range) {
+                this._tag.range = [this._first, source.slice(0, this._last).replace(/\s*$/, '').length].map(convertIndex);
+            }
+
+            if (hasOwnProperty(Rules, this._title)) {
+                sequences = Rules[this._title];
+            } else {
+                // default sequences
+                sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
+            }
+
+            for (i = 0, iz = sequences.length; i < iz; ++i) {
+                method = sequences[i];
+                if (!this[method]()) {
+                    return null;
+                }
+            }
+
+            return this._tag;
+        };
+
+        function parseTag(options) {
+            var title, parser, tag;
+
+            // skip to tag
+            if (!skipToTag()) {
+                return null;
+            }
+
+            // scan title
+            title = scanTitle();
+
+            // construct tag parser
+            parser = new TagParser(options, title);
+            tag = parser.parse();
+
+            // Seek global index to end of this tag.
+            while (index < parser._last) {
+                advance();
+            }
+
+            return tag;
+        }
+
+        //
+        // Parse JSDoc
+        //
+
+        function scanJSDocDescription(preserveWhitespace) {
+            var description = '', ch, atAllowed;
+
+            atAllowed = true;
+            while (index < length) {
+                ch = source.charCodeAt(index);
+
+                if (atAllowed && ch === 0x40  /* '@' */) {
+                    break;
+                }
+
+                if (esutils.code.isLineTerminator(ch)) {
+                    atAllowed = true;
+                } else if (atAllowed && !esutils.code.isWhiteSpace(ch)) {
+                    atAllowed = false;
+                }
+
+                description += advance();
+            }
+
+            return preserveWhitespace ? description : description.trim();
+        }
+
+        function parse(comment, options) {
+            var tags = [], tag, description, interestingTags, i, iz;
+
+            if (options === undefined) {
+                options = {};
+            }
+
+            if (typeof options.unwrap === 'boolean' && options.unwrap) {
+                source = unwrapComment(comment);
+            } else {
+                source = comment;
+            }
+
+            originalSource = comment;
+
+            // array of relevant tags
+            if (options.tags) {
+                if (Array.isArray(options.tags)) {
+                    interestingTags = { };
+                    for (i = 0, iz = options.tags.length; i < iz; i++) {
+                        if (typeof options.tags[i] === 'string') {
+                            interestingTags[options.tags[i]] = true;
+                        } else {
+                            utility.throwError('Invalid "tags" parameter: ' + options.tags);
+                        }
+                    }
+                } else {
+                    utility.throwError('Invalid "tags" parameter: ' + options.tags);
+                }
+            }
+
+            length = source.length;
+            index = 0;
+            lineNumber = 0;
+            recoverable = options.recoverable;
+            sloppy = options.sloppy;
+            strict = options.strict;
+
+            description = scanJSDocDescription(options.preserveWhitespace);
+
+            while (true) {
+                tag = parseTag(options);
+                if (!tag) {
+                    break;
+                }
+                if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
+                    tags.push(tag);
+                }
+            }
+
+            return {
+                description: description,
+                tags: tags
+            };
+        }
+        exports.parse = parse;
+    }(jsdoc = {}));
+
+    exports.version = utility.VERSION;
+    exports.parse = jsdoc.parse;
+    exports.parseType = typed.parseType;
+    exports.parseParamType = typed.parseParamType;
+    exports.unwrapComment = unwrapComment;
+    exports.Syntax = shallowCopy(typed.Syntax);
+    exports.Error = utility.DoctrineError;
+    exports.type = {
+        Syntax: exports.Syntax,
+        parseType: typed.parseType,
+        parseParamType: typed.parseParamType,
+        stringify: typed.stringify
+    };
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/typed.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/typed.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/typed.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1305 @@
+/*
+ * @fileoverview Type expression parser.
+ * @author Yusuke Suzuki <utatane.tea@gmail.com>
+ * @author Dan Tao <daniel.tao@gmail.com>
+ * @author Andrew Eisenberg <andrew@eisenberg.as>
+ */
+
+// "typed", the Type Expression Parser for doctrine.
+
+(function () {
+    'use strict';
+
+    var Syntax,
+        Token,
+        source,
+        length,
+        index,
+        previous,
+        token,
+        value,
+        esutils,
+        utility,
+        rangeOffset,
+        addRange;
+
+    esutils = require('esutils');
+    utility = require('./utility');
+
+    Syntax = {
+        NullableLiteral: 'NullableLiteral',
+        AllLiteral: 'AllLiteral',
+        NullLiteral: 'NullLiteral',
+        UndefinedLiteral: 'UndefinedLiteral',
+        VoidLiteral: 'VoidLiteral',
+        UnionType: 'UnionType',
+        ArrayType: 'ArrayType',
+        RecordType: 'RecordType',
+        FieldType: 'FieldType',
+        FunctionType: 'FunctionType',
+        ParameterType: 'ParameterType',
+        RestType: 'RestType',
+        NonNullableType: 'NonNullableType',
+        OptionalType: 'OptionalType',
+        NullableType: 'NullableType',
+        NameExpression: 'NameExpression',
+        TypeApplication: 'TypeApplication',
+        StringLiteralType: 'StringLiteralType',
+        NumericLiteralType: 'NumericLiteralType',
+        BooleanLiteralType: 'BooleanLiteralType'
+    };
+
+    Token = {
+        ILLEGAL: 0,    // ILLEGAL
+        DOT_LT: 1,     // .<
+        REST: 2,       // ...
+        LT: 3,         // <
+        GT: 4,         // >
+        LPAREN: 5,     // (
+        RPAREN: 6,     // )
+        LBRACE: 7,     // {
+        RBRACE: 8,     // }
+        LBRACK: 9,    // [
+        RBRACK: 10,    // ]
+        COMMA: 11,     // ,
+        COLON: 12,     // :
+        STAR: 13,      // *
+        PIPE: 14,      // |
+        QUESTION: 15,  // ?
+        BANG: 16,      // !
+        EQUAL: 17,     // =
+        NAME: 18,      // name token
+        STRING: 19,    // string
+        NUMBER: 20,    // number
+        EOF: 21
+    };
+
+    function isTypeName(ch) {
+        return '><(){}[],:*|?!='.indexOf(String.fromCharCode(ch)) === -1 && !esutils.code.isWhiteSpace(ch) && !esutils.code.isLineTerminator(ch);
+    }
+
+    function Context(previous, index, token, value) {
+        this._previous = previous;
+        this._index = index;
+        this._token = token;
+        this._value = value;
+    }
+
+    Context.prototype.restore = function () {
+        previous = this._previous;
+        index = this._index;
+        token = this._token;
+        value = this._value;
+    };
+
+    Context.save = function () {
+        return new Context(previous, index, token, value);
+    };
+
+    function maybeAddRange(node, range) {
+        if (addRange) {
+            node.range = [range[0] + rangeOffset, range[1] + rangeOffset];
+        }
+        return node;
+    }
+
+    function advance() {
+        var ch = source.charAt(index);
+        index += 1;
+        return ch;
+    }
+
+    function scanHexEscape(prefix) {
+        var i, len, ch, code = 0;
+
+        len = (prefix === 'u') ? 4 : 2;
+        for (i = 0; i < len; ++i) {
+            if (index < length && esutils.code.isHexDigit(source.charCodeAt(index))) {
+                ch = advance();
+                code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
+            } else {
+                return '';
+            }
+        }
+        return String.fromCharCode(code);
+    }
+
+    function scanString() {
+        var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
+        quote = source.charAt(index);
+        ++index;
+
+        while (index < length) {
+            ch = advance();
+
+            if (ch === quote) {
+                quote = '';
+                break;
+            } else if (ch === '\\') {
+                ch = advance();
+                if (!esutils.code.isLineTerminator(ch.charCodeAt(0))) {
+                    switch (ch) {
+                    case 'n':
+                        str += '\n';
+                        break;
+                    case 'r':
+                        str += '\r';
+                        break;
+                    case 't':
+                        str += '\t';
+                        break;
+                    case 'u':
+                    case 'x':
+                        restore = index;
+                        unescaped = scanHexEscape(ch);
+                        if (unescaped) {
+                            str += unescaped;
+                        } else {
+                            index = restore;
+                            str += ch;
+                        }
+                        break;
+                    case 'b':
+                        str += '\b';
+                        break;
+                    case 'f':
+                        str += '\f';
+                        break;
+                    case 'v':
+                        str += '\v';
+                        break;
+
+                    default:
+                        if (esutils.code.isOctalDigit(ch.charCodeAt(0))) {
+                            code = '01234567'.indexOf(ch);
+
+                            // \0 is not octal escape sequence
+                            // Deprecating unused code. TODO review removal
+                            //if (code !== 0) {
+                            //    octal = true;
+                            //}
+
+                            if (index < length && esutils.code.isOctalDigit(source.charCodeAt(index))) {
+                                //TODO Review Removal octal = true;
+                                code = code * 8 + '01234567'.indexOf(advance());
+
+                                // 3 digits are only allowed when string starts
+                                // with 0, 1, 2, 3
+                                if ('0123'.indexOf(ch) >= 0 &&
+                                        index < length &&
+                                        esutils.code.isOctalDigit(source.charCodeAt(index))) {
+                                    code = code * 8 + '01234567'.indexOf(advance());
+                                }
+                            }
+                            str += String.fromCharCode(code);
+                        } else {
+                            str += ch;
+                        }
+                        break;
+                    }
+                } else {
+                    if (ch ===  '\r' && source.charCodeAt(index) === 0x0A  /* '\n' */) {
+                        ++index;
+                    }
+                }
+            } else if (esutils.code.isLineTerminator(ch.charCodeAt(0))) {
+                break;
+            } else {
+                str += ch;
+            }
+        }
+
+        if (quote !== '') {
+            utility.throwError('unexpected quote');
+        }
+
+        value = str;
+        return Token.STRING;
+    }
+
+    function scanNumber() {
+        var number, ch;
+
+        number = '';
+        ch = source.charCodeAt(index);
+
+        if (ch !== 0x2E  /* '.' */) {
+            number = advance();
+            ch = source.charCodeAt(index);
+
+            if (number === '0') {
+                if (ch === 0x78  /* 'x' */ || ch === 0x58  /* 'X' */) {
+                    number += advance();
+                    while (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (!esutils.code.isHexDigit(ch)) {
+                            break;
+                        }
+                        number += advance();
+                    }
+
+                    if (number.length <= 2) {
+                        // only 0x
+                        utility.throwError('unexpected token');
+                    }
+
+                    if (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (esutils.code.isIdentifierStartES5(ch)) {
+                            utility.throwError('unexpected token');
+                        }
+                    }
+                    value = parseInt(number, 16);
+                    return Token.NUMBER;
+                }
+
+                if (esutils.code.isOctalDigit(ch)) {
+                    number += advance();
+                    while (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (!esutils.code.isOctalDigit(ch)) {
+                            break;
+                        }
+                        number += advance();
+                    }
+
+                    if (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (esutils.code.isIdentifierStartES5(ch) || esutils.code.isDecimalDigit(ch)) {
+                            utility.throwError('unexpected token');
+                        }
+                    }
+                    value = parseInt(number, 8);
+                    return Token.NUMBER;
+                }
+
+                if (esutils.code.isDecimalDigit(ch)) {
+                    utility.throwError('unexpected token');
+                }
+            }
+
+            while (index < length) {
+                ch = source.charCodeAt(index);
+                if (!esutils.code.isDecimalDigit(ch)) {
+                    break;
+                }
+                number += advance();
+            }
+        }
+
+        if (ch === 0x2E  /* '.' */) {
+            number += advance();
+            while (index < length) {
+                ch = source.charCodeAt(index);
+                if (!esutils.code.isDecimalDigit(ch)) {
+                    break;
+                }
+                number += advance();
+            }
+        }
+
+        if (ch === 0x65  /* 'e' */ || ch === 0x45  /* 'E' */) {
+            number += advance();
+
+            ch = source.charCodeAt(index);
+            if (ch === 0x2B  /* '+' */ || ch === 0x2D  /* '-' */) {
+                number += advance();
+            }
+
+            ch = source.charCodeAt(index);
+            if (esutils.code.isDecimalDigit(ch)) {
+                number += advance();
+                while (index < length) {
+                    ch = source.charCodeAt(index);
+                    if (!esutils.code.isDecimalDigit(ch)) {
+                        break;
+                    }
+                    number += advance();
+                }
+            } else {
+                utility.throwError('unexpected token');
+            }
+        }
+
+        if (index < length) {
+            ch = source.charCodeAt(index);
+            if (esutils.code.isIdentifierStartES5(ch)) {
+                utility.throwError('unexpected token');
+            }
+        }
+
+        value = parseFloat(number);
+        return Token.NUMBER;
+    }
+
+
+    function scanTypeName() {
+        var ch, ch2;
+
+        value = advance();
+        while (index < length && isTypeName(source.charCodeAt(index))) {
+            ch = source.charCodeAt(index);
+            if (ch === 0x2E  /* '.' */) {
+                if ((index + 1) >= length) {
+                    return Token.ILLEGAL;
+                }
+                ch2 = source.charCodeAt(index + 1);
+                if (ch2 === 0x3C  /* '<' */) {
+                    break;
+                }
+            }
+            value += advance();
+        }
+        return Token.NAME;
+    }
+
+    function next() {
+        var ch;
+
+        previous = index;
+
+        while (index < length && esutils.code.isWhiteSpace(source.charCodeAt(index))) {
+            advance();
+        }
+        if (index >= length) {
+            token = Token.EOF;
+            return token;
+        }
+
+        ch = source.charCodeAt(index);
+        switch (ch) {
+        case 0x27:  /* ''' */
+        case 0x22:  /* '"' */
+            token = scanString();
+            return token;
+
+        case 0x3A:  /* ':' */
+            advance();
+            token = Token.COLON;
+            return token;
+
+        case 0x2C:  /* ',' */
+            advance();
+            token = Token.COMMA;
+            return token;
+
+        case 0x28:  /* '(' */
+            advance();
+            token = Token.LPAREN;
+            return token;
+
+        case 0x29:  /* ')' */
+            advance();
+            token = Token.RPAREN;
+            return token;
+
+        case 0x5B:  /* '[' */
+            advance();
+            token = Token.LBRACK;
+            return token;
+
+        case 0x5D:  /* ']' */
+            advance();
+            token = Token.RBRACK;
+            return token;
+
+        case 0x7B:  /* '{' */
+            advance();
+            token = Token.LBRACE;
+            return token;
+
+        case 0x7D:  /* '}' */
+            advance();
+            token = Token.RBRACE;
+            return token;
+
+        case 0x2E:  /* '.' */
+            if (index + 1 < length) {
+                ch = source.charCodeAt(index + 1);
+                if (ch === 0x3C  /* '<' */) {
+                    advance();  // '.'
+                    advance();  // '<'
+                    token = Token.DOT_LT;
+                    return token;
+                }
+
+                if (ch === 0x2E  /* '.' */ && index + 2 < length && source.charCodeAt(index + 2) === 0x2E  /* '.' */) {
+                    advance();  // '.'
+                    advance();  // '.'
+                    advance();  // '.'
+                    token = Token.REST;
+                    return token;
+                }
+
+                if (esutils.code.isDecimalDigit(ch)) {
+                    token = scanNumber();
+                    return token;
+                }
+            }
+            token = Token.ILLEGAL;
+            return token;
+
+        case 0x3C:  /* '<' */
+            advance();
+            token = Token.LT;
+            return token;
+
+        case 0x3E:  /* '>' */
+            advance();
+            token = Token.GT;
+            return token;
+
+        case 0x2A:  /* '*' */
+            advance();
+            token = Token.STAR;
+            return token;
+
+        case 0x7C:  /* '|' */
+            advance();
+            token = Token.PIPE;
+            return token;
+
+        case 0x3F:  /* '?' */
+            advance();
+            token = Token.QUESTION;
+            return token;
+
+        case 0x21:  /* '!' */
+            advance();
+            token = Token.BANG;
+            return token;
+
+        case 0x3D:  /* '=' */
+            advance();
+            token = Token.EQUAL;
+            return token;
+
+        case 0x2D: /* '-' */
+            token = scanNumber();
+            return token;
+
+        default:
+            if (esutils.code.isDecimalDigit(ch)) {
+                token = scanNumber();
+                return token;
+            }
+
+            // type string permits following case,
+            //
+            // namespace.module.MyClass
+            //
+            // this reduced 1 token TK_NAME
+            utility.assert(isTypeName(ch));
+            token = scanTypeName();
+            return token;
+        }
+    }
+
+    function consume(target, text) {
+        utility.assert(token === target, text || 'consumed token not matched');
+        next();
+    }
+
+    function expect(target, message) {
+        if (token !== target) {
+            utility.throwError(message || 'unexpected token');
+        }
+        next();
+    }
+
+    // UnionType := '(' TypeUnionList ')'
+    //
+    // TypeUnionList :=
+    //     <<empty>>
+    //   | NonemptyTypeUnionList
+    //
+    // NonemptyTypeUnionList :=
+    //     TypeExpression
+    //   | TypeExpression '|' NonemptyTypeUnionList
+    function parseUnionType() {
+        var elements, startIndex = index - 1;
+        consume(Token.LPAREN, 'UnionType should start with (');
+        elements = [];
+        if (token !== Token.RPAREN) {
+            while (true) {
+                elements.push(parseTypeExpression());
+                if (token === Token.RPAREN) {
+                    break;
+                }
+                expect(Token.PIPE);
+            }
+        }
+        consume(Token.RPAREN, 'UnionType should end with )');
+        return maybeAddRange({
+            type: Syntax.UnionType,
+            elements: elements
+        }, [startIndex, previous]);
+    }
+
+    // ArrayType := '[' ElementTypeList ']'
+    //
+    // ElementTypeList :=
+    //     <<empty>>
+    //  | TypeExpression
+    //  | '...' TypeExpression
+    //  | TypeExpression ',' ElementTypeList
+    function parseArrayType() {
+        var elements, startIndex = index - 1, restStartIndex;
+        consume(Token.LBRACK, 'ArrayType should start with [');
+        elements = [];
+        while (token !== Token.RBRACK) {
+            if (token === Token.REST) {
+                restStartIndex = index - 3;
+                consume(Token.REST);
+                elements.push(maybeAddRange({
+                    type: Syntax.RestType,
+                    expression: parseTypeExpression()
+                }, [restStartIndex, previous]));
+                break;
+            } else {
+                elements.push(parseTypeExpression());
+            }
+            if (token !== Token.RBRACK) {
+                expect(Token.COMMA);
+            }
+        }
+        expect(Token.RBRACK);
+        return maybeAddRange({
+            type: Syntax.ArrayType,
+            elements: elements
+        }, [startIndex, previous]);
+    }
+
+    function parseFieldName() {
+        var v = value;
+        if (token === Token.NAME || token === Token.STRING) {
+            next();
+            return v;
+        }
+
+        if (token === Token.NUMBER) {
+            consume(Token.NUMBER);
+            return String(v);
+        }
+
+        utility.throwError('unexpected token');
+    }
+
+    // FieldType :=
+    //     FieldName
+    //   | FieldName ':' TypeExpression
+    //
+    // FieldName :=
+    //     NameExpression
+    //   | StringLiteral
+    //   | NumberLiteral
+    //   | ReservedIdentifier
+    function parseFieldType() {
+        var key, rangeStart = previous;
+
+        key = parseFieldName();
+        if (token === Token.COLON) {
+            consume(Token.COLON);
+            return maybeAddRange({
+                type: Syntax.FieldType,
+                key: key,
+                value: parseTypeExpression()
+            }, [rangeStart, previous]);
+        }
+        return maybeAddRange({
+            type: Syntax.FieldType,
+            key: key,
+            value: null
+        }, [rangeStart, previous]);
+    }
+
+    // RecordType := '{' FieldTypeList '}'
+    //
+    // FieldTypeList :=
+    //     <<empty>>
+    //   | FieldType
+    //   | FieldType ',' FieldTypeList
+    function parseRecordType() {
+        var fields, rangeStart = index - 1, rangeEnd;
+
+        consume(Token.LBRACE, 'RecordType should start with {');
+        fields = [];
+        if (token === Token.COMMA) {
+            consume(Token.COMMA);
+        } else {
+            while (token !== Token.RBRACE) {
+                fields.push(parseFieldType());
+                if (token !== Token.RBRACE) {
+                    expect(Token.COMMA);
+                }
+            }
+        }
+        rangeEnd = index;
+        expect(Token.RBRACE);
+        return maybeAddRange({
+            type: Syntax.RecordType,
+            fields: fields
+        }, [rangeStart, rangeEnd]);
+    }
+
+    // NameExpression :=
+    //    Identifier
+    //  | TagIdentifier ':' Identifier
+    //
+    // Tag identifier is one of "module", "external" or "event"
+    // Identifier is the same as Token.NAME, including any dots, something like
+    // namespace.module.MyClass
+    function parseNameExpression() {
+        var name = value, rangeStart = index - name.length;
+        expect(Token.NAME);
+
+        if (token === Token.COLON && (
+                name === 'module' ||
+                name === 'external' ||
+                name === 'event')) {
+            consume(Token.COLON);
+            name += ':' + value;
+            expect(Token.NAME);
+        }
+
+        return maybeAddRange({
+            type: Syntax.NameExpression,
+            name: name
+        }, [rangeStart, previous]);
+    }
+
+    // TypeExpressionList :=
+    //     TopLevelTypeExpression
+    //   | TopLevelTypeExpression ',' TypeExpressionList
+    function parseTypeExpressionList() {
+        var elements = [];
+
+        elements.push(parseTop());
+        while (token === Token.COMMA) {
+            consume(Token.COMMA);
+            elements.push(parseTop());
+        }
+        return elements;
+    }
+
+    // TypeName :=
+    //     NameExpression
+    //   | NameExpression TypeApplication
+    //
+    // TypeApplication :=
+    //     '.<' TypeExpressionList '>'
+    //   | '<' TypeExpressionList '>'   // this is extension of doctrine
+    function parseTypeName() {
+        var expr, applications, startIndex = index - value.length;
+
+        expr = parseNameExpression();
+        if (token === Token.DOT_LT || token === Token.LT) {
+            next();
+            applications = parseTypeExpressionList();
+            expect(Token.GT);
+            return maybeAddRange({
+                type: Syntax.TypeApplication,
+                expression: expr,
+                applications: applications
+            }, [startIndex, previous]);
+        }
+        return expr;
+    }
+
+    // ResultType :=
+    //     <<empty>>
+    //   | ':' void
+    //   | ':' TypeExpression
+    //
+    // BNF is above
+    // but, we remove <<empty>> pattern, so token is always TypeToken::COLON
+    function parseResultType() {
+        consume(Token.COLON, 'ResultType should start with :');
+        if (token === Token.NAME && value === 'void') {
+            consume(Token.NAME);
+            return {
+                type: Syntax.VoidLiteral
+            };
+        }
+        return parseTypeExpression();
+    }
+
+    // ParametersType :=
+    //     RestParameterType
+    //   | NonRestParametersType
+    //   | NonRestParametersType ',' RestParameterType
+    //
+    // RestParameterType :=
+    //     '...'
+    //     '...' Identifier
+    //
+    // NonRestParametersType :=
+    //     ParameterType ',' NonRestParametersType
+    //   | ParameterType
+    //   | OptionalParametersType
+    //
+    // OptionalParametersType :=
+    //     OptionalParameterType
+    //   | OptionalParameterType, OptionalParametersType
+    //
+    // OptionalParameterType := ParameterType=
+    //
+    // ParameterType := TypeExpression | Identifier ':' TypeExpression
+    //
+    // Identifier is "new" or "this"
+    function parseParametersType() {
+        var params = [], optionalSequence = false, expr, rest = false, startIndex, restStartIndex = index - 3, nameStartIndex;
+
+        while (token !== Token.RPAREN) {
+            if (token === Token.REST) {
+                // RestParameterType
+                consume(Token.REST);
+                rest = true;
+            }
+
+            startIndex = previous;
+
+            expr = parseTypeExpression();
+            if (expr.type === Syntax.NameExpression && token === Token.COLON) {
+                nameStartIndex = previous - expr.name.length;
+                // Identifier ':' TypeExpression
+                consume(Token.COLON);
+                expr = maybeAddRange({
+                    type: Syntax.ParameterType,
+                    name: expr.name,
+                    expression: parseTypeExpression()
+                }, [nameStartIndex, previous]);
+            }
+            if (token === Token.EQUAL) {
+                consume(Token.EQUAL);
+                expr = maybeAddRange({
+                    type: Syntax.OptionalType,
+                    expression: expr
+                }, [startIndex, previous]);
+                optionalSequence = true;
+            } else {
+                if (optionalSequence) {
+                    utility.throwError('unexpected token');
+                }
+            }
+            if (rest) {
+                expr = maybeAddRange({
+                    type: Syntax.RestType,
+                    expression: expr
+                }, [restStartIndex, previous]);
+            }
+            params.push(expr);
+            if (token !== Token.RPAREN) {
+                expect(Token.COMMA);
+            }
+        }
+        return params;
+    }
+
+    // FunctionType := 'function' FunctionSignatureType
+    //
+    // FunctionSignatureType :=
+    //   | TypeParameters '(' ')' ResultType
+    //   | TypeParameters '(' ParametersType ')' ResultType
+    //   | TypeParameters '(' 'this' ':' TypeName ')' ResultType
+    //   | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
+    function parseFunctionType() {
+        var isNew, thisBinding, params, result, fnType, startIndex = index - value.length;
+        utility.assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
+        consume(Token.NAME);
+
+        // Google Closure Compiler is not implementing TypeParameters.
+        // So we do not. if we don't get '(', we see it as error.
+        expect(Token.LPAREN);
+
+        isNew = false;
+        params = [];
+        thisBinding = null;
+        if (token !== Token.RPAREN) {
+            // ParametersType or 'this'
+            if (token === Token.NAME &&
+                    (value === 'this' || value === 'new')) {
+                // 'this' or 'new'
+                // 'new' is Closure Compiler extension
+                isNew = value === 'new';
+                consume(Token.NAME);
+                expect(Token.COLON);
+                thisBinding = parseTypeName();
+                if (token === Token.COMMA) {
+                    consume(Token.COMMA);
+                    params = parseParametersType();
+                }
+            } else {
+                params = parseParametersType();
+            }
+        }
+
+        expect(Token.RPAREN);
+
+        result = null;
+        if (token === Token.COLON) {
+            result = parseResultType();
+        }
+
+        fnType = maybeAddRange({
+            type: Syntax.FunctionType,
+            params: params,
+            result: result
+        }, [startIndex, previous]);
+        if (thisBinding) {
+            // avoid adding null 'new' and 'this' properties
+            fnType['this'] = thisBinding;
+            if (isNew) {
+                fnType['new'] = true;
+            }
+        }
+        return fnType;
+    }
+
+    // BasicTypeExpression :=
+    //     '*'
+    //   | 'null'
+    //   | 'undefined'
+    //   | TypeName
+    //   | FunctionType
+    //   | UnionType
+    //   | RecordType
+    //   | ArrayType
+    function parseBasicTypeExpression() {
+        var context, startIndex;
+        switch (token) {
+        case Token.STAR:
+            consume(Token.STAR);
+            return maybeAddRange({
+                type: Syntax.AllLiteral
+            }, [previous - 1, previous]);
+
+        case Token.LPAREN:
+            return parseUnionType();
+
+        case Token.LBRACK:
+            return parseArrayType();
+
+        case Token.LBRACE:
+            return parseRecordType();
+
+        case Token.NAME:
+            startIndex = index - value.length;
+
+            if (value === 'null') {
+                consume(Token.NAME);
+                return maybeAddRange({
+                    type: Syntax.NullLiteral
+                }, [startIndex, previous]);
+            }
+
+            if (value === 'undefined') {
+                consume(Token.NAME);
+                return maybeAddRange({
+                    type: Syntax.UndefinedLiteral
+                }, [startIndex, previous]);
+            }
+
+            if (value === 'true' || value === 'false') {
+                consume(Token.NAME);
+                return maybeAddRange({
+                    type: Syntax.BooleanLiteralType,
+                    value: value === 'true'
+                }, [startIndex, previous]);
+            }
+
+            context = Context.save();
+            if (value === 'function') {
+                try {
+                    return parseFunctionType();
+                } catch (e) {
+                    context.restore();
+                }
+            }
+
+            return parseTypeName();
+
+        case Token.STRING:
+            next();
+            return maybeAddRange({
+                type: Syntax.StringLiteralType,
+                value: value
+            }, [previous - value.length - 2, previous]);
+
+        case Token.NUMBER:
+            next();
+            return maybeAddRange({
+                type: Syntax.NumericLiteralType,
+                value: value
+            }, [previous - String(value).length, previous]);
+
+        default:
+            utility.throwError('unexpected token');
+        }
+    }
+
+    // TypeExpression :=
+    //     BasicTypeExpression
+    //   | '?' BasicTypeExpression
+    //   | '!' BasicTypeExpression
+    //   | BasicTypeExpression '?'
+    //   | BasicTypeExpression '!'
+    //   | '?'
+    //   | BasicTypeExpression '[]'
+    function parseTypeExpression() {
+        var expr, rangeStart;
+
+        if (token === Token.QUESTION) {
+            rangeStart = index - 1;
+            consume(Token.QUESTION);
+            if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
+                    token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
+                    token === Token.RBRACK || token === Token.GT) {
+                return maybeAddRange({
+                    type: Syntax.NullableLiteral
+                }, [rangeStart, previous]);
+            }
+            return maybeAddRange({
+                type: Syntax.NullableType,
+                expression: parseBasicTypeExpression(),
+                prefix: true
+            }, [rangeStart, previous]);
+        } else if (token === Token.BANG) {
+            rangeStart = index - 1;
+            consume(Token.BANG);
+            return maybeAddRange({
+                type: Syntax.NonNullableType,
+                expression: parseBasicTypeExpression(),
+                prefix: true
+            }, [rangeStart, previous]);
+        } else {
+            rangeStart = previous;
+        }
+
+        expr = parseBasicTypeExpression();
+        if (token === Token.BANG) {
+            consume(Token.BANG);
+            return maybeAddRange({
+                type: Syntax.NonNullableType,
+                expression: expr,
+                prefix: false
+            }, [rangeStart, previous]);
+        }
+
+        if (token === Token.QUESTION) {
+            consume(Token.QUESTION);
+            return maybeAddRange({
+                type: Syntax.NullableType,
+                expression: expr,
+                prefix: false
+            }, [rangeStart, previous]);
+        }
+
+        if (token === Token.LBRACK) {
+            consume(Token.LBRACK);
+            expect(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
+            return maybeAddRange({
+                type: Syntax.TypeApplication,
+                expression: maybeAddRange({
+                    type: Syntax.NameExpression,
+                    name: 'Array'
+                }, [rangeStart, previous]),
+                applications: [expr]
+            }, [rangeStart, previous]);
+        }
+
+        return expr;
+    }
+
+    // TopLevelTypeExpression :=
+    //      TypeExpression
+    //    | TypeUnionList
+    //
+    // This rule is Google Closure Compiler extension, not ES4
+    // like,
+    //   { number | string }
+    // If strict to ES4, we should write it as
+    //   { (number|string) }
+    function parseTop() {
+        var expr, elements;
+
+        expr = parseTypeExpression();
+        if (token !== Token.PIPE) {
+            return expr;
+        }
+
+        elements = [expr];
+        consume(Token.PIPE);
+        while (true) {
+            elements.push(parseTypeExpression());
+            if (token !== Token.PIPE) {
+                break;
+            }
+            consume(Token.PIPE);
+        }
+
+        return maybeAddRange({
+            type: Syntax.UnionType,
+            elements: elements
+        }, [0, index]);
+    }
+
+    function parseTopParamType() {
+        var expr;
+
+        if (token === Token.REST) {
+            consume(Token.REST);
+            return maybeAddRange({
+                type: Syntax.RestType,
+                expression: parseTop()
+            }, [0, index]);
+        }
+
+        expr = parseTop();
+        if (token === Token.EQUAL) {
+            consume(Token.EQUAL);
+            return maybeAddRange({
+                type: Syntax.OptionalType,
+                expression: expr
+            }, [0, index]);
+        }
+
+        return expr;
+    }
+
+    function parseType(src, opt) {
+        var expr;
+
+        source = src;
+        length = source.length;
+        index = 0;
+        previous = 0;
+        addRange = opt && opt.range;
+        rangeOffset = opt && opt.startIndex || 0;
+
+        next();
+        expr = parseTop();
+
+        if (opt && opt.midstream) {
+            return {
+                expression: expr,
+                index: previous
+            };
+        }
+
+        if (token !== Token.EOF) {
+            utility.throwError('not reach to EOF');
+        }
+
+        return expr;
+    }
+
+    function parseParamType(src, opt) {
+        var expr;
+
+        source = src;
+        length = source.length;
+        index = 0;
+        previous = 0;
+        addRange = opt && opt.range;
+        rangeOffset = opt && opt.startIndex || 0;
+
+        next();
+        expr = parseTopParamType();
+
+        if (opt && opt.midstream) {
+            return {
+                expression: expr,
+                index: previous
+            };
+        }
+
+        if (token !== Token.EOF) {
+            utility.throwError('not reach to EOF');
+        }
+
+        return expr;
+    }
+
+    function stringifyImpl(node, compact, topLevel) {
+        var result, i, iz;
+
+        switch (node.type) {
+        case Syntax.NullableLiteral:
+            result = '?';
+            break;
+
+        case Syntax.AllLiteral:
+            result = '*';
+            break;
+
+        case Syntax.NullLiteral:
+            result = 'null';
+            break;
+
+        case Syntax.UndefinedLiteral:
+            result = 'undefined';
+            break;
+
+        case Syntax.VoidLiteral:
+            result = 'void';
+            break;
+
+        case Syntax.UnionType:
+            if (!topLevel) {
+                result = '(';
+            } else {
+                result = '';
+            }
+
+            for (i = 0, iz = node.elements.length; i < iz; ++i) {
+                result += stringifyImpl(node.elements[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? '|' : ' | ';
+                }
+            }
+
+            if (!topLevel) {
+                result += ')';
+            }
+            break;
+
+        case Syntax.ArrayType:
+            result = '[';
+            for (i = 0, iz = node.elements.length; i < iz; ++i) {
+                result += stringifyImpl(node.elements[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+            result += ']';
+            break;
+
+        case Syntax.RecordType:
+            result = '{';
+            for (i = 0, iz = node.fields.length; i < iz; ++i) {
+                result += stringifyImpl(node.fields[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+            result += '}';
+            break;
+
+        case Syntax.FieldType:
+            if (node.value) {
+                result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
+            } else {
+                result = node.key;
+            }
+            break;
+
+        case Syntax.FunctionType:
+            result = compact ? 'function(' : 'function (';
+
+            if (node['this']) {
+                if (node['new']) {
+                    result += (compact ? 'new:' : 'new: ');
+                } else {
+                    result += (compact ? 'this:' : 'this: ');
+                }
+
+                result += stringifyImpl(node['this'], compact);
+
+                if (node.params.length !== 0) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+
+            for (i = 0, iz = node.params.length; i < iz; ++i) {
+                result += stringifyImpl(node.params[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+
+            result += ')';
+
+            if (node.result) {
+                result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
+            }
+            break;
+
+        case Syntax.ParameterType:
+            result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
+            break;
+
+        case Syntax.RestType:
+            result = '...';
+            if (node.expression) {
+                result += stringifyImpl(node.expression, compact);
+            }
+            break;
+
+        case Syntax.NonNullableType:
+            if (node.prefix) {
+                result = '!' + stringifyImpl(node.expression, compact);
+            } else {
+                result = stringifyImpl(node.expression, compact) + '!';
+            }
+            break;
+
+        case Syntax.OptionalType:
+            result = stringifyImpl(node.expression, compact) + '=';
+            break;
+
+        case Syntax.NullableType:
+            if (node.prefix) {
+                result = '?' + stringifyImpl(node.expression, compact);
+            } else {
+                result = stringifyImpl(node.expression, compact) + '?';
+            }
+            break;
+
+        case Syntax.NameExpression:
+            result = node.name;
+            break;
+
+        case Syntax.TypeApplication:
+            result = stringifyImpl(node.expression, compact) + '.<';
+            for (i = 0, iz = node.applications.length; i < iz; ++i) {
+                result += stringifyImpl(node.applications[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+            result += '>';
+            break;
+
+        case Syntax.StringLiteralType:
+            result = '"' + node.value + '"';
+            break;
+
+        case Syntax.NumericLiteralType:
+            result = String(node.value);
+            break;
+
+        case Syntax.BooleanLiteralType:
+            result = String(node.value);
+            break;
+
+        default:
+            utility.throwError('Unknown type ' + node.type);
+        }
+
+        return result;
+    }
+
+    function stringify(node, options) {
+        if (options == null) {
+            options = {};
+        }
+        return stringifyImpl(node, options.compact, options.topLevel);
+    }
+
+    exports.parseType = parseType;
+    exports.parseParamType = parseParamType;
+    exports.stringify = stringify;
+    exports.Syntax = Syntax;
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/utility.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/utility.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/lib/utility.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,35 @@
+/*
+ * @fileoverview Utilities for Doctrine
+ * @author Yusuke Suzuki <utatane.tea@gmail.com>
+ */
+
+
+(function () {
+    'use strict';
+
+    var VERSION;
+
+    VERSION = require('../package.json').version;
+    exports.VERSION = VERSION;
+
+    function DoctrineError(message) {
+        this.name = 'DoctrineError';
+        this.message = message;
+    }
+    DoctrineError.prototype = (function () {
+        var Middle = function () { };
+        Middle.prototype = Error.prototype;
+        return new Middle();
+    }());
+    DoctrineError.prototype.constructor = DoctrineError;
+    exports.DoctrineError = DoctrineError;
+
+    function throwError(message) {
+        throw new DoctrineError(message);
+    }
+    exports.throwError = throwError;
+
+    exports.assert = require('assert');
+}());
+
+/* vim: set sw=4 ts=4 et tw=80 : */
Index: frontend/node_modules/eslint-plugin-react/node_modules/doctrine/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/doctrine/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/doctrine/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,57 @@
+{
+  "name": "doctrine",
+  "description": "JSDoc parser",
+  "homepage": "https://github.com/eslint/doctrine",
+  "main": "lib/doctrine.js",
+  "version": "2.1.0",
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "directories": {
+    "lib": "./lib"
+  },
+  "files": [
+    "lib"
+  ],
+  "maintainers": [
+    {
+      "name": "Nicholas C. Zakas",
+      "email": "nicholas+npm@nczconsulting.com",
+      "web": "https://www.nczonline.net"
+    },
+    {
+      "name": "Yusuke Suzuki",
+      "email": "utatane.tea@gmail.com",
+      "web": "https://github.com/Constellation"
+    }
+  ],
+  "repository": "eslint/doctrine",
+  "devDependencies": {
+    "coveralls": "^2.11.2",
+    "dateformat": "^1.0.11",
+    "eslint": "^1.10.3",
+    "eslint-release": "^0.10.0",
+    "linefix": "^0.1.1",
+    "mocha": "^3.4.2",
+    "npm-license": "^0.3.1",
+    "nyc": "^10.3.2",
+    "semver": "^5.0.3",
+    "shelljs": "^0.5.3",
+    "shelljs-nodecli": "^0.1.1",
+    "should": "^5.0.1"
+  },
+  "license": "Apache-2.0",
+  "scripts": {
+    "pretest": "npm run lint",
+    "test": "nyc mocha",
+    "coveralls": "nyc report --reporter=text-lcov | coveralls",
+    "lint": "eslint lib/",
+    "release": "eslint-release",
+    "ci-release": "eslint-ci-release",
+    "alpharelease": "eslint-prerelease alpha",
+    "betarelease": "eslint-prerelease beta"
+  },
+  "dependencies": {
+    "esutils": "^2.0.2"
+  }
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/.editorconfig
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/.editorconfig	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/.editorconfig	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,35 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 200
+
+[*.{d.ts,{,m}js}]
+block_comment_start = /*
+block_comment = *
+block_comment_end = */
+
+[*.yml]
+indent_style = space
+indent_size = 1
+
+[{package.json,*.mjs,.nycrc}]
+indent_style = tab
+
+[CHANGELOG.md]
+indent_style = space
+indent_size = 2
+
+[{*.json,Makefile,CONTRIBUTING.md,readme.markdown}]
+max_line_length = unset
+
+[test/{dotdot,resolver,module_dir,multirepo,node_path,pathfilter,precedence}/**/*]
+indent_style = unset
+indent_size = unset
+max_line_length = unset
+insert_final_newline = unset
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/FUNDING.yml
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/FUNDING.yml	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/FUNDING.yml	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: [ljharb]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: npm/resolve
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/INCIDENT_RESPONSE_PROCESS.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/INCIDENT_RESPONSE_PROCESS.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/INCIDENT_RESPONSE_PROCESS.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,119 @@
+# Incident Response Process for **resolve**
+
+## Reporting a Vulnerability
+
+We take the security of **resolve** very seriously. If you believe you’ve found a security vulnerability, please inform us responsibly through coordinated disclosure.
+
+### How to Report
+
+> **Do not** report security vulnerabilities through public GitHub issues, discussions, or social media.
+
+Instead, please use one of these secure channels:
+
+1. **GitHub Security Advisories**
+   Use the **Report a vulnerability** button in the Security tab of the [browserify/resolve repository](https://github.com/browserify/resolve).
+
+2. **Email**
+   Follow the posted [Security Policy](https://github.com/browserify/resolve/security/policy).
+
+### What to Include
+
+**Required Information:**
+- Brief description of the vulnerability type
+- Affected version(s) and components
+- Steps to reproduce the issue
+- Impact assessment (what an attacker could achieve)
+- Confirm the issue is not present in test files (in other words, only via the official entry points in `exports`)
+
+**Helpful Additional Details:**
+- Full paths of affected source files
+- Specific commit or branch where the issue exists
+- Required configuration to reproduce
+- Proof-of-concept code (if available)
+- Suggested mitigation or fix
+
+## Our Response Process
+
+**Timeline Commitments:**
+- **Initial acknowledgment**: Within 24 hours
+- **Detailed response**: Within 3 business days
+- **Status updates**: Every 7 days until resolved
+- **Resolution target**: 90 days for most issues
+
+**What We’ll Do:**
+1. Acknowledge your report and assign a tracking ID
+2. Assess the vulnerability and determine severity
+3. Develop and test a fix
+4. Coordinate disclosure timeline with you
+5. Release a security update and publish an advisory and CVE
+6. Credit you in our security advisory (if desired)
+
+## Disclosure Policy
+
+- **Coordinated disclosure**: We’ll work with you on timing
+- **Typical timeline**: 90 days from report to public disclosure
+- **Early disclosure**: If actively exploited
+- **Delayed disclosure**: For complex issues
+
+## Scope
+
+**In Scope:**
+- **resolve** package (all supported versions)
+- Official examples and documentation
+- Core resolution APIs
+- Dependencies with direct security implications
+
+**Out of Scope:**
+- Third-party wrappers or extensions
+- Bundler-specific integrations
+- Social engineering or physical attacks
+- Theoretical vulnerabilities without practical exploitation
+- Issues in non-production files
+
+## Security Measures
+
+**Our Commitments:**
+- Regular vulnerability scanning via `npm audit`
+- Automated security checks in CI/CD (GitHub Actions)
+- Secure coding practices and mandatory code review
+- Prompt patch releases for critical issues
+
+**User Responsibilities:**
+- Keep **resolve** updated
+- Monitor dependency vulnerabilities
+- Follow secure configuration guidelines for module resolution
+
+## Legal Safe Harbor
+
+**We will NOT:**
+- Initiate legal action
+- Contact law enforcement
+- Suspend or terminate your access
+
+**You must:**
+- Only test against your own installations
+- Not access, modify, or delete user data
+- Not degrade service availability
+- Not publicly disclose before coordinated disclosure
+- Act in good faith
+
+## Recognition
+
+- **Advisory Credits**: Credit in GitHub Security Advisories (unless anonymous)
+
+## Security Updates
+
+**Stay Informed:**
+- Subscribe to npm updates for **resolve**
+- Enable GitHub Security Advisory notifications
+
+**Update Process:**
+- Patch releases (e.g., 1.22.10 → 1.22.11)
+- Out-of-band releases for critical issues
+- Advisories via GitHub Security Advisories
+
+## Contact Information
+
+- **Security reports**: Security tab of [browserify/resolve](https://github.com/browserify/resolve/security)
+- **General inquiries**: GitHub Discussions or Issues
+
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/THREAT_MODEL.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/THREAT_MODEL.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/.github/THREAT_MODEL.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,74 @@
+## Threat Model for resolve (module path resolution library)
+
+### 1. Library Overview
+
+- **Library Name:** resolve
+- **Brief Description:** Implements Node.js `require.resolve()` algorithm for synchronous and asynchronous file path resolution. Used to locate modules and files in Node.js projects.
+- **Key Public APIs/Functions:** `resolve.sync()` / `resolve/sync`, `resolve()` / `resolve/async`
+
+### 2. Define Scope
+
+This threat model focuses on the core path resolution algorithm, including filesystem interaction, option handling, and cache management.
+
+### 3. Conceptual System Diagram
+
+```
+Caller Application → resolve(id, options) → Resolution Algorithm → File System
+                           │
+                           └→ Options Handling
+                           └→ Cache System
+```
+
+**Trust Boundaries:**
+- **Input module IDs:** May come from untrusted sources (user input, configuration)
+- **Filesystem access:** The library interacts with the filesystem to resolve paths
+- **Options:** Provided by the caller
+- **Cache:** Used to improve performance, but could be a vector for tampering or information disclosure if not handled securely
+
+### 4. Identify Assets
+
+- **Integrity of resolution output:** Ensure correct and safe file path matching.
+- **Confidentiality of configuration:** Prevent sensitive path information from being leaked.
+- **Availability/performance for host application:** Prevent crashes or resource exhaustion.
+- **Security of host application:** Prevent path traversal or unintended filesystem access.
+- **Reputation of library:** Maintain trust by avoiding supply chain attacks and vulnerabilities[1][3][4].
+
+### 5. Identify Threats
+
+| Component / API / Interaction                       | S  | T  | R  | I  | D  | E  |
+|-----------------------------------------------------|----|----|----|----|----|----|
+| Public API Call (`resolve/async`, `resolve/sync`)   | ✓  | ✓  | –  | ✓  | –  | –  |
+| Filesystem Access                                   | –  | ✓  | –  | ✓  | ✓  | –  |
+| Options Handling                                    | ✓  | ✓  | –  | ✓  | –  | –  |
+| Cache System                                        | –  | ✓  | –  | ✓  | –  | –  |
+
+**Key Threats:**
+- **Spoofing:** Malicious module IDs mimicking legitimate packages, or spoofing configuration options[1].
+- **Tampering:** Caller-provided paths altering resolution order, or cache tampering leading to incorrect results[1][4].
+- **Information Disclosure:** Error messages revealing filesystem structure or sensitive paths[1].
+- **Denial of Service:** Recursive or excessive resolution exhausting filesystem handles or causing application crashes[1].
+- **Path Traversal:** Malicious input allowing access to files outside the intended directory[4].
+
+### 6. Mitigation/Countermeasures
+
+| Threat Identified                          | Proposed Mitigation |
+|--------------------------------------------|---------------------|
+| Spoofing (malicious module IDs/config)     | Sanitize input IDs; validate against known patterns; restrict `basedir` to app-controlled paths[1][4]. |
+| Tampering (path traversal, cache)          | Validate input IDs for directory escapes; secure cache reads/writes; restrict cache to trusted sources[1][4]. |
+| Information Disclosure (error messages)    | Generic "not found" errors without internal paths; avoid exposing sensitive configuration in errors[1]. |
+| Denial of Service (resource exhaustion)    | Limit recursive resolution depth; implement timeout; monitor for excessive filesystem operations[1]. |
+
+### 7. Risk Ranking
+
+- **High:** Path traversal via malicious IDs (if not properly mitigated)
+- **Medium:** Cache tampering or spoofing (if cache is not secured)
+- **Low:** Information disclosure in errors (if error handling is generic)
+
+### 8. Next Steps & Review
+
+1. **Implement input sanitization for module IDs and configuration.**
+2. **Add resolution depth limiting and timeout.**
+3. **Audit cache handling for race conditions and tampering.**
+4. **Regularly review dependencies for vulnerabilities.**
+5. **Keep documentation and threat model up to date.**
+6. **Monitor for new threats as the ecosystem and library evolve[1][3].**
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/.nycrc
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/.nycrc	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/.nycrc	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+{
+	"all": true,
+	"reporter": [
+		"html",
+		"text",
+		"lcov"
+	],
+	"exclude": [
+		"coverage",
+		"eslint.config.mjs",
+		"example",
+		"scripts",
+		"test",
+		"**/*.d.*ts"
+	]
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/LICENSE
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2012 James Halliday
+
+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/node_modules/resolve/SECURITY.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/SECURITY.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/SECURITY.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+# Security
+
+Please [file a private vulnerability report](https://github.com/browserify/resolve/security/advisories/new),
+or email [@ljharb](https://github.com/ljharb),
+if you have a potential security vulnerability to report.
+
+## Incident Response
+
+See our [Incident Response Process](.github/INCIDENT_RESPONSE_PROCESS.md).
+
+## Threat Model
+
+See [THREAT_MODEL.md](./THREAT_MODEL.md).
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/async.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/async.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/async.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+import type resolveAsync = require('./lib/async');
+
+export = resolveAsync;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/async.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./lib/async');
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/bin/resolve
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/bin/resolve	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/bin/resolve	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+
+'use strict';
+
+var path = require('path');
+var fs = require('fs');
+
+if (
+    String(process.env.npm_lifecycle_script).slice(0, 8) !== 'resolve '
+    && (
+        !process.argv
+        || process.argv.length < 2
+        || (process.argv[1] !== __filename && fs.statSync(process.argv[1]).ino !== fs.statSync(__filename).ino)
+        || (process.env.npm_lifecycle_event !== 'npx' && process.env._ && fs.realpathSync(path.resolve(process.env._)) !== __filename)
+    )
+) {
+    console.error('Error: `resolve` must be run directly as an executable');
+    process.exit(1);
+}
+
+var supportsPreserveSymlinkFlag = require('supports-preserve-symlinks-flag');
+
+var preserveSymlinks = false;
+for (var i = 2; i < process.argv.length; i += 1) {
+    if (process.argv[i].slice(0, 2) === '--') {
+        if (supportsPreserveSymlinkFlag && process.argv[i] === '--preserve-symlinks') {
+            preserveSymlinks = true;
+        } else if (process.argv[i].length > 2) {
+            console.error('Unknown argument ' + process.argv[i].replace(/[=].*$/, ''));
+            process.exit(2);
+        }
+        process.argv.splice(i, 1);
+        i -= 1;
+        if (process.argv[i] === '--') { break; } // eslint-disable-line no-restricted-syntax
+    }
+}
+
+if (process.argv.length < 3) {
+    console.error('Error: `resolve` expects a specifier');
+    process.exit(2);
+}
+
+var resolve = require('../');
+
+var result = resolve.sync(process.argv[2], {
+    basedir: process.cwd(),
+    preserveSymlinks: preserveSymlinks
+});
+
+console.log(result);
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/eslint.config.mjs
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/eslint.config.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/eslint.config.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,82 @@
+import ljharb from '@ljharb/eslint-config/flat';
+
+export default [
+	...ljharb,
+	{
+		ignores: [
+			'test/resolver/malformed_package_json/package.json',
+			'test/list-exports/**',
+		],
+	},
+	{
+		rules: {
+			'array-bracket-newline': 'off',
+			complexity: 'off',
+			'consistent-return': 'off',
+			curly: 'off',
+			'dot-notation': ['error', { allowKeywords: true }],
+			eqeqeq: ['error', 'allow-null'],
+			'func-name-matching': 'off',
+			'func-style': 'off',
+			'global-require': 'warn',
+			'id-length': ['error', { min: 1, max: 40 }],
+			'max-depth': 'off',
+			'max-lines-per-function': 'off',
+			'max-lines': 'off',
+			'max-nested-callbacks': 'off',
+			'max-params': 'off',
+			'max-statements-per-line': ['error', { max: 2 }],
+			'max-statements': 'off',
+			'multiline-comment-style': 'off',
+			'no-extra-parens': 'off',
+			'no-magic-numbers': 'off',
+			'no-shadow': 'off',
+			'no-use-before-define': 'off',
+			'sort-keys': 'off',
+			strict: 'off',
+		},
+	},
+	{
+		files: ['**/*.js'],
+		rules: {
+			indent: ['error', 4],
+		},
+	},
+	{
+		files: ['bin/**'],
+		rules: {
+			'no-process-exit': 'off',
+		},
+	},
+	{
+		files: ['example/**'],
+		rules: {
+			'no-console': 'off',
+		},
+	},
+	{
+		files: ['test/resolver/nested_symlinks/mylib/*.js'],
+		rules: {
+			'no-throw-literal': 'off',
+		},
+	},
+	{
+		files: ['test/**'],
+		languageOptions: {
+			ecmaVersion: 5,
+			parserOptions: {
+				allowReserved: false,
+			},
+		},
+		rules: {
+			'dot-notation': ['error', { allowPattern: 'throws' }],
+			'max-lines': 'off',
+			'max-lines-per-function': 'off',
+			'no-unused-vars': ['error', {
+				vars: 'all',
+				args: 'none',
+				caughtErrors: 'none',
+			}],
+		},
+	},
+];
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/example/async.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/example/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/example/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,5 @@
+var resolve = require('../');
+resolve('tap', { basedir: __dirname }, function (err, res) {
+    if (err) console.error(err);
+    else console.log(res);
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/example/sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/example/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/example/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+var resolve = require('../');
+var res = resolve.sync('tap', { basedir: __dirname });
+console.log(res);
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.d.mts
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.d.mts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.d.mts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+import type { default as resolve } from './index.js';
+
+/** Asynchronously resolve a module path, like `require.resolve()`, on behalf of files. */
+export declare const async: typeof resolve;
+
+/** Synchronously resolve a module path, like `require.resolve()`, on behalf of files. */
+export declare const sync: typeof resolve.sync;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,184 @@
+/**
+ * Asynchronously resolve a module path, like `require.resolve()`, on behalf of files.
+ *
+ * @param id - The module identifier to resolve.
+ * @param options - Resolution options.
+ * @param callback - Called with `(err, resolved, pkg)` when resolution completes.
+ */
+declare function resolve(id: string, callback: resolve.Callback): void;
+declare function resolve(id: string, options: resolve.AsyncOptions, callback: resolve.Callback): void;
+
+declare namespace resolve {
+  /** Asynchronously resolve a module path, like `require.resolve()`, on behalf of files. */
+  function async(id: string, callback: Callback): void;
+  function async(id: string, options: AsyncOptions, callback: Callback): void;
+
+  /**
+   * Synchronously resolve a module path, like `require.resolve()`, on behalf of files.
+   *
+   * @param id - The module identifier to resolve.
+   * @param options - Resolution options.
+   * @returns The resolved file path.
+   * @throws If the module cannot be found.
+   */
+  function sync(id: string, options?: SyncOptions): string;
+
+  /** A parsed package.json object. */
+  interface PackageJSON {
+    [key: string]: unknown;
+    name?: string;
+    main?: string;
+    exports?: unknown;
+    engines?: { node?: string };
+  }
+
+  /** Callback for asynchronous resolution. */
+  type Callback = (err: Error | null, resolved?: string, pkg?: PackageJSON) => void;
+
+  /** Options shared by both async and sync resolution. */
+  interface BaseOptions {
+    /** Directory to resolve from. Defaults to `__dirname` of the calling file. */
+    basedir?: string;
+    /** The `package.json` object associated with the calling module. */
+    package?: PackageJSON;
+    /** File extensions to search, in order. Defaults to `['.js']`. */
+    extensions?: ReadonlyArray<string>;
+    /** Whether to include core modules (e.g. `fs`, `path`) in results. Defaults to `true`. */
+    includeCoreModules?: boolean;
+    /** Whether to preserve symlinks instead of resolving them. Defaults to `false`. */
+    preserveSymlinks?: boolean;
+    /** Additional lookup paths. */
+    paths?: ReadonlyArray<string> | ((request: string, start: string, getNodeModulesDirs: () => string[], opts: BaseOptions) => string[]);
+    /** The filename used for error messages and as a fallback for basedir. */
+    filename?: string;
+    /** The directory name(s) to use for node_modules lookups. Defaults to `['node_modules']`. */
+    moduleDirectory?: string | ReadonlyArray<string>;
+    /**
+     * Transform a package.json object before its `main` field is used.
+     *
+     * @param pkg - The parsed package.json.
+     * @param pkgFile - The path to the package.json file.
+     * @param dir - The directory containing the package.json.
+     * @returns The (possibly modified) package.json object.
+     */
+    packageFilter?: (pkg: PackageJSON, pkgFile: string, dir: string) => PackageJSON;
+    /**
+     * Transform a resolved path before it is used.
+     *
+     * @param pkg - The parsed package.json.
+     * @param path - The resolved path.
+     * @param relativePath - The path relative to the package directory.
+     * @returns An alternative path, or undefined/falsy to use the original.
+     */
+    pathFilter?: (pkg: PackageJSON, path: string, relativePath: string) => string | undefined;
+    /**
+     * Override the default node_modules candidate iterator.
+     *
+     * @param request - The module being resolved.
+     * @param start - The starting directory.
+     * @param thunk - A function returning the default candidate directories.
+     * @param opts - The resolve options.
+     * @returns An array of candidate directories.
+     */
+    packageIterator?: (request: string, start: string, thunk: () => string[], opts: BaseOptions) => string[];
+    /**
+     * An exports category string, as defined by `node-exports-info`.
+     * Mutually exclusive with `engines`.
+     */
+    exportsCategory?: string;
+    /**
+     * When `true`, reads the consumer's `engines.node` to determine the exports category.
+     * When a string, it is treated as a semver range for engines.node.
+     * Mutually exclusive with `exportsCategory`.
+     */
+    engines?: boolean | string;
+    /** Custom conditions for package.json `exports` resolution. */
+    conditions?: ReadonlyArray<string>;
+  }
+
+  /** Options for asynchronous resolution. */
+  interface AsyncOptions extends BaseOptions {
+    /**
+     * Check whether a path is a file.
+     *
+     * @param file - The path to check.
+     * @param cb - Called with `(err, isFile)`.
+     */
+    isFile?: (file: string, cb: (err: Error | null, isFile?: boolean) => void) => void;
+    /**
+     * Check whether a path is a directory.
+     *
+     * @param dir - The path to check.
+     * @param cb - Called with `(err, isDirectory)`.
+     */
+    isDirectory?: (dir: string, cb: (err: Error | null, isDirectory?: boolean) => void) => void;
+    /**
+     * Resolve a path's real location, following symlinks.
+     *
+     * @param file - The path to resolve.
+     * @param cb - Called with `(err, realPath)`.
+     */
+    realpath?: (file: string, cb: (err: Error | null, realPath?: string) => void) => void;
+    /**
+     * Read a file's contents.
+     * Mutually exclusive with `readPackage`.
+     *
+     * @param file - The path to read.
+     * @param cb - Called with `(err, contents)`.
+     */
+    readFile?: (file: string, cb: (err: Error | null, contents?: string | Buffer) => void) => void;
+    /**
+     * Read and parse a package.json file.
+     * Mutually exclusive with `readFile`.
+     *
+     * @param readFile - The file-reading function.
+     * @param pkgFile - The path to the package.json.
+     * @param cb - Called with `(err, pkg)`.
+     */
+    readPackage?: (readFile: (file: string, cb: (err: Error | null, contents?: string | Buffer) => void) => void, pkgFile: string, cb: (err: Error | null, pkg?: PackageJSON) => void) => void;
+  }
+
+  /** Options for synchronous resolution. */
+  interface SyncOptions extends BaseOptions {
+    /**
+     * Check whether a path is a file.
+     *
+     * @param file - The path to check.
+     * @returns `true` if the path is a file.
+     */
+    isFile?: (file: string) => boolean;
+    /**
+     * Check whether a path is a directory.
+     *
+     * @param dir - The path to check.
+     * @returns `true` if the path is a directory.
+     */
+    isDirectory?: (dir: string) => boolean;
+    /**
+     * Resolve a path's real location, following symlinks.
+     *
+     * @param file - The path to resolve.
+     * @returns The resolved real path.
+     */
+    realpathSync?: (file: string) => string;
+    /**
+     * Read a file's contents synchronously.
+     * Mutually exclusive with `readPackageSync`.
+     *
+     * @param file - The path to read.
+     * @returns The file contents.
+     */
+    readFileSync?: (file: string) => string | Buffer;
+    /**
+     * Read and parse a package.json file synchronously.
+     * Mutually exclusive with `readFileSync`.
+     *
+     * @param readFileSync - The synchronous file-reading function.
+     * @param pkgFile - The path to the package.json.
+     * @returns The parsed package.json object.
+     */
+    readPackageSync?: (readFileSync: (file: string) => string | Buffer, pkgFile: string) => PackageJSON;
+  }
+}
+
+export = resolve;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4 @@
+var async = require('./lib/async');
+async.sync = require('./lib/sync');
+
+module.exports = async;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.mjs
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/index.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4 @@
+import async from 'resolve/async';
+import sync from 'resolve/sync';
+
+export { async, sync };
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+import type resolve = require('../index.js');
+
+/**
+ * Asynchronously resolve a module path, like `require.resolve()`, on behalf of files.
+ *
+ * @param id - The module identifier to resolve.
+ * @param options - Resolution options.
+ * @param callback - Called with `(err, resolved, pkg)` when resolution completes.
+ */
+declare function resolveAsync(id: string, callback: resolve.Callback): void;
+declare function resolveAsync(id: string, options: resolve.AsyncOptions, callback: resolve.Callback): void;
+
+export = resolveAsync;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,655 @@
+var fs = require('fs');
+var getHomedir = require('./homedir');
+var path = require('path');
+var caller = require('./caller');
+var nodeModulesPaths = require('./node-modules-paths');
+var normalizeOptions = require('./normalize-options');
+var isCore = require('is-core-module');
+var $Error = require('es-errors');
+var $TypeError = require('es-errors/type');
+var getCategoryInfo = require('node-exports-info/getCategoryInfo');
+var getCategoriesForRange = require('node-exports-info/getCategoriesForRange');
+
+var resolveExports = require('./exports-resolve');
+var parsePackageSpecifier = require('./parse-package-specifier');
+var getExportsCategory = require('./get-exports-category');
+var selectMostRestrictive = require('./select-most-restrictive');
+
+var realpathFS = process.platform !== 'win32' && fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;
+
+var relativePathRegex = /^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/;
+var windowsDriveRegex = /^\w:[/\\]*$/;
+var nodeModulesRegex = /[/\\]node_modules[/\\]*$/;
+
+var homedir = getHomedir();
+function defaultPaths() {
+    if (!homedir) return [];
+    return [
+        path.join(homedir, '.node_modules'),
+        path.join(homedir, '.node_libraries')
+    ];
+}
+
+var defaultIsFile = function isFile(file, cb) {
+    fs.stat(file, function (err, stat) {
+        if (!err) {
+            return cb(null, stat.isFile() || stat.isFIFO());
+        }
+        if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
+        return cb(err);
+    });
+};
+
+var defaultIsDir = function isDirectory(dir, cb) {
+    fs.stat(dir, function (err, stat) {
+        if (!err) {
+            return cb(null, stat.isDirectory());
+        }
+        if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
+        return cb(err);
+    });
+};
+
+var defaultRealpath = function realpath(x, cb) {
+    realpathFS(x, function (realpathErr, realPath) {
+        if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr);
+        else cb(null, realpathErr ? x : realPath);
+    });
+};
+
+function maybeRealpath(realpath, x, opts, cb) {
+    if (!opts || !opts.preserveSymlinks) {
+        realpath(x, cb);
+    } else {
+        cb(null, x);
+    }
+}
+
+function defaultReadPackage(readFile, pkgfile, cb) {
+    readFile(pkgfile, function (readFileErr, body) {
+        if (readFileErr) cb(readFileErr);
+        else {
+            try {
+                var pkg = JSON.parse(body);
+                cb(null, pkg);
+            } catch (jsonErr) {
+                cb(jsonErr);
+            }
+        }
+    });
+}
+
+function getPackageCandidates(x, start, opts) {
+    var dirs = nodeModulesPaths(start, opts, x);
+    for (var i = 0; i < dirs.length; i++) {
+        dirs[i] = path.join(dirs[i], x);
+    }
+    return dirs;
+}
+
+function findUpConsumer(currentDir, isFile, readPackage, readFile, done) {
+    var pkgfile = path.join(currentDir, 'package.json');
+    isFile(pkgfile, function (err, exists) {
+        if (err) { return done(err); }
+        if (exists) {
+            readPackage(readFile, pkgfile, function (readErr, pkg) {
+                if (readErr && !(readErr instanceof SyntaxError)) { return done(readErr); }
+                done(null, pkg || null);
+            });
+        } else {
+            var parentDir = path.dirname(currentDir);
+            if (parentDir === currentDir) {
+                return done(null, null);
+            }
+            findUpConsumer(parentDir, isFile, readPackage, readFile, done);
+        }
+    });
+}
+
+function findConsumerPackage(startDir, isFile, readPackage, readFile, done) {
+    var dir = path.resolve(startDir);
+    findUpConsumer(dir, isFile, readPackage, readFile, done);
+}
+
+function findUpWithDir(currentDir, isFile, readPackage, readFile, done) {
+    // Stop at node_modules boundaries - can't self-reference across node_modules
+    if (nodeModulesRegex.test(currentDir)) {
+        return done(null, null);
+    }
+    var pkgfile = path.join(currentDir, 'package.json');
+    isFile(pkgfile, function (err, exists) {
+        if (err) { return done(err); }
+        if (exists) {
+            readPackage(readFile, pkgfile, function (readErr, pkg) {
+                if (readErr && !(readErr instanceof SyntaxError)) { return done(readErr); }
+                done(null, pkg ? {
+                    __proto__: null, pkg: pkg, dir: currentDir
+                } : null);
+            });
+        } else {
+            var parentDir = path.dirname(currentDir);
+            if (parentDir === currentDir) {
+                return done(null, null);
+            }
+            findUpWithDir(parentDir, isFile, readPackage, readFile, done);
+        }
+    });
+}
+
+module.exports = function resolve(x, options, callback) {
+    var cb = callback;
+    var opts = options;
+    if (typeof options === 'function') {
+        cb = opts;
+        opts = {};
+    }
+    if (typeof x !== 'string') {
+        var err = new $TypeError('Path must be a string.');
+        return process.nextTick(function () {
+            cb(err);
+        });
+    }
+
+    opts = normalizeOptions(x, opts);
+
+    var isFile = opts.isFile || defaultIsFile;
+    var isDirectory = opts.isDirectory || defaultIsDir;
+    var readFile = opts.readFile || fs.readFile;
+    var realpath = opts.realpath || defaultRealpath;
+    var readPackage = opts.readPackage || defaultReadPackage;
+    if (opts.readFile && opts.readPackage) {
+        var conflictErr = new $TypeError('`readFile` and `readPackage` are mutually exclusive.');
+        return process.nextTick(function () {
+            cb(conflictErr);
+        });
+    }
+    var packageIterator = opts.packageIterator;
+
+    if (typeof opts.moduleSystem !== 'undefined' && opts.moduleSystem !== 'require' && opts.moduleSystem !== 'import') {
+        var msErr = new $TypeError('`moduleSystem` must be `\'require\'` or `\'import\'`.');
+        return process.nextTick(function () {
+            cb(msErr);
+        });
+    }
+
+    var extensions = opts.extensions || ['.js'];
+    var includeCoreModules = opts.includeCoreModules !== false;
+    var basedir = opts.basedir || path.dirname(caller());
+    var parent = opts.filename || basedir;
+
+    opts.paths = opts.paths || defaultPaths();
+
+    // Determine exports category
+    var exportsCategory;
+    // hoist the caught error into a `var` rather than closing over the catch binding;
+    // old V8 (node < 4) drops the catch param when the inner closure is rewritten by nyc.
+    var catErr;
+    try {
+        exportsCategory = getExportsCategory(opts);
+    } catch (e) {
+        catErr = e;
+    }
+    if (catErr) {
+        return process.nextTick(function () {
+            cb(catErr);
+        });
+    }
+
+    // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
+    var absoluteStart = path.resolve(basedir);
+
+    maybeRealpath(
+        realpath,
+        absoluteStart,
+        opts,
+        function (err, realStart) {
+            if (err) cb(err);
+            else if (exportsCategory === 'engines') {
+                // Need to read consumer's package.json for engines.node
+                findConsumerPackage(realStart, isFile, readPackage, readFile, function (findErr, consumerPkg) {
+                    if (findErr) return cb(findErr);
+                    if (consumerPkg && consumerPkg.engines && consumerPkg.engines.node) {
+                        var categories = getCategoriesForRange(consumerPkg.engines.node);
+                        exportsCategory = selectMostRestrictive(categories);
+                    } else {
+                        exportsCategory = null;
+                    }
+                    validateBasedir(realStart);
+                });
+            } else {
+                validateBasedir(realStart);
+            }
+        }
+    );
+
+    function findPackageWithDir(startDir, done) {
+        var dir = path.resolve(startDir);
+        findUpWithDir(dir, isFile, readPackage, readFile, done);
+    }
+
+    function resolveSelfReference(x, startDir, done) {
+        var parsed = parsePackageSpecifier(x);
+        findPackageWithDir(startDir, function (err, pkgInfo) {
+            if (err) return done(err);
+            if (!pkgInfo || !pkgInfo.pkg || pkgInfo.pkg.name !== parsed.name) {
+                return done(null, null); // Not a self-reference
+            }
+
+            var pkg = pkgInfo.pkg;
+            var pkgDir = pkgInfo.dir;
+
+            if (opts.packageFilter) {
+                pkg = opts.packageFilter(pkg, path.join(pkgDir, 'package.json'), pkgDir);
+            }
+
+            // If package has exports field, resolve via exports
+            if (typeof pkg.exports !== 'undefined') {
+                var categoryInfo = getCategoryInfo(exportsCategory, opts.moduleSystem || 'require');
+                var conditions = opts.conditions || categoryInfo.conditions;
+                var resolved;
+                try {
+                    resolved = resolveExports(pkg.exports, parsed.subpath, conditions, categoryInfo.flags);
+                } catch (exportsErr) {
+                    return done(exportsErr);
+                }
+                if (resolved) {
+                    var resolvedPath = path.resolve(pkgDir, resolved);
+                    isFile(resolvedPath, function (err, exists) {
+                        if (err) return done(err);
+                        if (exists) return done(null, resolvedPath, pkg);
+                        // File doesn't exist
+                        done(null, undefined);
+                    });
+                    return;
+                }
+                // exports field exists but didn't resolve
+                return done(null, undefined);
+            }
+
+            // No exports field - fall back to traditional resolution for self-reference
+            if (parsed.subpath === '.') {
+                loadAsDirectory(pkgDir, pkg, function (err, result, resultPkg) {
+                    done(err, result, resultPkg);
+                });
+            } else {
+                var subPath = path.join(pkgDir, parsed.subpath.slice(1));
+                loadAsFile(subPath, pkg, function (err, m, mPkg) {
+                    if (err) return done(err);
+                    if (m) return done(null, m, mPkg);
+                    loadAsDirectory(subPath, pkg, function (err, n, nPkg) {
+                        done(err, n, nPkg);
+                    });
+                });
+            }
+        });
+    }
+
+    function validateBasedir(basedir) {
+        if (opts.basedir) {
+            var dirError = new $TypeError('Provided basedir "' + basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
+            dirError.code = 'INVALID_BASEDIR';
+            isDirectory(basedir, function (err, result) {
+                if (err) return cb(err);
+                if (!result) { return cb(dirError); }
+                validBasedir(basedir);
+            });
+        } else {
+            validBasedir(basedir);
+        }
+    }
+
+    var useExports = false;
+    var res;
+    function validBasedir(basedir) {
+        useExports = exportsCategory !== null && exportsCategory !== 'pre-exports';
+
+        if (relativePathRegex.test(x)) {
+            res = path.resolve(basedir, x);
+            if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
+            if (x.slice(-1) === '/' && res === basedir) {
+                loadAsDirectory(res, opts.package, onfile);
+            } else loadAsFile(res, opts.package, onfile);
+        } else if (includeCoreModules && isCore(x)) {
+            return cb(null, x);
+        } else if (useExports) {
+            // Try self-reference resolution first
+            resolveSelfReference(x, basedir, function (selfErr, selfRef, selfPkg) {
+                if (selfErr) return cb(selfErr);
+                if (selfRef) {
+                    return maybeRealpath(realpath, selfRef, opts, function (err, realSelf) {
+                        if (err) cb(err);
+                        else cb(null, realSelf, selfPkg);
+                    });
+                }
+                if (selfRef === undefined) {
+                    // exports field exists but didn't resolve - error per Node semantics
+                    var moduleError = new $Error("Cannot find module '" + x + "' from '" + parent + "'");
+                    moduleError.code = 'MODULE_NOT_FOUND';
+                    return cb(moduleError);
+                }
+                loadNodeModulesWithExports(x, basedir, function (err, n, pkg) {
+                    if (err) cb(err);
+                    else if (n) {
+                        return maybeRealpath(realpath, n, opts, function (err, realN) {
+                            if (err) {
+                                cb(err);
+                            } else {
+                                cb(null, realN, pkg);
+                            }
+                        });
+                    } else {
+                        var moduleError = new $Error("Cannot find module '" + x + "' from '" + parent + "'");
+                        moduleError.code = 'MODULE_NOT_FOUND';
+                        cb(moduleError);
+                    }
+                });
+            });
+        } else {
+            loadNodeModules(x, basedir, function (err, n, pkg) {
+                if (err) cb(err);
+                else if (n) {
+                    return maybeRealpath(realpath, n, opts, function (err, realN) {
+                        if (err) {
+                            cb(err);
+                        } else {
+                            cb(null, realN, pkg);
+                        }
+                    });
+                } else {
+                    var moduleError = new $Error("Cannot find module '" + x + "' from '" + parent + "'");
+                    moduleError.code = 'MODULE_NOT_FOUND';
+                    cb(moduleError);
+                }
+            });
+        }
+    }
+
+    function onfile(err, m, pkg) {
+        if (err) cb(err);
+        else if (m) cb(null, m, pkg);
+        else loadAsDirectory(res, function (err, d, pkg) {
+            if (err) cb(err);
+            else if (d) {
+                maybeRealpath(realpath, d, opts, function (err, realD) {
+                    if (err) {
+                        cb(err);
+                    } else {
+                        cb(null, realD, pkg);
+                    }
+                });
+            } else {
+                var moduleError = new $Error("Cannot find module '" + x + "' from '" + parent + "'");
+                moduleError.code = 'MODULE_NOT_FOUND';
+                cb(moduleError);
+            }
+        });
+    }
+
+    function loadAsFile(x, thePackage, callback) {
+        var loadAsFilePackage = thePackage;
+        var cb = callback;
+        if (typeof loadAsFilePackage === 'function') {
+            cb = loadAsFilePackage;
+            loadAsFilePackage = undefined;
+        }
+
+        var exts = [''].concat(extensions);
+        load(exts, x, loadAsFilePackage);
+
+        function load(exts, x, loadPackage) {
+            if (exts.length === 0) return cb(null, undefined, loadPackage);
+            var file = x + exts[0];
+
+            var pkg = loadPackage;
+            if (pkg) onpkg(null, pkg);
+            else loadpkg(path.dirname(file), onpkg);
+
+            function onpkg(err, pkg_, dir) {
+                pkg = pkg_;
+                if (err) return cb(err);
+                if (dir && pkg && opts.pathFilter) {
+                    var rfile = path.relative(dir, file);
+                    var rel = rfile.slice(0, rfile.length - exts[0].length);
+                    var r = opts.pathFilter(pkg, x, rel);
+                    if (r) return load(
+                        [''].concat(extensions),
+                        path.resolve(dir, r),
+                        pkg
+                    );
+                }
+                isFile(file, onex);
+            }
+            function onex(err, ex) {
+                if (err) return cb(err);
+                if (ex) return cb(null, file, pkg);
+                load(exts.slice(1), x, pkg);
+            }
+        }
+    }
+
+    function loadpkg(dir, cb) {
+        if (dir === '' || dir === '/') return cb(null);
+        if (process.platform === 'win32' && windowsDriveRegex.test(dir)) {
+            return cb(null);
+        }
+        if (nodeModulesRegex.test(dir)) return cb(null);
+
+        maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) {
+            if (unwrapErr) return loadpkg(path.dirname(dir), cb);
+            var pkgfile = path.join(pkgdir, 'package.json');
+            isFile(pkgfile, function (err, ex) {
+                // on err, ex is false
+                if (!ex) return loadpkg(path.dirname(dir), cb);
+
+                readPackage(readFile, pkgfile, function (err, pkgParam) {
+                    if (err && !(err instanceof SyntaxError)) return cb(err);
+
+                    var pkg = pkgParam;
+
+                    if (pkg && opts.packageFilter) {
+                        pkg = opts.packageFilter(pkg, pkgfile, dir);
+                    }
+                    cb(null, pkg, dir);
+                });
+            });
+        });
+    }
+
+    function loadAsDirectory(x, loadAsDirectoryPackage, callback) {
+        var cb = callback;
+        var fpkg = loadAsDirectoryPackage;
+        if (typeof fpkg === 'function') {
+            cb = fpkg;
+            fpkg = opts.package;
+        }
+
+        maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) {
+            if (unwrapErr) return loadAsDirectory(path.dirname(x), fpkg, cb);
+            var pkgfile = path.join(pkgdir, 'package.json');
+            isFile(pkgfile, function (err, ex) {
+                if (err) return cb(err);
+                if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb);
+
+                readPackage(readFile, pkgfile, function (err, pkgParam) {
+                    if (err) return cb(err);
+
+                    var pkg = pkgParam;
+
+                    if (pkg && opts.packageFilter) {
+                        pkg = opts.packageFilter(pkg, pkgfile, pkgdir);
+                    }
+
+                    if (pkg && pkg.main) {
+                        if (typeof pkg.main !== 'string') {
+                            var mainError = new $TypeError('package “' + pkg.name + '” `main` must be a string');
+                            mainError.code = 'INVALID_PACKAGE_MAIN';
+                            return cb(mainError);
+                        }
+                        if (pkg.main === '.' || pkg.main === './') {
+                            pkg.main = 'index';
+                        }
+                        loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) {
+                            if (err) return cb(err);
+                            if (m) return cb(null, m, pkg);
+                            if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb);
+
+                            var dir = path.resolve(x, pkg.main);
+                            loadAsDirectory(dir, pkg, function (err, n, pkg) {
+                                if (err) return cb(err);
+                                if (n) return cb(null, n, pkg);
+                                loadAsFile(path.join(x, 'index'), pkg, function (err, m, pkg) {
+                                    if (err) return cb(err);
+                                    if (m) return cb(null, m, pkg);
+                                    var incorrectMainError = new $Error("Cannot find module '" + path.resolve(x, pkg.main) + "'. Please verify that the package.json has a valid \"main\" entry");
+                                    incorrectMainError.code = 'INCORRECT_PACKAGE_MAIN';
+                                    return cb(incorrectMainError);
+                                });
+                            });
+                        });
+                        return;
+                    }
+
+                    loadAsFile(path.join(x, '/index'), pkg, cb);
+                });
+            });
+        });
+    }
+
+    function processDirs(cb, dirs) {
+        if (dirs.length === 0) return cb(null, undefined);
+        var dir = dirs[0];
+
+        isDirectory(path.dirname(dir), isdir);
+
+        function isdir(err, isdir) {
+            if (err) return cb(err);
+            if (!isdir) return processDirs(cb, dirs.slice(1));
+            loadAsFile(dir, opts.package, onfile);
+        }
+
+        function onfile(err, m, pkg) {
+            if (err) return cb(err);
+            if (m) return cb(null, m, pkg);
+            loadAsDirectory(dir, opts.package, ondir);
+        }
+
+        function ondir(err, n, pkg) {
+            if (err) return cb(err);
+            if (n) return cb(null, n, pkg);
+            processDirs(cb, dirs.slice(1));
+        }
+    }
+    function loadNodeModules(x, start, cb) {
+        var thunk = function () { return getPackageCandidates(x, start, opts); };
+        processDirs(
+            cb,
+            packageIterator ? packageIterator(x, start, thunk, opts) : thunk()
+        );
+    }
+
+    function loadNodeModulesWithExports(x, start, done) {
+        var parsed = parsePackageSpecifier(x);
+        var categoryInfo = getCategoryInfo(exportsCategory, opts.moduleSystem || 'require');
+        var conditions = opts.conditions || categoryInfo.conditions;
+
+        var thunk = function () { return getPackageCandidates(parsed.name, start, opts); };
+        var dirs = packageIterator ? packageIterator(parsed.name, start, thunk, opts) : thunk();
+
+        processExportsDirs(dirs, 0);
+
+        function processExportsDirs(dirs, idx) {
+            if (idx >= dirs.length) return done(null, undefined);
+            var pkgDir = dirs[idx];
+
+            isDirectory(pkgDir, function (err, isDir) {
+                if (err) return done(err);
+                if (!isDir) return processExportsDirs(dirs, idx + 1);
+
+                var pkgfile = path.join(pkgDir, 'package.json');
+                isFile(pkgfile, function (err, exists) {
+                    if (err) return done(err);
+                    if (!exists) {
+                        // No package.json, fall back to file/directory resolution
+                        if (parsed.subpath === '.') {
+                            loadAsFile(pkgDir, opts.package, function (err, m, pkg) {
+                                if (err) return done(err);
+                                if (m) return done(null, m, pkg);
+                                loadAsDirectory(pkgDir, opts.package, function (err, n, pkg) {
+                                    if (err) return done(err);
+                                    if (n) return done(null, n, pkg);
+                                    processExportsDirs(dirs, idx + 1);
+                                });
+                            });
+                        } else {
+                            var fullPath = path.join(pkgDir, parsed.subpath.slice(1));
+                            loadAsFile(fullPath, opts.package, function (err, m, pkg) {
+                                if (err) return done(err);
+                                if (m) return done(null, m, pkg);
+                                loadAsDirectory(fullPath, opts.package, function (err, n, pkg) {
+                                    if (err) return done(err);
+                                    if (n) return done(null, n, pkg);
+                                    processExportsDirs(dirs, idx + 1);
+                                });
+                            });
+                        }
+                        return;
+                    }
+
+                    readPackage(readFile, pkgfile, function (err, pkg) {
+                        if (err) {
+                            if (!(err instanceof SyntaxError)) return done(err);
+                            return processExportsDirs(dirs, idx + 1);
+                        }
+
+                        if (pkg && opts.packageFilter) {
+                            // eslint-disable-next-line no-param-reassign
+                            pkg = opts.packageFilter(pkg, pkgfile, pkgDir);
+                        }
+
+                        // If package has exports field, use exports resolution
+                        if (pkg && typeof pkg.exports !== 'undefined') {
+                            var resolved;
+                            try {
+                                resolved = resolveExports(pkg.exports, parsed.subpath, conditions, categoryInfo.flags);
+                            } catch (exportsErr) {
+                                return done(exportsErr);
+                            }
+                            if (resolved) {
+                                var resolvedPath = path.resolve(pkgDir, resolved);
+                                isFile(resolvedPath, function (err, exists) {
+                                    if (err) return done(err);
+                                    if (exists) return done(null, resolvedPath, pkg);
+                                    // File doesn't exist
+                                    done(null, undefined);
+                                });
+                                return;
+                            }
+                            // exports field exists but didn't resolve
+                            return done(null, undefined);
+                        }
+
+                        // No exports field, fall back to traditional resolution
+                        if (parsed.subpath === '.') {
+                            loadAsDirectory(pkgDir, pkg, function (err, result, resultPkg) {
+                                if (err) return done(err);
+                                if (result) return done(null, result, resultPkg);
+                                processExportsDirs(dirs, idx + 1);
+                            });
+                        } else {
+                            var subPath = path.join(pkgDir, parsed.subpath.slice(1));
+                            loadAsFile(subPath, pkg, function (err, m, mPkg) {
+                                if (err) return done(err);
+                                if (m) return done(null, m, mPkg);
+                                loadAsDirectory(subPath, pkg, function (err, n, nPkg) {
+                                    if (err) return done(err);
+                                    if (n) return done(null, n, nPkg);
+                                    processExportsDirs(dirs, idx + 1);
+                                });
+                            });
+                        }
+                    });
+                });
+            });
+        }
+    }
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/caller.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/caller.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/caller.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,12 @@
+'use strict';
+
+var $Error = require('es-errors');
+
+module.exports = function () {
+    // see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
+    var origPrepareStackTrace = $Error.prepareStackTrace;
+    $Error.prepareStackTrace = function (_, stack) { return stack; };
+    var stack = (new $Error()).stack;
+    $Error.prepareStackTrace = origPrepareStackTrace;
+    return stack[2].getFileName();
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/exports-resolve.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/exports-resolve.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/exports-resolve.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,273 @@
+'use strict';
+
+var objectKeys = require('object-keys');
+var $Error = require('es-errors');
+
+// Check if an exports map key looks like a subpath (starts with '.')
+function isSubpathKey(key) {
+    return key.length > 0 && key.charAt(0) === '.';
+}
+
+// Normalize the exports field into a map of subpath -> target
+function normalizeExports(exportsField) {
+    if (typeof exportsField === 'string') {
+        return { __proto__: null, '.': exportsField };
+    }
+    if (Array.isArray(exportsField)) {
+        return { __proto__: null, '.': exportsField };
+    }
+    if (typeof exportsField === 'object' && exportsField !== null) {
+        var keys = objectKeys(exportsField);
+        if (keys.length === 0) {
+            return { __proto__: null };
+        }
+        // If any key starts with '.', it's a subpath map
+        // If no key starts with '.', it's a conditions object for '.'
+        var hasSubpath = false;
+        for (var i = 0; !hasSubpath && i < keys.length; i++) {
+            if (isSubpathKey(keys[i])) {
+                hasSubpath = true;
+            }
+        }
+        // Copy to new object with null prototype
+        var result = { __proto__: null };
+        for (var j = 0; j < keys.length; j++) {
+            result[keys[j]] = exportsField[keys[j]];
+        }
+        if (hasSubpath) {
+            return result;
+        }
+        return { __proto__: null, '.': result };
+    }
+    return null;
+}
+
+// Resolve a target value through conditions
+// conditions: array of condition strings, or null (broken: string/array only)
+function resolveTarget(target, conditions) {
+    if (typeof target === 'string') {
+        return target;
+    }
+
+    if (target === null) {
+        return null;
+    }
+
+    if (Array.isArray(target)) {
+        for (var i = 0; i < target.length; i++) {
+            var resolved = resolveTarget(target[i], conditions);
+            if (resolved !== null && typeof resolved !== 'undefined') {
+                return resolved;
+            }
+        }
+        return null;
+    }
+
+    if (typeof target === 'object') {
+        // If no conditions supported (broken category), can't resolve objects
+        if (conditions === null) {
+            return null;
+        }
+        var keys = objectKeys(target);
+        for (var j = 0; j < keys.length; j++) {
+            var key = keys[j];
+            for (var k = 0; k < conditions.length; k++) {
+                if (key === conditions[k]) {
+                    var result = resolveTarget(target[key], conditions);
+                    if (result != null) {
+                        return result;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    return null;
+}
+
+// Validate a resolved path
+function validateTarget(target) {
+    if (typeof target !== 'string') {
+        return false;
+    }
+    if (target.slice(0, 2) !== './') {
+        return false;
+    }
+    if (target.indexOf('/node_modules/') !== -1) {
+        return false;
+    }
+    // Check for '..' path traversal
+    var parts = target.split('/');
+    for (var i = 0; i < parts.length; i++) {
+        if (parts[i] === '..') {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Find the best pattern match for a subpath among keys with '*'
+function findPatternMatch(subpath, exportsMap, allowPatternTrailers) {
+    var keys = objectKeys(exportsMap);
+    var bestKey = null;
+    var bestPrefixLen = -1;
+    var bestMatch = '';
+
+    for (var i = 0; i < keys.length; i++) {
+        var key = keys[i];
+        var starIndex = key.indexOf('*');
+        // Key must have exactly one '*'
+        if (starIndex !== -1 && key.indexOf('*', starIndex + 1) === -1) {
+            var prefix = key.slice(0, starIndex);
+            var suffix = key.slice(starIndex + 1);
+
+            // Pattern trailers: if suffix is non-empty after *, need allowPatternTrailers
+            if (suffix.length === 0 || allowPatternTrailers) {
+                if (
+                    subpath.length >= prefix.length + suffix.length
+                    && subpath.slice(0, prefix.length) === prefix
+                    && (suffix.length === 0 || subpath.slice(subpath.length - suffix.length) === suffix)
+                ) {
+                    // Longest prefix wins
+                    if (prefix.length > bestPrefixLen) {
+                        bestPrefixLen = prefix.length;
+                        bestKey = key;
+                        bestMatch = subpath.slice(prefix.length, subpath.length - suffix.length);
+                    }
+                }
+            }
+        }
+    }
+
+    if (bestKey !== null) {
+        return {
+            __proto__: null, key: bestKey, match: bestMatch
+        };
+    }
+    return null;
+}
+
+// Find directory slash match (for categories that support it)
+function findDirSlashMatch(subpath, exportsMap) {
+    var keys = objectKeys(exportsMap);
+    var bestKey = null;
+    var bestPrefixLen = -1;
+
+    for (var i = 0; i < keys.length; i++) {
+        var key = keys[i];
+        if (key.charAt(key.length - 1) === '/') {
+            if (subpath.slice(0, key.length) === key && key.length > bestPrefixLen) {
+                bestPrefixLen = key.length;
+                bestKey = key;
+            }
+        }
+    }
+
+    if (bestKey !== null) {
+        return {
+            __proto__: null, key: bestKey, remainder: subpath.slice(bestKey.length)
+        };
+    }
+    return null;
+}
+
+// Replace '*' in target string with match value
+function substitutePattern(target, match) {
+    if (typeof target === 'string') {
+        return target.split('*').join(match);
+    }
+    if (Array.isArray(target)) {
+        var result = [];
+        for (var i = 0; i < target.length; i++) {
+            result.push(substitutePattern(target[i], match));
+        }
+        return result;
+    }
+    if (typeof target === 'object' && target !== null) {
+        var obj = { __proto__: null };
+        var keys = objectKeys(target);
+        for (var j = 0; j < keys.length; j++) {
+            obj[keys[j]] = substitutePattern(target[keys[j]], match);
+        }
+        return obj;
+    }
+    return target;
+}
+
+// Main exports resolution function
+// exportsField: the value of package.json "exports"
+// subpath: the subpath to resolve (e.g., "." or "./foo/bar")
+// conditions: array of condition strings, or null for broken category
+// options: { patterns: boolean, patternTrailers: boolean, dirSlash: boolean }
+// Returns: resolved relative path string, or null if no exports field
+// Throws: when exports field exists but subpath is not exported
+module.exports = function resolveExports(exportsField, subpath, conditions, options) {
+    if (typeof exportsField === 'undefined') {
+        return null;
+    }
+
+    var exportsMap = normalizeExports(exportsField);
+    if (!exportsMap) {
+        return null;
+    }
+
+    var allowPatterns = options && options.patterns;
+    var allowPatternTrailers = options && options.patternTrailers;
+    var allowDirSlash = options && options.dirSlash;
+
+    // 1. Exact key match
+    if (typeof exportsMap[subpath] !== 'undefined') {
+        var resolved = resolveTarget(exportsMap[subpath], conditions);
+        if (resolved !== null && typeof resolved !== 'undefined') {
+            if (!validateTarget(resolved)) {
+                var invalidError = new $Error('Invalid "exports" target "' + resolved + '" for subpath "' + subpath + '"');
+                invalidError.code = 'ERR_INVALID_PACKAGE_CONFIG';
+                throw invalidError;
+            }
+            return resolved;
+        }
+        // Target exists but resolved to null (explicitly not exported)
+        var notExportedError = new $Error('Package subpath "' + subpath + '" is not defined by "exports"');
+        notExportedError.code = 'ERR_PACKAGE_PATH_NOT_EXPORTED';
+        throw notExportedError;
+    }
+
+    // 2. Pattern match (keys with '*')
+    if (allowPatterns) {
+        var patternResult = findPatternMatch(subpath, exportsMap, allowPatternTrailers);
+        if (patternResult) {
+            var substituted = substitutePattern(exportsMap[patternResult.key], patternResult.match);
+            var patternResolved = resolveTarget(substituted, conditions);
+            if (patternResolved !== null && typeof patternResolved !== 'undefined') {
+                if (!validateTarget(patternResolved)) {
+                    var patternInvalidError = new $Error('Invalid "exports" target "' + patternResolved + '" for subpath "' + subpath + '"');
+                    patternInvalidError.code = 'ERR_INVALID_PACKAGE_CONFIG';
+                    throw patternInvalidError;
+                }
+                return patternResolved;
+            }
+        }
+    }
+
+    // 3. Directory slash match (for older categories)
+    if (allowDirSlash) {
+        var dirResult = findDirSlashMatch(subpath, exportsMap);
+        if (dirResult) {
+            var dirTarget = resolveTarget(exportsMap[dirResult.key], conditions);
+            if (dirTarget !== null && typeof dirTarget !== 'undefined' && typeof dirTarget === 'string') {
+                var dirResolved = dirTarget + dirResult.remainder;
+                if (!validateTarget(dirResolved)) {
+                    var dirInvalidError = new $Error('Invalid "exports" target "' + dirResolved + '" for subpath "' + subpath + '"');
+                    dirInvalidError.code = 'ERR_INVALID_PACKAGE_CONFIG';
+                    throw dirInvalidError;
+                }
+                return dirResolved;
+            }
+        }
+    }
+
+    var err = new $Error('Package subpath "' + subpath + '" is not defined by "exports"');
+    err.code = 'ERR_PACKAGE_PATH_NOT_EXPORTED';
+    throw err;
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/get-exports-category.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/get-exports-category.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/get-exports-category.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,52 @@
+'use strict';
+
+var isCategory = require('node-exports-info/isCategory');
+var getCategoriesForRange = require('node-exports-info/getCategoriesForRange');
+var $TypeError = require('es-errors/type');
+
+var selectMostRestrictive = require('./select-most-restrictive');
+
+// Determine the active exports category from resolve options
+// Returns null if no exports resolution should be applied
+// Returns 'engines' if engines: true (needs consumer package.json lookup)
+// Throws TypeError if invalid options are provided
+/** @type {(opts?: { exportsCategory?: import('node-exports-info/getCategory').Category, engines?: boolean | string }) => null | import('node-exports-info/getCategory').Category} */
+module.exports = function getExportsCategory(opts) {
+    if (!opts) {
+        return null;
+    }
+
+    var hasCategory = typeof opts.exportsCategory !== 'undefined';
+    var engines = opts.engines;
+    var hasEngines = typeof engines !== 'undefined' && engines !== false;
+
+    if (hasCategory && hasEngines) {
+        throw new $TypeError('`exportsCategory` and `engines` are mutually exclusive.');
+    }
+
+    if (hasCategory) {
+        if (!isCategory(opts.exportsCategory)) {
+            var catError = new $TypeError('Invalid exports category: "' + opts.exportsCategory + '"');
+            catError.code = 'INVALID_EXPORTS_CATEGORY';
+            throw catError;
+        }
+        return opts.exportsCategory;
+    }
+
+    if (hasEngines) {
+        // engines: true means read from consumer's package.json
+        if (engines === true) {
+            return 'engines';
+        }
+
+        // engines must be a non-empty string (semver range)
+        if (typeof engines !== 'string' || engines === '') {
+            throw new $TypeError('`engines` must be `true`, `false`, or a non-empty string semver range.');
+        }
+
+        var categories = getCategoriesForRange(engines);
+        return selectMostRestrictive(categories);
+    }
+
+    return null;
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/homedir.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/homedir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/homedir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,31 @@
+'use strict';
+
+var os = require('os');
+
+// adapted from https://github.com/sindresorhus/os-homedir/blob/11e089f4754db38bb535e5a8416320c4446e8cfd/index.js
+
+module.exports = os.homedir || function homedir() {
+    var home = process.env.HOME;
+    var user = process.env.LOGNAME || process.env.USER || process.env.LNAME || process.env.USERNAME;
+
+    if (process.platform === 'win32') {
+        return process.env.USERPROFILE
+            || (
+                process.env.HOMEDRIVE
+                && process.env.HOMEPATH
+                && (process.env.HOMEDRIVE + process.env.HOMEPATH)
+            )
+            || home
+            || null;
+    }
+
+    if (process.platform === 'darwin') {
+        return home || (user ? '/Users/' + user : null);
+    }
+
+    if (process.platform === 'linux') {
+        return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null));
+    }
+
+    return home || null;
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/node-modules-paths.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/node-modules-paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/node-modules-paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,45 @@
+var path = require('path');
+var parse = path.parse || require('path-parse'); // eslint-disable-line global-require
+
+var driveLetterRegex = /^([A-Za-z]:)/;
+var uncPathRegex = /^\\\\/;
+
+function getNodeModulesDirs(absoluteStart, modules) {
+    var prefix = '/';
+    if (driveLetterRegex.test(absoluteStart)) {
+        prefix = '';
+    } else if (uncPathRegex.test(absoluteStart)) {
+        prefix = '\\\\';
+    }
+
+    var paths = [absoluteStart];
+    var parsed = parse(absoluteStart);
+    while (parsed.dir !== paths[paths.length - 1]) {
+        paths.push(parsed.dir);
+        parsed = parse(parsed.dir);
+    }
+
+    return paths.reduce(function (dirs, aPath) {
+        return dirs.concat(modules.map(function (moduleDir) {
+            return path.resolve(prefix, aPath, moduleDir);
+        }));
+    }, []);
+}
+
+module.exports = function nodeModulesPaths(start, opts, request) {
+    var modules = opts && opts.moduleDirectory
+        ? [].concat(opts.moduleDirectory)
+        : ['node_modules'];
+
+    if (opts && typeof opts.paths === 'function') {
+        return opts.paths(
+            request,
+            start,
+            function () { return getNodeModulesDirs(start, modules); },
+            opts
+        );
+    }
+
+    var dirs = getNodeModulesDirs(start, modules);
+    return opts && opts.paths ? dirs.concat(opts.paths) : dirs;
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/normalize-options.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/normalize-options.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/normalize-options.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,10 @@
+module.exports = function (x, opts) {
+    /**
+     * This file is purposefully a passthrough. It's expected that third-party
+     * environments will override it at runtime in order to inject special logic
+     * into `resolve` (by manipulating the options). One such example is the PnP
+     * code path in Yarn.
+     */
+
+    return opts || {};
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/parse-package-specifier.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/parse-package-specifier.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/parse-package-specifier.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,31 @@
+'use strict';
+
+/** @type {(x: string) => { __proto__: null, name: string, subpath: string }} */
+module.exports = function parsePackageSpecifier(x) {
+    if (x.charAt(0) === '@') {
+        var slashIndex = x.indexOf('/');
+        if (slashIndex === -1) {
+            return {
+                __proto__: null, name: x, subpath: '.'
+            };
+        }
+        var secondSlash = x.indexOf('/', slashIndex + 1);
+        if (secondSlash === -1) {
+            return {
+                __proto__: null, name: x, subpath: '.'
+            };
+        }
+        return {
+            __proto__: null, name: x.slice(0, secondSlash), subpath: '.' + x.slice(secondSlash)
+        };
+    }
+    var firstSlash = x.indexOf('/');
+    if (firstSlash === -1) {
+        return {
+            __proto__: null, name: x, subpath: '.'
+        };
+    }
+    return {
+        __proto__: null, name: x.slice(0, firstSlash), subpath: '.' + x.slice(firstSlash)
+    };
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/select-most-restrictive.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/select-most-restrictive.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/select-most-restrictive.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,42 @@
+'use strict';
+
+// Category ranking from most restrictive (lowest rank) to least restrictive (highest rank)
+// Lower rank = more restrictive = fewer features supported
+var categoryRank = /** @type {const} */ {
+    __proto__: null,
+    'pre-exports': /** @type {const} */ (0),
+    broken: /** @type {const} */ (1),
+    experimental: /** @type {const} */ (2),
+    conditions: /** @type {const} */ (3),
+    'broken-dir-slash-conditions': /** @type {const} */ (4),
+    patterns: /** @type {const} */ (5),
+    'pattern-trailers': /** @type {const} */ (6),
+    'pattern-trailers+json-imports': /** @type {const} */ (7),
+    'pattern-trailers-no-dir-slash': /** @type {const} */ (8),
+    'pattern-trailers-no-dir-slash+json-imports': /** @type {const} */ (9),
+    'require-esm': /** @type {const} */ (10),
+    'strips-types': /** @type {const} */ (11),
+    'subpath-imports-slash': /** @type {const} */ (12)
+};
+
+// Select the most restrictive category from an array of categories
+/** @type {(categories?: ReturnType<import('node-exports-info/getCategory')>[]) => import('node-exports-info/getCategory').Category | null} */
+module.exports = function selectMostRestrictive(categories) {
+    if (!categories || categories.length === 0) {
+        return null;
+    }
+
+    var mostRestrictive = null;
+    var lowestRank = Infinity;
+
+    for (var i = 0; i < categories.length; i++) {
+        var cat = categories[i];
+        var rank = categoryRank[cat];
+        if (typeof rank === 'number' && rank < lowestRank) {
+            lowestRank = rank;
+            mostRestrictive = cat;
+        }
+    }
+
+    return mostRestrictive;
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+import type resolve = require('../index.js');
+
+/**
+ * Synchronously resolve a module path, like `require.resolve()`, on behalf of files.
+ *
+ * @param id - The module identifier to resolve.
+ * @param options - Resolution options.
+ * @returns The resolved file path.
+ * @throws If the module cannot be found.
+ */
+declare function resolveSync(id: string, options?: resolve.SyncOptions): string;
+
+export = resolveSync;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,423 @@
+var isCore = require('is-core-module');
+var fs = require('fs');
+var path = require('path');
+var $Error = require('es-errors');
+var $TypeError = require('es-errors/type');
+
+var getHomedir = require('./homedir');
+var caller = require('./caller');
+var nodeModulesPaths = require('./node-modules-paths');
+var normalizeOptions = require('./normalize-options');
+var resolveExports = require('./exports-resolve');
+var parsePackageSpecifier = require('./parse-package-specifier');
+var getExportsCategory = require('./get-exports-category');
+var getCategoryInfo = require('node-exports-info/getCategoryInfo');
+var getCategoriesForRange = require('node-exports-info/getCategoriesForRange');
+var selectMostRestrictive = require('./select-most-restrictive');
+
+var realpathFS = process.platform !== 'win32' && fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync;
+
+var relativePathRegex = /^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/;
+var windowsDriveRegex = /^\w:[/\\]*$/;
+var nodeModulesRegex = /[/\\]node_modules[/\\]*$/;
+
+var homedir = getHomedir();
+function defaultPaths() {
+    if (!homedir) return [];
+    return [
+        path.join(homedir, '.node_modules'),
+        path.join(homedir, '.node_libraries')
+    ];
+}
+
+var defaultIsFile = function isFile(file) {
+    try {
+        var stat = fs.statSync(file, { throwIfNoEntry: false });
+    } catch (e) {
+        if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
+        throw e;
+    }
+    return !!stat && (stat.isFile() || stat.isFIFO());
+};
+
+var defaultIsDir = function isDirectory(dir) {
+    try {
+        var stat = fs.statSync(dir, { throwIfNoEntry: false });
+    } catch (e) {
+        if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
+        throw e;
+    }
+    return !!stat && stat.isDirectory();
+};
+
+var defaultRealpathSync = function realpathSync(x) {
+    try {
+        return realpathFS(x);
+    } catch (realpathErr) {
+        if (realpathErr.code !== 'ENOENT') {
+            throw realpathErr;
+        }
+    }
+    return x;
+};
+
+function maybeRealpathSync(realpathSync, x, opts) {
+    if (!opts || !opts.preserveSymlinks) {
+        return realpathSync(x);
+    }
+    return x;
+}
+
+function defaultReadPackageSync(readFileSync, pkgfile) {
+    return JSON.parse(readFileSync(pkgfile));
+}
+
+function getPackageCandidates(x, start, opts) {
+    var dirs = nodeModulesPaths(start, opts, x);
+    for (var i = 0; i < dirs.length; i++) {
+        dirs[i] = path.join(dirs[i], x);
+    }
+    return dirs;
+}
+
+function findConsumerPackageSync(startDir, isFile, readPackageSync, readFileSync) {
+    var dir = path.resolve(startDir);
+    while (true) {
+        var pkgfile = path.join(dir, 'package.json');
+        if (isFile(pkgfile)) {
+            try {
+                return readPackageSync(readFileSync, pkgfile);
+            } catch (e) {
+                if (!(e instanceof SyntaxError)) {
+                    throw e;
+                }
+            }
+        }
+        var parentDir = path.dirname(dir);
+        if (parentDir === dir) {
+            return null;
+        }
+        dir = parentDir;
+    }
+}
+
+function findPackageWithDirSync(startDir, isFile, readPackageSync, readFileSync) {
+    var dir = path.resolve(startDir);
+    while (true) {
+        // Stop at node_modules boundaries - can't self-reference across node_modules
+        if (nodeModulesRegex.test(dir)) {
+            return null;
+        }
+        var pkgfile = path.join(dir, 'package.json');
+        if (isFile(pkgfile)) {
+            try {
+                var pkg = readPackageSync(readFileSync, pkgfile);
+                return {
+                    __proto__: null, pkg: pkg, dir: dir
+                };
+            } catch (e) {
+                if (!(e instanceof SyntaxError)) {
+                    throw e;
+                }
+            }
+        }
+        var parentDir = path.dirname(dir);
+        if (parentDir === dir) {
+            return null;
+        }
+        dir = parentDir;
+    }
+}
+
+module.exports = function resolveSync(x, options) {
+    if (typeof x !== 'string') {
+        throw new $TypeError('Path must be a string.');
+    }
+    var opts = normalizeOptions(x, options);
+
+    var isFile = opts.isFile || defaultIsFile;
+    var isDirectory = opts.isDirectory || defaultIsDir;
+    var readFileSync = opts.readFileSync || fs.readFileSync;
+    var realpathSync = opts.realpathSync || defaultRealpathSync;
+    var readPackageSync = opts.readPackageSync || defaultReadPackageSync;
+    if (opts.readFileSync && opts.readPackageSync) {
+        throw new $TypeError('`readFileSync` and `readPackageSync` are mutually exclusive.');
+    }
+    var packageIterator = opts.packageIterator;
+
+    if (typeof opts.moduleSystem !== 'undefined' && opts.moduleSystem !== 'require' && opts.moduleSystem !== 'import') {
+        throw new $TypeError('`moduleSystem` must be `\'require\'` or `\'import\'`.');
+    }
+
+    var extensions = opts.extensions || ['.js'];
+    var includeCoreModules = opts.includeCoreModules !== false;
+    var basedir = opts.basedir || path.dirname(caller());
+    var parent = opts.filename || basedir;
+
+    opts.paths = opts.paths || defaultPaths();
+
+    // Determine exports category
+    var exportsCategory = getExportsCategory(opts);
+    if (exportsCategory === 'engines') {
+        // Read consumer's package.json to get engines.node
+        var consumerPkg = findConsumerPackageSync(basedir, isFile, readPackageSync, readFileSync);
+        if (consumerPkg && consumerPkg.engines && consumerPkg.engines.node) {
+            var categories = getCategoriesForRange(consumerPkg.engines.node);
+            exportsCategory = selectMostRestrictive(categories);
+        } else {
+            exportsCategory = null;
+        }
+    }
+
+    var useExports = exportsCategory !== null && exportsCategory !== 'pre-exports';
+
+    // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory
+    var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts);
+
+    if (opts.basedir && !isDirectory(absoluteStart)) {
+        var dirError = new $TypeError('Provided basedir "' + opts.basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory'));
+        dirError.code = 'INVALID_BASEDIR';
+        throw dirError;
+    }
+
+    if (relativePathRegex.test(x)) {
+        var res = path.resolve(absoluteStart, x);
+        if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
+        var m = loadAsFileSync(res) || loadAsDirectorySync(res);
+        if (m) return maybeRealpathSync(realpathSync, m, opts);
+    } else if (includeCoreModules && isCore(x)) {
+        return x;
+    } else if (useExports) {
+        // Try self-reference resolution first
+        var selfRef = resolveSelfReferenceSync(x, absoluteStart);
+        if (selfRef) return maybeRealpathSync(realpathSync, selfRef, opts);
+        var nE = loadNodeModulesWithExportsSync(x, absoluteStart);
+        if (nE) return maybeRealpathSync(realpathSync, nE, opts);
+    } else {
+        var n = loadNodeModulesSync(x, absoluteStart);
+        if (n) return maybeRealpathSync(realpathSync, n, opts);
+    }
+
+    var err = new $Error("Cannot find module '" + x + "' from '" + parent + "'");
+    err.code = 'MODULE_NOT_FOUND';
+    throw err;
+
+    function resolveSelfReferenceSync(x, startDir) {
+        var parsed = parsePackageSpecifier(x);
+        var pkgInfo = findPackageWithDirSync(startDir, isFile, readPackageSync, readFileSync);
+
+        if (!pkgInfo || !pkgInfo.pkg || pkgInfo.pkg.name !== parsed.name) {
+            return null; // Not a self-reference
+        }
+
+        var pkg = pkgInfo.pkg;
+        var pkgDir = pkgInfo.dir;
+
+        if (opts.packageFilter) {
+            pkg = opts.packageFilter(pkg, path.join(pkgDir, 'package.json'), pkgDir);
+        }
+
+        // If package has exports field, resolve via exports
+        if (typeof pkg.exports !== 'undefined') {
+            var categoryInfo = getCategoryInfo(exportsCategory, opts.moduleSystem || 'require');
+            var conditions = opts.conditions || categoryInfo.conditions;
+            var resolved = resolveExports(pkg.exports, parsed.subpath, conditions, categoryInfo.flags);
+            if (resolved) {
+                var resolvedPath = path.resolve(pkgDir, resolved);
+                if (isFile(resolvedPath)) {
+                    return resolvedPath;
+                }
+            }
+            // exports field exists but didn't resolve - this is an error per Node semantics
+            return undefined;
+        }
+
+        // No exports field - fall back to traditional resolution for self-reference
+        // (Note: this matches Node's behavior where self-ref without exports uses main)
+        if (parsed.subpath === '.') {
+            return loadAsDirectorySync(pkgDir);
+        }
+        var subPath = path.join(pkgDir, parsed.subpath.slice(1));
+        var sm = loadAsFileSync(subPath);
+        if (sm) return sm;
+        return loadAsDirectorySync(subPath);
+    }
+
+    function loadAsFileSync(x) {
+        var pkg = loadpkg(path.dirname(x));
+
+        if (pkg && pkg.dir && pkg.pkg && opts.pathFilter) {
+            var rfile = path.relative(pkg.dir, x);
+            var r = opts.pathFilter(pkg.pkg, x, rfile);
+            if (r) {
+                x = path.resolve(pkg.dir, r); // eslint-disable-line no-param-reassign
+            }
+        }
+
+        if (isFile(x)) {
+            return x;
+        }
+
+        for (var i = 0; i < extensions.length; i++) {
+            var file = x + extensions[i];
+            if (isFile(file)) {
+                return file;
+            }
+        }
+    }
+
+    function loadpkg(dir) {
+        if (dir === '' || dir === '/') return;
+        if (process.platform === 'win32' && windowsDriveRegex.test(dir)) {
+            return;
+        }
+        if (nodeModulesRegex.test(dir)) return;
+
+        var pkgfile = path.join(isDirectory(dir) ? maybeRealpathSync(realpathSync, dir, opts) : dir, 'package.json');
+
+        if (!isFile(pkgfile)) {
+            return loadpkg(path.dirname(dir));
+        }
+
+        var pkg;
+        try {
+            pkg = readPackageSync(readFileSync, pkgfile);
+        } catch (e) {
+            if (!(e instanceof SyntaxError)) {
+                throw e;
+            }
+        }
+
+        if (pkg && opts.packageFilter) {
+            pkg = opts.packageFilter(pkg, pkgfile, dir);
+        }
+
+        return { pkg: pkg, dir: dir };
+    }
+
+    function loadAsDirectorySync(x) {
+        var pkgfile = path.join(isDirectory(x) ? maybeRealpathSync(realpathSync, x, opts) : x, '/package.json');
+        if (isFile(pkgfile)) {
+            try {
+                var pkg = readPackageSync(readFileSync, pkgfile);
+            } catch (e) {}
+
+            if (pkg && opts.packageFilter) {
+                pkg = opts.packageFilter(pkg, pkgfile, x);
+            }
+
+            if (pkg && pkg.main) {
+                if (typeof pkg.main !== 'string') {
+                    var mainError = new $TypeError('package “' + pkg.name + '” `main` must be a string');
+                    mainError.code = 'INVALID_PACKAGE_MAIN';
+                    throw mainError;
+                }
+                if (pkg.main === '.' || pkg.main === './') {
+                    pkg.main = 'index';
+                }
+                try {
+                    var mainPath = path.resolve(x, pkg.main);
+                    var m = loadAsFileSync(mainPath);
+                    if (m) return m;
+                    var n = loadAsDirectorySync(mainPath);
+                    if (n) return n;
+                    var checkIndex = loadAsFileSync(path.resolve(x, 'index'));
+                    if (checkIndex) return checkIndex;
+                } catch (e) { }
+                var incorrectMainError = new $Error("Cannot find module '" + path.resolve(x, pkg.main) + "'. Please verify that the package.json has a valid \"main\" entry");
+                incorrectMainError.code = 'INCORRECT_PACKAGE_MAIN';
+                throw incorrectMainError;
+            }
+        }
+
+        return loadAsFileSync(path.join(x, '/index'));
+    }
+
+    function loadNodeModulesSync(x, start) {
+        var thunk = function () { return getPackageCandidates(x, start, opts); };
+        var dirs = packageIterator ? packageIterator(x, start, thunk, opts) : thunk();
+
+        for (var i = 0; i < dirs.length; i++) {
+            var dir = dirs[i];
+            if (isDirectory(path.dirname(dir))) {
+                var m = loadAsFileSync(dir);
+                if (m) return m;
+                var n = loadAsDirectorySync(dir);
+                if (n) return n;
+            }
+        }
+    }
+
+    function loadNodeModulesWithExportsSync(x, start) {
+        var parsed = parsePackageSpecifier(x);
+        var categoryInfo = getCategoryInfo(exportsCategory, opts.moduleSystem || 'require');
+        var conditions = opts.conditions || categoryInfo.conditions;
+
+        // Get candidate directories for the package name
+        var thunk = function () { return getPackageCandidates(parsed.name, start, opts); };
+        var dirs = packageIterator ? packageIterator(parsed.name, start, thunk, opts) : thunk();
+
+        for (var i = 0; i < dirs.length; i++) {
+            var pkgDir = dirs[i];
+            if (isDirectory(pkgDir)) {
+                var pkgfile = path.join(pkgDir, 'package.json');
+                if (isFile(pkgfile)) {
+                    var pkg;
+                    try {
+                        pkg = readPackageSync(readFileSync, pkgfile);
+                    } catch (e) {
+                        if (!(e instanceof SyntaxError)) {
+                            throw e;
+                        }
+                        pkg = null;
+                    }
+
+                    if (pkg) {
+                        if (opts.packageFilter) {
+                            pkg = opts.packageFilter(pkg, pkgfile, pkgDir);
+                        }
+
+                        // If package has exports field, use exports resolution
+                        if (typeof pkg.exports !== 'undefined') {
+                            var resolved = resolveExports(pkg.exports, parsed.subpath, conditions, categoryInfo.flags);
+                            if (resolved) {
+                                var resolvedPath = path.resolve(pkgDir, resolved);
+                                if (isFile(resolvedPath)) {
+                                    return resolvedPath;
+                                }
+                            }
+                            // exports field exists but didn't resolve - this is an error per Node semantics
+                            // (don't fall through to main/index)
+                            return undefined;
+                        }
+
+                        // No exports field, fall back to traditional resolution
+                        if (parsed.subpath === '.') {
+                            var result = loadAsDirectorySync(pkgDir);
+                            if (result) { return result; }
+                        } else {
+                            var subPath = path.join(pkgDir, parsed.subpath.slice(1));
+                            var sm = loadAsFileSync(subPath);
+                            if (sm) { return sm; }
+                            var sn = loadAsDirectorySync(subPath);
+                            if (sn) { return sn; }
+                        }
+                    }
+                } else if (parsed.subpath === '.') {
+                    // No package.json, fall back to file/directory resolution
+                    var m = loadAsFileSync(pkgDir);
+                    if (m) { return m; }
+                    var n = loadAsDirectorySync(pkgDir);
+                    if (n) { return n; }
+                } else {
+                    // No package.json, fall back to file/directory resolution for subpath
+                    var fullPath = path.join(pkgDir, parsed.subpath.slice(1));
+                    var m2 = loadAsFileSync(fullPath);
+                    if (m2) { return m2; }
+                    var n2 = loadAsDirectorySync(fullPath);
+                    if (n2) { return n2; }
+                }
+            }
+        }
+    }
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,99 @@
+{
+	"name": "resolve",
+	"description": "resolve like require.resolve() on behalf of files asynchronously and synchronously",
+	"version": "2.0.0-next.7",
+	"repository": {
+		"type": "git",
+		"url": "ssh://github.com/browserify/resolve.git"
+	},
+	"bin": {
+		"resolve": "./bin/resolve"
+	},
+	"main": "index.js",
+	"exports": {
+		".": [
+			{
+				"import": "./index.mjs",
+				"default": "./index.js"
+			},
+			"./index.js"
+		],
+		"./sync": "./lib/sync.js",
+		"./async": "./lib/async.js",
+		"./package.json": "./package.json"
+	},
+	"keywords": [
+		"resolve",
+		"require",
+		"node",
+		"module"
+	],
+	"scripts": {
+		"prepack": "npmignore --auto --commentLines=autogenerated",
+		"prepublishOnly": "safe-publish-latest",
+		"prepublish": "not-in-publish || npm run prepublishOnly",
+		"prelint": "eclint check $(git ls-files | grep -Ev test\\/list-exports$ | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')",
+		"lint": "eslint .",
+		"postlint": "tsc -p . && attw -P",
+		"pretests-only": "npm run submodule:update && cd ./test/resolver/nested_symlinks && node mylib/sync && node mylib/async",
+		"tests-only": "nyc tape test/*.js",
+		"pretest": "npm run lint",
+		"test": "npm run --silent tests-only",
+		"posttest": "npm run test:multirepo && npx npm@\">= 10.2\" audit --production",
+		"test:multirepo": "cd ./test/resolver/multirepo && npm install && npm test",
+		"submodule:update": "git submodule update --init --depth 1 && cd test/list-exports && git sparse-checkout init --cone && git sparse-checkout set packages/tests"
+	},
+	"license": "MIT",
+	"author": {
+		"name": "James Halliday",
+		"email": "mail@substack.net",
+		"url": "http://substack.net"
+	},
+	"funding": {
+		"url": "https://github.com/sponsors/ljharb"
+	},
+	"dependencies": {
+		"es-errors": "^1.3.0",
+		"is-core-module": "^2.16.2",
+		"node-exports-info": "^1.6.0",
+		"object-keys": "^1.1.1",
+		"path-parse": "^1.0.7",
+		"supports-preserve-symlinks-flag": "^1.0.0"
+	},
+	"devDependencies": {
+		"@arethetypeswrong/cli": "^0.18.2",
+		"@ljharb/eslint-config": "^22.2.3",
+		"@ljharb/tsconfig": "^0.3.2",
+		"@types/node": "^25.8.0",
+		"array.prototype.map": "^1.0.8",
+		"copy-dir": "^1.3.0",
+		"eclint": "^2.8.1",
+		"eslint": "^9.39.4",
+		"in-publish": "^2.0.1",
+		"jiti": "^0.0.0",
+		"mkdirp": "^0.5.6",
+		"mv": "^2.1.1",
+		"npmignore": "^0.3.5",
+		"nyc": "^10.3.2",
+		"rimraf": "^2.7.1",
+		"safe-publish-latest": "^2.0.0",
+		"tap": "^0.4.13",
+		"tape": "^5.9.0",
+		"tmp": "^0.0.31",
+		"typescript": "next"
+	},
+	"publishConfig": {
+		"ignore": [
+			".github/workflows",
+			".github/.well-known",
+			".gitmodules",
+			"appveyor.yml",
+			"CONTRIBUTING.md",
+			"test/resolver/malformed_package_json",
+			"test/list-exports"
+		]
+	},
+	"engines": {
+		"node": ">= 0.4"
+	}
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/readme.markdown
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/readme.markdown	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/readme.markdown	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,326 @@
+# resolve <sup>[![Version Badge][2]][1]</sup>
+
+implements the [node `require.resolve()` algorithm](https://nodejs.org/api/modules.html#modules_all_together) such that you can `require.resolve()` on behalf of a file asynchronously and synchronously
+
+[![github actions][actions-image]][actions-url]
+[![coverage][codecov-image]][codecov-url]
+[![License][license-image]][license-url]
+[![Downloads][downloads-image]][downloads-url]
+[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/10759/badge)](https://bestpractices.coreinfrastructure.org/projects/10759)
+
+[![npm badge][11]][1]
+
+# example
+
+asynchronously resolve:
+
+```js
+var resolve = require('resolve/async'); // or, require('resolve')
+resolve('tap', { basedir: __dirname }, function (err, res) {
+    if (err) console.error(err);
+    else console.log(res);
+});
+```
+
+```
+$ node example/async.js
+/home/substack/projects/node-resolve/node_modules/tap/lib/main.js
+```
+
+synchronously resolve:
+
+```js
+var resolve = require('resolve/sync'); // or, `require('resolve').sync
+var res = resolve('tap', { basedir: __dirname });
+console.log(res);
+```
+
+```
+$ node example/sync.js
+/home/substack/projects/node-resolve/node_modules/tap/lib/main.js
+```
+
+# methods
+
+```js
+var resolve = require('resolve');
+var async = require('resolve/async');
+var sync = require('resolve/sync');
+```
+
+For both the synchronous and asynchronous methods, errors may have any of the following `err.code` values:
+
+- `MODULE_NOT_FOUND`: the given path string (`id`) could not be resolved to a module
+- `INVALID_BASEDIR`: the specified `opts.basedir` doesn't exist, or is not a directory
+- `INVALID_PACKAGE_MAIN`: a `package.json` was encountered with an invalid `main` property (eg. not a string)
+- `ERR_PACKAGE_PATH_NOT_EXPORTED`: the requested subpath is not defined in the package's `exports` field
+- `ERR_INVALID_PACKAGE_CONFIG`: the package's `exports` field contains an invalid target
+- `INVALID_EXPORTS_CATEGORY`: an invalid `exportsCategory` was specified
+
+## resolve(id, opts={}, cb)
+
+Asynchronously resolve the module path string `id` into `cb(err, res [, pkg])`, where `pkg` (if defined) is the data from `package.json`.
+
+options are:
+
+* opts.basedir - directory to begin resolving from
+
+* opts.package - `package.json` data applicable to the module being loaded
+
+* opts.extensions - array of file extensions to search in order
+
+* opts.includeCoreModules - set to `false` to exclude node core modules (e.g. `fs`) from the search
+
+* opts.readFile - how to read files asynchronously
+
+* opts.isFile - function to asynchronously test whether a file exists
+
+* opts.isDirectory - function to asynchronously test whether a file exists and is a directory
+
+* opts.realpath - function to asynchronously resolve a potential symlink to its real path
+
+* `opts.readPackage(readFile, pkgfile, cb)` - function to asynchronously read and parse a package.json file
+  * readFile - the passed `opts.readFile` or `fs.readFile` if not specified
+  * pkgfile - path to package.json
+  * cb - callback. a SyntaxError error argument will be ignored, all other error arguments will be treated as an error.
+
+* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
+  * pkg - package data
+  * pkgfile - path to package.json
+  * dir - directory that contains package.json
+
+* `opts.pathFilter(pkg, path, relativePath)` - transform a path within a package
+  * pkg - package data
+  * path - the path being resolved
+  * relativePath - the path relative from the package.json location
+  * returns - a relative path that will be joined from the package.json location
+
+* opts.paths - require.paths array to use if nothing is found on the normal `node_modules` recursive walk (probably don't use this)
+
+  For advanced users, `paths` can also be a `opts.paths(request, start, opts)` function
+    * request - the import specifier being resolved
+    * start - lookup path
+    * getNodeModulesDirs - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
+    * opts - the resolution options
+
+* `opts.packageIterator(request, start, opts)` - return the list of candidate paths where the packages sources may be found (probably don't use this)
+    * request - the import specifier being resolved
+    * start - lookup path
+    * getPackageCandidates - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
+    * opts - the resolution options
+
+* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
+
+* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving.
+This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag.
+
+* opts.exportsCategory - a [node-exports-info](https://npmjs.com/package/node-exports-info) category string (e.g. `'conditions'`, `'patterns'`, `'pre-exports'`) that determines which `exports` field semantics to use.
+When set, resolution will use the package's `exports` field according to that category's supported conditions and features.
+This also enables **self-reference** support, allowing a package to import itself by name (e.g., `require('my-package')` from within `my-package`).
+
+* opts.engines - determines `exports` field resolution based on Node.js version semantics:
+  * When a **string** (e.g. `'>= 14'`): treated as a semver range, mapped to the most restrictive [node-exports-info](https://npmjs.com/package/node-exports-info) category covering that range.
+  * When `true`: reads `engines.node` from the nearest `package.json` to `basedir` and uses the most restrictive category for that range.
+  * When `false` or omitted: no engine-based exports resolution.
+  * Throws if set to an empty string or non-boolean/non-string value.
+
+**Note:** `exportsCategory` and `engines` are mutually exclusive - only one can be specified.
+
+* opts.moduleSystem - either `'require'` or `'import'`. Determines which module system conditions are used when resolving the `exports` field. When `'require'` (the default), the conditions include `require`; when `'import'`, the conditions include `import` instead. This option only has effect when `exportsCategory` or `engines` is also set. For example, given a package with `exports: { ".": { "import": "./esm.js", "require": "./cjs.js" } }`, resolving with `moduleSystem: 'import'` returns `esm.js`, while `moduleSystem: 'require'` (or omitting it) returns `cjs.js`.
+
+* opts.conditions - an array of condition strings (e.g. `['require', 'node']`) to use when resolving the `exports` field.
+If specified, this overrides the conditions that would otherwise be derived from the category (including those from `moduleSystem`).
+This option only has effect when `exportsCategory` or `engines` is also set.
+
+default `opts` values:
+
+```js
+{
+    paths: [],
+    basedir: __dirname,
+    extensions: ['.js'],
+    includeCoreModules: true,
+    readFile: fs.readFile,
+    isFile: function isFile(file, cb) {
+        fs.stat(file, function (err, stat) {
+            if (!err) {
+                return cb(null, stat.isFile() || stat.isFIFO());
+            }
+            if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
+            return cb(err);
+        });
+    },
+    isDirectory: function isDirectory(dir, cb) {
+        fs.stat(dir, function (err, stat) {
+            if (!err) {
+                return cb(null, stat.isDirectory());
+            }
+            if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
+            return cb(err);
+        });
+    },
+    realpath: function realpath(file, cb) {
+        var realpath = typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath;
+        realpath(file, function (realPathErr, realPath) {
+            if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr);
+            else cb(null, realPathErr ? file : realPath);
+        });
+    },
+    readPackage: function defaultReadPackage(readFile, pkgfile, cb) {
+        readFile(pkgfile, function (readFileErr, body) {
+            if (readFileErr) cb(readFileErr);
+            else {
+                try {
+                    var pkg = JSON.parse(body);
+                    cb(null, pkg);
+                } catch (jsonErr) {
+                    cb(jsonErr);
+                }
+            }
+        });
+    },
+    moduleDirectory: 'node_modules',
+    preserveSymlinks: false
+}
+```
+
+## resolve.sync(id, opts)
+
+Synchronously resolve the module path string `id`, returning the result and
+throwing an error when `id` can't be resolved.
+
+options are:
+
+* opts.basedir - directory to begin resolving from
+
+* opts.extensions - array of file extensions to search in order
+
+* opts.includeCoreModules - set to `false` to exclude node core modules (e.g. `fs`) from the search
+
+* opts.readFileSync - how to read files synchronously
+
+* opts.isFile - function to synchronously test whether a file exists
+
+* opts.isDirectory - function to synchronously test whether a file exists and is a directory
+
+* opts.realpathSync - function to synchronously resolve a potential symlink to its real path
+
+* `opts.readPackageSync(readFileSync, pkgfile)` - function to synchronously read and parse a package.json file. a thrown SyntaxError will be ignored, all other exceptions will propagate.
+  * readFileSync - the passed `opts.readFileSync` or `fs.readFileSync` if not specified
+  * pkgfile - path to package.json
+
+* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field
+  * pkg - package data
+  * pkgfile - path to package.json
+  * dir - directory that contains package.json
+
+* `opts.pathFilter(pkg, path, relativePath)` - transform a path within a package
+  * pkg - package data
+  * path - the path being resolved
+  * relativePath - the path relative from the package.json location
+  * returns - a relative path that will be joined from the package.json location
+
+* opts.paths - require.paths array to use if nothing is found on the normal `node_modules` recursive walk (probably don't use this)
+
+  For advanced users, `paths` can also be a `opts.paths(request, start, opts)` function
+    * request - the import specifier being resolved
+    * start - lookup path
+    * getNodeModulesDirs - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
+    * opts - the resolution options
+
+* `opts.packageIterator(request, start, opts)` - return the list of candidate paths where the packages sources may be found (probably don't use this)
+    * request - the import specifier being resolved
+    * start - lookup path
+    * getPackageCandidates - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution
+    * opts - the resolution options
+
+* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
+
+* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving.
+This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag.
+
+* opts.exportsCategory - a [node-exports-info](https://npmjs.com/package/node-exports-info) category string (e.g. `'conditions'`, `'patterns'`, `'pre-exports'`) that determines which `exports` field semantics to use. When set, resolution will use the package's `exports` field according to that category's supported conditions and features. This also enables **self-reference** support, allowing a package to import itself by name (e.g., `require('my-package')` from within `my-package`).
+
+* opts.enginesRange - a semver range string (e.g. `'>= 14'`) that will be mapped to the most restrictive [node-exports-info](https://npmjs.com/package/node-exports-info) category covering that range.
+
+* opts.engines - if `true`, reads `engines.node` from the nearest `package.json` to `basedir` and uses the most restrictive [node-exports-info](https://npmjs.com/package/node-exports-info) category for that range.
+
+**Note:** `exportsCategory`, `enginesRange`, and `engines` are mutually exclusive - only one can be specified.
+
+* opts.moduleSystem - either `'require'` or `'import'`. Determines which module system conditions are used when resolving the `exports` field. When `'require'` (the default), the conditions include `require`; when `'import'`, the conditions include `import` instead. This option only has effect when `exportsCategory` or `engines` is also set. For example, given a package with `exports: { ".": { "import": "./esm.js", "require": "./cjs.js" } }`, resolving with `moduleSystem: 'import'` returns `esm.js`, while `moduleSystem: 'require'` (or omitting it) returns `cjs.js`.
+
+* opts.conditions - an array of condition strings (e.g. `['require', 'node']`) to use when resolving the `exports` field. If specified, this overrides the conditions that would otherwise be derived from the category (including those from `moduleSystem`). This option only has effect when one of `exportsCategory`, `enginesRange`, or `engines` is also set.
+
+default `opts` values:
+
+```js
+{
+    paths: [],
+    basedir: __dirname,
+    extensions: ['.js'],
+    includeCoreModules: true,
+    readFileSync: fs.readFileSync,
+    isFile: function isFile(file) {
+        try {
+            var stat = fs.statSync(file);
+        } catch (e) {
+            if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
+            throw e;
+        }
+        return stat.isFile() || stat.isFIFO();
+    },
+    isDirectory: function isDirectory(dir) {
+        try {
+            var stat = fs.statSync(dir);
+        } catch (e) {
+            if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false;
+            throw e;
+        }
+        return stat.isDirectory();
+    },
+    realpathSync: function realpathSync(file) {
+        try {
+            var realpath = typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync;
+            return realpath(file);
+        } catch (realPathErr) {
+            if (realPathErr.code !== 'ENOENT') {
+                throw realPathErr;
+            }
+        }
+        return file;
+    },
+    readPackageSync: function defaultReadPackageSync(readFileSync, pkgfile) {
+        return JSON.parse(readFileSync(pkgfile));
+    },
+    moduleDirectory: 'node_modules',
+    preserveSymlinks: false
+}
+```
+
+# install
+
+With [npm](https://npmjs.org) do:
+
+```sh
+npm install resolve
+```
+
+# license
+
+MIT
+
+[1]: https://npmjs.org/package/resolve
+[2]: https://versionbadg.es/browserify/resolve.svg
+[5]: https://david-dm.org/browserify/resolve.svg
+[6]: https://david-dm.org/browserify/resolve
+[7]: https://david-dm.org/browserify/resolve/dev-status.svg
+[8]: https://david-dm.org/browserify/resolve#info=devDependencies
+[11]: https://nodei.co/npm/resolve.png?downloads=true&stars=true
+[license-image]: https://img.shields.io/npm/l/resolve.svg
+[license-url]: LICENSE
+[downloads-image]: https://img.shields.io/npm/dm/resolve.svg
+[downloads-url]: https://npm-stat.com/charts.html?package=resolve
+[codecov-image]: https://codecov.io/gh/browserify/resolve/branch/main/graphs/badge.svg
+[codecov-url]: https://app.codecov.io/gh/browserify/resolve/
+[actions-image]: https://img.shields.io/github/check-runs/browserify/resolve/main
+[actions-url]: https://github.com/browserify/resolve/actions
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/sync.d.ts
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/sync.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/sync.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+import type resolveSync = require('./lib/sync');
+
+export = resolveSync;
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./lib/sync');
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/default_paths.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/default_paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/default_paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,49 @@
+'use strict';
+
+var path = require('path');
+var test = require('tape');
+var mockProperty = require('mock-property');
+
+var homedirPath = require.resolve('../lib/homedir');
+var asyncPath = require.resolve('../async');
+var libAsyncPath = require.resolve('../lib/async');
+var syncPath = require.resolve('../sync');
+var libSyncPath = require.resolve('../lib/sync');
+
+function mockNullHomedir(t) {
+    t.teardown(mockProperty(require.cache, homedirPath, {
+        value: { id: homedirPath, filename: homedirPath, loaded: true, exports: function () { return null; } }
+    }));
+}
+
+test('async: null homedir does not throw', function (t) {
+    t.plan(2);
+
+    mockNullHomedir(t);
+    t.teardown(mockProperty(require.cache, asyncPath, { 'delete': true }));
+    t.teardown(mockProperty(require.cache, libAsyncPath, { 'delete': true }));
+
+    var resolve = require('../lib/async');
+
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./baz', { basedir: dir }, function (err, res) {
+        t.error(err, 'no error');
+        t.equal(res, path.join(dir, 'baz', 'quux.js'), 'resolves correctly with null homedir');
+    });
+});
+
+test('sync: null homedir does not throw', function (t) {
+    mockNullHomedir(t);
+    t.teardown(mockProperty(require.cache, syncPath, { 'delete': true }));
+    t.teardown(mockProperty(require.cache, libSyncPath, { 'delete': true }));
+
+    var resolveSync = require('../lib/sync');
+
+    var dir = path.join(__dirname, 'resolver');
+
+    var res = resolveSync('./baz', { basedir: dir });
+    t.equal(res, path.join(dir, 'baz', 'quux.js'), 'resolves correctly with null homedir');
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,29 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('dotdot', function (t) {
+    t.plan(4);
+    var dir = path.join(__dirname, '/dotdot/abc');
+
+    resolve('..', { basedir: dir }, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(__dirname, 'dotdot/index.js'));
+    });
+
+    resolve('.', { basedir: dir }, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, 'index.js'));
+    });
+});
+
+test('dotdot sync', function (t) {
+    t.plan(2);
+    var dir = path.join(__dirname, '/dotdot/abc');
+
+    var a = resolve.sync('..', { basedir: dir });
+    t.equal(a, path.join(__dirname, 'dotdot/index.js'));
+
+    var b = resolve.sync('.', { basedir: dir });
+    t.equal(b, path.join(dir, 'index.js'));
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/exports.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/exports.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/exports.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1015 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+var fixturesDir = path.join(__dirname, 'list-exports', 'packages', 'tests', 'fixtures');
+
+var categories = [
+    'broken',
+    'broken-dir-slash-conditions',
+    'conditions',
+    'experimental',
+    'pattern-trailers',
+    'pattern-trailers+json-imports',
+    'pattern-trailers-no-dir-slash',
+    'pattern-trailers-no-dir-slash+json-imports',
+    'patterns',
+    'require-esm',
+    'strips-types',
+    'subpath-imports-slash'
+    // 'pre-exports' is tested separately since it uses main/index resolution
+];
+
+// Fixtures that are symlinks pointing outside the fixture dir cause path confusion
+// ex-private is a private package whose expected files don't include exports data
+var skipFixtures = ['list-exports', 'ls-exports', 'ex-private'];
+
+function getFixtures() {
+    return fs.readdirSync(fixturesDir).filter(function (name) {
+        if (skipFixtures.indexOf(name) !== -1) {
+            return false;
+        }
+        var stat = fs.statSync(path.join(fixturesDir, name));
+        return stat.isDirectory();
+    });
+}
+
+function loadExpected(fixtureName, category) {
+    var expectedPath = path.join(fixturesDir, fixtureName, 'expected', category + '.json');
+    if (!fs.existsSync(expectedPath)) {
+        return null;
+    }
+    try {
+        return JSON.parse(fs.readFileSync(expectedPath, 'utf8'));
+    } catch (e) {
+        return null;
+    }
+}
+
+function loadProjectPkg(fixtureName) {
+    var pkgPath = path.join(fixturesDir, fixtureName, 'project', 'package.json');
+    try {
+        return JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
+    } catch (e) {
+        return null;
+    }
+}
+
+test('async exports resolution - exportsCategory option', function (t) {
+    var fixtures = getFixtures();
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (!expected || !expected.exports || !expected.exports[category]) {
+                return;
+            }
+
+            var requireMap = expected.exports[category].require;
+            if (!requireMap || typeof requireMap !== 'object') {
+                return;
+            }
+
+            Object.keys(requireMap).forEach(function (subpath) {
+                var expectedFile = requireMap[subpath];
+                var specifier = subpath === '.' ? pkgName : pkgName + subpath.substring(1);
+
+                t.test(fixtureName + ' / ' + category + ' / ' + subpath, function (st) {
+                    st.plan(1);
+                    resolve(specifier, {
+                        basedir: __dirname,
+                        exportsCategory: category,
+                        extensions: ['.js', '.json'],
+                        packageIterator: function () {
+                            return [projectDir];
+                        }
+                    }, function (err, result) {
+                        if (err) {
+                            st.fail('Unexpected error for ' + specifier + ': ' + err.message);
+                            return;
+                        }
+                        var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                        st.equal(relativeResult, expectedFile, specifier + ' resolves to ' + expectedFile);
+                    });
+                });
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - pre-exports category uses main/index', function (t) {
+    var fixtures = getFixtures();
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        var expected = loadExpected(fixtureName, 'pre-exports');
+        if (!expected || !expected.exports || !expected.exports['pre-exports']) {
+            return;
+        }
+
+        var requireMap = expected.exports['pre-exports'].require;
+        if (!requireMap || typeof requireMap !== 'object') {
+            return;
+        }
+
+        // For pre-exports, only test the main entry point (.)
+        var mainEntry = requireMap['.'];
+        if (!mainEntry) {
+            return;
+        }
+
+        t.test(fixtureName + ' / pre-exports / .', function (st) {
+            st.plan(1);
+            resolve(pkgName, {
+                basedir: __dirname,
+                exportsCategory: 'pre-exports',
+                extensions: ['.js', '.json'],
+                packageIterator: function () {
+                    return [projectDir];
+                }
+            }, function (err, result) {
+                if (err) {
+                    st.fail('Unexpected error for ' + pkgName + ': ' + err.message);
+                    return;
+                }
+                var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                st.equal(relativeResult, mainEntry, pkgName + ' resolves to ' + mainEntry);
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - mutual exclusivity of options', function (t) {
+    t.test('exportsCategory and engines (string) are mutually exclusive', function (st) {
+        st.plan(1);
+        resolve('tape', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            engines: '>= 14'
+        }, function (err) {
+            st.ok(err && (/mutually exclusive/).test(err.message), 'throws with mutually exclusive message');
+        });
+    });
+
+    t.test('exportsCategory and engines (true) are mutually exclusive', function (st) {
+        st.plan(1);
+        resolve('tape', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            engines: true
+        }, function (err) {
+            st.ok(err && (/mutually exclusive/).test(err.message), 'throws with mutually exclusive message');
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - invalid category', function (t) {
+    t.plan(2);
+    resolve('tape', {
+        basedir: __dirname,
+        exportsCategory: 'not-a-real-category'
+    }, function (err) {
+        t.equal(err && err.code, 'INVALID_EXPORTS_CATEGORY', 'has correct error code');
+        t.ok(err && (/Invalid exports category/).test(err.message), 'has correct error message');
+    });
+});
+
+test('async exports resolution - engines option', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-exports-string', 'project');
+
+    t.test('engines string maps to category', function (st) {
+        st.plan(1);
+        resolve('ex-exports-string', {
+            basedir: __dirname,
+            engines: '>= 14',
+            packageIterator: function () {
+                return [projectDir];
+            }
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('index.js') > -1, 'resolves to index.js');
+        });
+    });
+
+    t.test('engines: false is same as omitting', function (st) {
+        st.plan(1);
+        resolve('tape', {
+            basedir: __dirname,
+            engines: false
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('tape') > -1, 'resolves without exports resolution');
+        });
+    });
+
+    t.test('engines: empty string throws', function (st) {
+        st.plan(1);
+        resolve('tape', {
+            basedir: __dirname,
+            engines: ''
+        }, function (err) {
+            st.ok(err && (/must be.*true.*false.*non-empty string/i).test(err.message), 'throws with correct message');
+        });
+    });
+
+    t.test('engines: number throws', function (st) {
+        st.plan(1);
+        resolve('tape', {
+            basedir: __dirname,
+            engines: 14
+        }, function (err) {
+            st.ok(err && (/must be.*true.*false.*non-empty string/i).test(err.message), 'throws with correct message');
+        });
+    });
+
+    t.test('engines: object throws', function (st) {
+        st.plan(1);
+        resolve('tape', {
+            basedir: __dirname,
+            engines: { node: '>= 14' }
+        }, function (err) {
+            st.ok(err && (/must be.*true.*false.*non-empty string/i).test(err.message), 'throws with correct message');
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - conditions override', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-conditions', 'project');
+
+    t.test('default category conditions resolve to require.js', function (st) {
+        st.plan(1);
+        resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            packageIterator: function () {
+                return [projectDir];
+            }
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('require.js') > -1, 'resolves to require.js with default conditions');
+        });
+    });
+
+    t.test('conditions override to [default] resolves to default.js', function (st) {
+        st.plan(1);
+        resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            conditions: ['default'],
+            packageIterator: function () {
+                return [projectDir];
+            }
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('default.js') > -1, 'resolves to default.js with conditions override');
+        });
+    });
+
+    t.test('conditions override to [node] resolves to node.js', function (st) {
+        st.plan(1);
+        resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            conditions: ['node'],
+            packageIterator: function () {
+                return [projectDir];
+            }
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('node.js') > -1, 'resolves to node.js with conditions override');
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - subpath not exported throws', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-exports-string', 'project');
+
+    t.plan(2);
+    resolve('ex-exports-string/not-exported', {
+        basedir: __dirname,
+        exportsCategory: 'conditions',
+        packageIterator: function () {
+            return [projectDir];
+        }
+    }, function (err) {
+        t.equal(err && err.code, 'ERR_PACKAGE_PATH_NOT_EXPORTED', 'has correct error code');
+        t.ok(err && (/not defined by "exports"/).test(err.message), 'has correct error message');
+    });
+});
+
+test('async existing resolution without exports options still works', function (t) {
+    t.plan(1);
+    resolve('tape', { basedir: __dirname }, function (err, result) {
+        if (err) {
+            t.fail(err.message);
+            return;
+        }
+        t.ok(result.indexOf('tape') > -1, 'resolves tape without exports options');
+    });
+});
+
+test('all fixtures are tested', function (t) {
+    var fixtures = getFixtures();
+    var testedFixtures = [];
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            t.fail('Fixture ' + fixtureName + ' has no loadable package.json');
+            return;
+        }
+
+        var hasAnyTests = false;
+
+        // Check if at least one category has expected results
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (expected && expected.exports && expected.exports[category]) {
+                var requireMap = expected.exports[category].require;
+                if (requireMap && typeof requireMap === 'object' && Object.keys(requireMap).length > 0) {
+                    hasAnyTests = true;
+                }
+            }
+        });
+
+        // Also check pre-exports
+        var preExpected = loadExpected(fixtureName, 'pre-exports');
+        if (preExpected && preExpected.exports && preExpected.exports['pre-exports']) {
+            var preRequireMap = preExpected.exports['pre-exports'].require;
+            if (preRequireMap && preRequireMap['.']) {
+                hasAnyTests = true;
+            }
+        }
+
+        if (hasAnyTests) {
+            testedFixtures.push(fixtureName);
+        } else {
+            t.fail('Fixture ' + fixtureName + ' has no testable entrypoints');
+        }
+    });
+
+    t.ok(testedFixtures.length > 0, 'At least one fixture is tested');
+    t.equal(testedFixtures.length, fixtures.length, 'All ' + fixtures.length + ' fixtures have testable entrypoints');
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem: import uses import conditions', function (t) {
+    // ex-node-addons is skipped because getCategoryInfo includes node-addons in import
+    // conditions for some categories, but list-exports expected data does not
+    var skipImportFixtures = ['ex-node-addons'];
+    var fixtures = getFixtures().filter(function (n) { return skipImportFixtures.indexOf(n) === -1; });
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (!expected || !expected.exports || !expected.exports[category]) {
+                return;
+            }
+
+            var importMap = expected.exports[category].import;
+            if (!importMap || typeof importMap !== 'object') {
+                return;
+            }
+
+            Object.keys(importMap).forEach(function (subpath) {
+                var expectedFile = importMap[subpath];
+                var specifier = subpath === '.' ? pkgName : pkgName + subpath.substring(1);
+
+                t.test(fixtureName + ' / ' + category + ' / import / ' + subpath, function (st) {
+                    st.plan(1);
+                    resolve(specifier, {
+                        basedir: __dirname,
+                        exportsCategory: category,
+                        moduleSystem: 'import',
+                        extensions: ['.js', '.json', '.mjs'],
+                        packageIterator: function () {
+                            return [projectDir];
+                        }
+                    }, function (err, result) {
+                        if (err) {
+                            st.fail('Unexpected error for ' + specifier + ' with moduleSystem:import: ' + err.message);
+                            return;
+                        }
+                        var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                        st.equal(relativeResult, expectedFile, specifier + ' with moduleSystem:import resolves to ' + expectedFile);
+                    });
+                });
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem: require matches default behavior', function (t) {
+    var fixtures = getFixtures();
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (!expected || !expected.exports || !expected.exports[category]) {
+                return;
+            }
+
+            var requireMap = expected.exports[category].require;
+            if (!requireMap || typeof requireMap !== 'object') {
+                return;
+            }
+
+            Object.keys(requireMap).forEach(function (subpath) {
+                var expectedFile = requireMap[subpath];
+                var specifier = subpath === '.' ? pkgName : pkgName + subpath.substring(1);
+
+                t.test(fixtureName + ' / ' + category + ' / explicit require / ' + subpath, function (st) {
+                    st.plan(1);
+                    resolve(specifier, {
+                        basedir: __dirname,
+                        exportsCategory: category,
+                        moduleSystem: 'require',
+                        extensions: ['.js', '.json'],
+                        packageIterator: function () {
+                            return [projectDir];
+                        }
+                    }, function (err, result) {
+                        if (err) {
+                            st.fail('Unexpected error for ' + specifier + ' with moduleSystem:require: ' + err.message);
+                            return;
+                        }
+                        var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                        st.equal(relativeResult, expectedFile, specifier + ' with moduleSystem:require resolves to ' + expectedFile);
+                    });
+                });
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem import vs require produce different results', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-conditions', 'project');
+
+    t.test('./idnr: import resolves to import.mjs, require resolves to default.js', function (st) {
+        st.plan(2);
+        resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('import.mjs') > -1, 'moduleSystem:import resolves to import.mjs');
+
+            resolve('ex-conditions/idnr', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('default.js') > -1, 'moduleSystem:require resolves to default.js');
+            });
+        });
+    });
+
+    t.test('./rdni: import resolves to default.js, require resolves to require.js', function (st) {
+        st.plan(2);
+        resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('default.js') > -1, 'moduleSystem:import resolves to default.js');
+
+            resolve('ex-conditions/rdni', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('require.js') > -1, 'moduleSystem:require resolves to require.js');
+            });
+        });
+    });
+
+    t.test('./indr: import resolves to import.mjs, require resolves to node.js', function (st) {
+        st.plan(2);
+        resolve('ex-conditions/indr', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('import.mjs') > -1, 'moduleSystem:import resolves to import.mjs');
+
+            resolve('ex-conditions/indr', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('node.js') > -1, 'moduleSystem:require resolves to node.js');
+            });
+        });
+    });
+
+    t.test('./irdn: import resolves to import.mjs, require resolves to require.js', function (st) {
+        st.plan(2);
+        resolve('ex-conditions/irdn', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('import.mjs') > -1, 'moduleSystem:import resolves to import.mjs');
+
+            resolve('ex-conditions/irdn', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('require.js') > -1, 'moduleSystem:require resolves to require.js');
+            });
+        });
+    });
+
+    t.test('default (no moduleSystem) matches require behavior', function (st) {
+        st.plan(2);
+        resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, defaultResult) {
+            if (err) { return st.fail(err.message); }
+            resolve('ex-conditions/rdni', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.equal(defaultResult, requireResult, 'default and explicit require produce the same result');
+                st.ok(defaultResult.indexOf('require.js') > -1, 'both resolve to require.js');
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem import with various fixtures', function (t) {
+    t.test('ex-conditions-in-folder: import resolves to mjs, require to cjs', function (st) {
+        var projectDir = path.join(fixturesDir, 'ex-conditions-in-folder', 'project');
+        st.plan(2);
+        resolve('ex-conditions-in-folder', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('mjs/index.mjs') > -1, 'import resolves to mjs/index.mjs');
+
+            resolve('ex-conditions-in-folder', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('cjs/index.js') > -1, 'require resolves to cjs/index.js');
+            });
+        });
+    });
+
+    t.test('ex-exports-TL-object: import resolves to index.mjs, require to file.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'ex-exports-TL-object', 'project');
+        st.plan(2);
+        resolve('ex-exports-TL-object', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('index.mjs') > -1, 'import resolves to index.mjs');
+
+            resolve('ex-exports-TL-object', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('file.js') > -1, 'require resolves to file.js');
+            });
+        });
+    });
+
+    t.test('flatted-3: import resolves to esm/index.js, require to cjs/index.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'flatted-3', 'project');
+        st.plan(2);
+        resolve('flatted', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('esm/index.js') > -1, 'import resolves to esm/index.js');
+
+            resolve('flatted', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('cjs/index.js') > -1, 'require resolves to cjs/index.js');
+            });
+        });
+    });
+
+    t.test('is-promise-2.2.1: import resolves to index.mjs, require to index.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'is-promise-2.2.1', 'project');
+        st.plan(2);
+        resolve('is-promise', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('index.mjs') > -1, 'import resolves to index.mjs');
+
+            resolve('is-promise', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('index.js') > -1 && requireResult.indexOf('index.mjs') === -1, 'require resolves to index.js');
+            });
+        });
+    });
+
+    t.test('resolve-2: import resolves to index.mjs, require to index.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'resolve-2', 'project');
+        st.plan(2);
+        resolve('resolve', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('index.mjs') > -1, 'import resolves to index.mjs');
+
+            resolve('resolve', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('index.js') > -1 && requireResult.indexOf('index.mjs') === -1, 'require resolves to index.js');
+            });
+        });
+    });
+
+    t.test('preact: import resolves to .mjs, require to .js', function (st) {
+        var projectDir = path.join(fixturesDir, 'preact', 'project');
+        st.plan(2);
+        resolve('preact', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('preact.mjs') > -1, 'import resolves to preact.mjs');
+
+            resolve('preact', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs'],
+                packageIterator: function () { return [projectDir]; }
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('preact.js') > -1 && requireResult.indexOf('preact.mjs') === -1, 'require resolves to preact.js');
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem import with self-reference', function (t) {
+    t.test('self-reference with moduleSystem:import uses import conditions', function (st) {
+        var conditionsDir = path.join(fixturesDir, 'ex-conditions', 'project');
+        st.plan(2);
+        resolve('ex-conditions/idnr', {
+            basedir: conditionsDir,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs']
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('import.mjs') > -1, 'self-reference with import resolves to import.mjs');
+
+            resolve('ex-conditions/idnr', {
+                basedir: conditionsDir,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs']
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('default.js') > -1, 'self-reference with require resolves to default.js');
+            });
+        });
+    });
+
+    t.test('self-reference with moduleSystem:import on TL-object package', function (st) {
+        var projectDir = path.join(fixturesDir, 'ex-exports-TL-object', 'project');
+        st.plan(2);
+        resolve('ex-exports-TL-object', {
+            basedir: projectDir,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs']
+        }, function (err, importResult) {
+            if (err) { return st.fail(err.message); }
+            st.ok(importResult.indexOf('index.mjs') > -1, 'self-ref import resolves to index.mjs');
+
+            resolve('ex-exports-TL-object', {
+                basedir: projectDir,
+                exportsCategory: 'conditions',
+                moduleSystem: 'require',
+                extensions: ['.js', '.mjs']
+            }, function (err2, requireResult) {
+                if (err2) { return st.fail(err2.message); }
+                st.ok(requireResult.indexOf('file.js') > -1, 'self-ref require resolves to file.js');
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem with engines option', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-conditions', 'project');
+
+    t.test('moduleSystem:import works with engines string', function (st) {
+        st.plan(1);
+        resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            engines: '>= 14',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, result) {
+            if (err) { return st.fail(err.message); }
+            st.ok(result.indexOf('import.mjs') > -1, 'engines + moduleSystem:import resolves to import.mjs');
+        });
+    });
+
+    t.test('moduleSystem:require works with engines string', function (st) {
+        st.plan(1);
+        resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            engines: '>= 14',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        }, function (err, result) {
+            if (err) { return st.fail(err.message); }
+            st.ok(result.indexOf('default.js') > -1, 'engines + moduleSystem:require resolves to default.js');
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - invalid moduleSystem errors', function (t) {
+    t.test('moduleSystem: true errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: true }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: false errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: false }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: empty string errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: '' }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: number errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: 42 }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: random string errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: 'cjs' }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: object errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: {} }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: null errors', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: null }, function (err) {
+            st.ok(err && (/moduleSystem/).test(err.message), 'errors with moduleSystem message: ' + (err && err.message));
+        });
+    });
+
+    t.test('moduleSystem: undefined does not error', function (st) {
+        st.plan(1);
+        resolve('tape', { basedir: __dirname, moduleSystem: undefined }, function (err, result) {
+            if (err) { return st.fail(err.message); }
+            st.ok(result.indexOf('tape') > -1, 'undefined moduleSystem resolves normally');
+        });
+    });
+
+    t.end();
+});
+
+test('async exports resolution - moduleSystem does not affect resolution without exports', function (t) {
+    t.plan(1);
+    resolve('tape', {
+        basedir: __dirname,
+        moduleSystem: 'import'
+    }, function (err, result) {
+        if (err) { return t.fail(err.message); }
+        t.ok(result.indexOf('tape') > -1, 'moduleSystem is ignored when no exports options are set');
+    });
+});
+
+test('async exports resolution - self-reference', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-exports-string', 'project');
+
+    t.test('self-reference resolves via exports when inside package', function (st) {
+        st.plan(1);
+        // basedir is inside the package, specifier is the package name
+        resolve('ex-exports-string', {
+            basedir: projectDir,
+            exportsCategory: 'conditions'
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('index.js') > -1, 'self-reference resolves to index.js via exports');
+        });
+    });
+
+    t.test('self-reference with subpath resolves via exports', function (st) {
+        var conditionsDir = path.join(fixturesDir, 'ex-conditions', 'project');
+        st.plan(1);
+        resolve('ex-conditions/rdni', {
+            basedir: conditionsDir,
+            exportsCategory: 'conditions'
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            st.ok(result.indexOf('require.js') > -1, 'self-reference subpath resolves correctly');
+        });
+    });
+
+    t.test('self-reference without exports falls back to main', function (st) {
+        var mainDotlessDir = path.join(fixturesDir, 'ex-main-dotless', 'project');
+        st.plan(1);
+        resolve('ex-main-dotless', {
+            basedir: mainDotlessDir,
+            exportsCategory: 'conditions'
+        }, function (err, result) {
+            // If it throws, that's also acceptable behavior (no exports means not exported)
+            st.ok(!err || result, 'self-reference without exports throws or resolves');
+        });
+    });
+
+    t.test('self-reference does not cross node_modules boundary', function (st) {
+        // basedir is inside node_modules, should NOT self-reference parent package
+        var nodeModulesDir = path.join(__dirname, '..', 'node_modules', 'tape');
+        st.plan(1);
+        resolve('resolve', {
+            basedir: nodeModulesDir,
+            exportsCategory: 'conditions'
+        }, function (err, result) {
+            if (err) {
+                st.fail(err.message);
+                return;
+            }
+            // The result should be from node_modules, not from a self-reference
+            st.ok(result.indexOf('node_modules') > -1 || result === 'resolve', 'does not self-reference across node_modules');
+        });
+    });
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/exports_sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/exports_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/exports_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,955 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var test = require('tape');
+var resolve = require('../sync');
+
+var fixturesDir = path.join(__dirname, 'list-exports', 'packages', 'tests', 'fixtures');
+
+var categories = [
+    'broken',
+    'broken-dir-slash-conditions',
+    'conditions',
+    'experimental',
+    'pattern-trailers',
+    'pattern-trailers+json-imports',
+    'pattern-trailers-no-dir-slash',
+    'pattern-trailers-no-dir-slash+json-imports',
+    'patterns',
+    'require-esm',
+    'strips-types',
+    'subpath-imports-slash'
+    // 'pre-exports' is tested separately since it uses main/index resolution
+];
+
+// Fixtures that are symlinks pointing outside the fixture dir cause path confusion
+// ex-private is a private package whose expected files don't include exports data
+var skipFixtures = ['list-exports', 'ls-exports', 'ex-private'];
+
+function getFixtures() {
+    return fs.readdirSync(fixturesDir).filter(function (name) {
+        if (skipFixtures.indexOf(name) !== -1) {
+            return false;
+        }
+        var stat = fs.statSync(path.join(fixturesDir, name));
+        return stat.isDirectory();
+    });
+}
+
+function loadExpected(fixtureName, category) {
+    var expectedPath = path.join(fixturesDir, fixtureName, 'expected', category + '.json');
+    if (!fs.existsSync(expectedPath)) {
+        return null;
+    }
+    try {
+        return JSON.parse(fs.readFileSync(expectedPath, 'utf8'));
+    } catch (e) {
+        return null;
+    }
+}
+
+function loadProjectPkg(fixtureName) {
+    var pkgPath = path.join(fixturesDir, fixtureName, 'project', 'package.json');
+    try {
+        return JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
+    } catch (e) {
+        return null;
+    }
+}
+
+test('exports resolution - exportsCategory option', function (t) {
+    var fixtures = getFixtures();
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (!expected || !expected.exports || !expected.exports[category]) {
+                return;
+            }
+
+            var requireMap = expected.exports[category].require;
+            if (!requireMap || typeof requireMap !== 'object') {
+                return;
+            }
+
+            Object.keys(requireMap).forEach(function (subpath) {
+                var expectedFile = requireMap[subpath];
+                var specifier = subpath === '.' ? pkgName : pkgName + subpath.substring(1);
+
+                t.test(fixtureName + ' / ' + category + ' / ' + subpath, function (st) {
+                    st.plan(1);
+                    try {
+                        var result = resolve(specifier, {
+                            basedir: __dirname,
+                            exportsCategory: category,
+                            extensions: ['.js', '.json'],
+                            packageIterator: function () {
+                                return [projectDir];
+                            }
+                        });
+                        var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                        st.equal(relativeResult, expectedFile, specifier + ' resolves to ' + expectedFile);
+                    } catch (e) {
+                        st.fail('Unexpected error for ' + specifier + ': ' + e.message);
+                    }
+                });
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('exports resolution - pre-exports category uses main/index', function (t) {
+    var fixtures = getFixtures();
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        var expected = loadExpected(fixtureName, 'pre-exports');
+        if (!expected || !expected.exports || !expected.exports['pre-exports']) {
+            return;
+        }
+
+        var requireMap = expected.exports['pre-exports'].require;
+        if (!requireMap || typeof requireMap !== 'object') {
+            return;
+        }
+
+        // For pre-exports, only test the main entry point (.)
+        var mainEntry = requireMap['.'];
+        if (!mainEntry) {
+            return;
+        }
+
+        t.test(fixtureName + ' / pre-exports / .', function (st) {
+            st.plan(1);
+            try {
+                var result = resolve(pkgName, {
+                    basedir: __dirname,
+                    exportsCategory: 'pre-exports',
+                    extensions: ['.js', '.json'],
+                    packageIterator: function () {
+                        return [projectDir];
+                    }
+                });
+                var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                st.equal(relativeResult, mainEntry, pkgName + ' resolves to ' + mainEntry);
+            } catch (e) {
+                st.fail('Unexpected error for ' + pkgName + ': ' + e.message);
+            }
+        });
+    });
+
+    t.end();
+});
+
+test('exports resolution - mutual exclusivity of options', function (t) {
+    t.test('exportsCategory and engines (string) are mutually exclusive', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                engines: '>= 14'
+            });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/mutually exclusive/).test(e.message), 'throws with mutually exclusive message');
+        }
+    });
+
+    t.test('exportsCategory and engines (true) are mutually exclusive', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', {
+                basedir: __dirname,
+                exportsCategory: 'conditions',
+                engines: true
+            });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/mutually exclusive/).test(e.message), 'throws with mutually exclusive message');
+        }
+    });
+
+    t.end();
+});
+
+test('exports resolution - invalid category', function (t) {
+    t.plan(2);
+    try {
+        resolve('tape', {
+            basedir: __dirname,
+            exportsCategory: 'not-a-real-category'
+        });
+        t.fail('should have thrown');
+    } catch (e) {
+        t.equal(e.code, 'INVALID_EXPORTS_CATEGORY', 'has correct error code');
+        t.ok((/Invalid exports category/).test(e.message), 'has correct error message');
+    }
+});
+
+test('exports resolution - engines option', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-exports-string', 'project');
+
+    t.test('engines string maps to category', function (st) {
+        st.plan(1);
+        var result = resolve('ex-exports-string', {
+            basedir: __dirname,
+            engines: '>= 14',
+            packageIterator: function () {
+                return [projectDir];
+            }
+        });
+        st.ok(result.indexOf('index.js') > -1, 'resolves to index.js');
+    });
+
+    t.test('engines: false is same as omitting', function (st) {
+        st.plan(1);
+        var result = resolve('tape', {
+            basedir: __dirname,
+            engines: false
+        });
+        st.ok(result.indexOf('tape') > -1, 'resolves without exports resolution');
+    });
+
+    t.test('engines: empty string throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', {
+                basedir: __dirname,
+                engines: ''
+            });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/must be.*true.*false.*non-empty string/i).test(e.message), 'throws with correct message');
+        }
+    });
+
+    t.test('engines: number throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', {
+                basedir: __dirname,
+                engines: 14
+            });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/must be.*true.*false.*non-empty string/i).test(e.message), 'throws with correct message');
+        }
+    });
+
+    t.test('engines: object throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', {
+                basedir: __dirname,
+                engines: { node: '>= 14' }
+            });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/must be.*true.*false.*non-empty string/i).test(e.message), 'throws with correct message');
+        }
+    });
+
+    t.end();
+});
+
+test('exports resolution - conditions override', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-conditions', 'project');
+
+    t.test('default category conditions resolve to require.js', function (st) {
+        st.plan(1);
+        var result = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            packageIterator: function () {
+                return [projectDir];
+            }
+        });
+        st.ok(result.indexOf('require.js') > -1, 'resolves to require.js with default conditions');
+    });
+
+    t.test('conditions override to [default] resolves to default.js', function (st) {
+        st.plan(1);
+        var result = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            conditions: ['default'],
+            packageIterator: function () {
+                return [projectDir];
+            }
+        });
+        st.ok(result.indexOf('default.js') > -1, 'resolves to default.js with conditions override');
+    });
+
+    t.test('conditions override to [node] resolves to node.js', function (st) {
+        st.plan(1);
+        var result = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            conditions: ['node'],
+            packageIterator: function () {
+                return [projectDir];
+            }
+        });
+        st.ok(result.indexOf('node.js') > -1, 'resolves to node.js with conditions override');
+    });
+
+    t.end();
+});
+
+test('exports resolution - subpath not exported throws', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-exports-string', 'project');
+
+    t.plan(2);
+    try {
+        resolve('ex-exports-string/not-exported', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            packageIterator: function () {
+                return [projectDir];
+            }
+        });
+        t.fail('should have thrown');
+    } catch (e) {
+        t.equal(e.code, 'ERR_PACKAGE_PATH_NOT_EXPORTED', 'has correct error code');
+        t.ok((/not defined by "exports"/).test(e.message), 'has correct error message');
+    }
+});
+
+test('existing resolution without exports options still works', function (t) {
+    t.plan(1);
+    var result = resolve('tape', { basedir: __dirname });
+    t.ok(result.indexOf('tape') > -1, 'resolves tape without exports options');
+});
+
+test('all fixtures are tested', function (t) {
+    var fixtures = getFixtures();
+    var testedFixtures = [];
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            t.fail('Fixture ' + fixtureName + ' has no loadable package.json');
+            return;
+        }
+
+        var hasAnyTests = false;
+
+        // Check if at least one category has expected results
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (expected && expected.exports && expected.exports[category]) {
+                var requireMap = expected.exports[category].require;
+                if (requireMap && typeof requireMap === 'object' && Object.keys(requireMap).length > 0) {
+                    hasAnyTests = true;
+                }
+            }
+        });
+
+        // Also check pre-exports
+        var preExpected = loadExpected(fixtureName, 'pre-exports');
+        if (preExpected && preExpected.exports && preExpected.exports['pre-exports']) {
+            var preRequireMap = preExpected.exports['pre-exports'].require;
+            if (preRequireMap && preRequireMap['.']) {
+                hasAnyTests = true;
+            }
+        }
+
+        if (hasAnyTests) {
+            testedFixtures.push(fixtureName);
+        } else {
+            t.fail('Fixture ' + fixtureName + ' has no testable entrypoints');
+        }
+    });
+
+    t.ok(testedFixtures.length > 0, 'At least one fixture is tested');
+    t.equal(testedFixtures.length, fixtures.length, 'All ' + fixtures.length + ' fixtures have testable entrypoints');
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem: import uses import conditions', function (t) {
+    // ex-node-addons is skipped because getCategoryInfo includes node-addons in import
+    // conditions for some categories, but list-exports expected data does not
+    var skipImportFixtures = ['ex-node-addons'];
+    var fixtures = getFixtures().filter(function (n) { return skipImportFixtures.indexOf(n) === -1; });
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (!expected || !expected.exports || !expected.exports[category]) {
+                return;
+            }
+
+            var importMap = expected.exports[category].import;
+            if (!importMap || typeof importMap !== 'object') {
+                return;
+            }
+
+            Object.keys(importMap).forEach(function (subpath) {
+                var expectedFile = importMap[subpath];
+                var specifier = subpath === '.' ? pkgName : pkgName + subpath.substring(1);
+
+                t.test(fixtureName + ' / ' + category + ' / import / ' + subpath, function (st) {
+                    st.plan(1);
+                    try {
+                        var result = resolve(specifier, {
+                            basedir: __dirname,
+                            exportsCategory: category,
+                            moduleSystem: 'import',
+                            extensions: ['.js', '.json', '.mjs'],
+                            packageIterator: function () {
+                                return [projectDir];
+                            }
+                        });
+                        var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                        st.equal(relativeResult, expectedFile, specifier + ' with moduleSystem:import resolves to ' + expectedFile);
+                    } catch (e) {
+                        st.fail('Unexpected error for ' + specifier + ' with moduleSystem:import: ' + e.message);
+                    }
+                });
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem: require matches default behavior', function (t) {
+    var fixtures = getFixtures();
+
+    fixtures.forEach(function (fixtureName) {
+        var projectPkg = loadProjectPkg(fixtureName);
+        if (!projectPkg) {
+            return;
+        }
+        var projectDir = path.join(fixturesDir, fixtureName, 'project');
+        var pkgName = projectPkg.name;
+
+        categories.forEach(function (category) {
+            var expected = loadExpected(fixtureName, category);
+            if (!expected || !expected.exports || !expected.exports[category]) {
+                return;
+            }
+
+            var requireMap = expected.exports[category].require;
+            if (!requireMap || typeof requireMap !== 'object') {
+                return;
+            }
+
+            Object.keys(requireMap).forEach(function (subpath) {
+                var expectedFile = requireMap[subpath];
+                var specifier = subpath === '.' ? pkgName : pkgName + subpath.substring(1);
+
+                t.test(fixtureName + ' / ' + category + ' / explicit require / ' + subpath, function (st) {
+                    st.plan(1);
+                    try {
+                        var result = resolve(specifier, {
+                            basedir: __dirname,
+                            exportsCategory: category,
+                            moduleSystem: 'require',
+                            extensions: ['.js', '.json'],
+                            packageIterator: function () {
+                                return [projectDir];
+                            }
+                        });
+                        var relativeResult = './' + path.relative(projectDir, result).split(path.sep).join('/');
+                        st.equal(relativeResult, expectedFile, specifier + ' with moduleSystem:require resolves to ' + expectedFile);
+                    } catch (e) {
+                        st.fail('Unexpected error for ' + specifier + ' with moduleSystem:require: ' + e.message);
+                    }
+                });
+            });
+        });
+    });
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem import vs require produce different results', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-conditions', 'project');
+
+    t.test('./idnr: import resolves to import.mjs, require resolves to default.js', function (st) {
+        st.plan(2);
+        var importResult = resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('import.mjs') > -1, 'moduleSystem:import resolves to import.mjs');
+
+        var requireResult = resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('default.js') > -1, 'moduleSystem:require resolves to default.js');
+    });
+
+    t.test('./rdni: import resolves to default.js, require resolves to require.js', function (st) {
+        st.plan(2);
+        var importResult = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('default.js') > -1, 'moduleSystem:import resolves to default.js');
+
+        var requireResult = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('require.js') > -1, 'moduleSystem:require resolves to require.js');
+    });
+
+    t.test('./indr: import resolves to import.mjs, require resolves to node.js', function (st) {
+        st.plan(2);
+        var importResult = resolve('ex-conditions/indr', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('import.mjs') > -1, 'moduleSystem:import resolves to import.mjs');
+
+        var requireResult = resolve('ex-conditions/indr', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('node.js') > -1, 'moduleSystem:require resolves to node.js');
+    });
+
+    t.test('./irdn: import resolves to import.mjs, require resolves to require.js', function (st) {
+        st.plan(2);
+        var importResult = resolve('ex-conditions/irdn', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('import.mjs') > -1, 'moduleSystem:import resolves to import.mjs');
+
+        var requireResult = resolve('ex-conditions/irdn', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('require.js') > -1, 'moduleSystem:require resolves to require.js');
+    });
+
+    t.test('default (no moduleSystem) matches require behavior', function (st) {
+        st.plan(2);
+        var defaultResult = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        var requireResult = resolve('ex-conditions/rdni', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.equal(defaultResult, requireResult, 'default and explicit require produce the same result');
+        st.ok(defaultResult.indexOf('require.js') > -1, 'both resolve to require.js');
+    });
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem import with various fixtures', function (t) {
+    t.test('ex-conditions-in-folder: import resolves to mjs, require to cjs', function (st) {
+        var projectDir = path.join(fixturesDir, 'ex-conditions-in-folder', 'project');
+        st.plan(2);
+        var importResult = resolve('ex-conditions-in-folder', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('mjs/index.mjs') > -1, 'import resolves to mjs/index.mjs');
+
+        var requireResult = resolve('ex-conditions-in-folder', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('cjs/index.js') > -1, 'require resolves to cjs/index.js');
+    });
+
+    t.test('ex-exports-TL-object: import resolves to index.mjs, require to file.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'ex-exports-TL-object', 'project');
+        st.plan(2);
+        var importResult = resolve('ex-exports-TL-object', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('index.mjs') > -1, 'import resolves to index.mjs');
+
+        var requireResult = resolve('ex-exports-TL-object', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('file.js') > -1, 'require resolves to file.js');
+    });
+
+    t.test('flatted-3: import resolves to esm/index.js, require to cjs/index.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'flatted-3', 'project');
+        st.plan(2);
+        var importResult = resolve('flatted', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('esm/index.js') > -1, 'import resolves to esm/index.js');
+
+        var requireResult = resolve('flatted', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('cjs/index.js') > -1, 'require resolves to cjs/index.js');
+    });
+
+    t.test('is-promise-2.2.1: import resolves to index.mjs, require to index.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'is-promise-2.2.1', 'project');
+        st.plan(2);
+        var importResult = resolve('is-promise', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('index.mjs') > -1, 'import resolves to index.mjs');
+
+        var requireResult = resolve('is-promise', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('index.js') > -1 && requireResult.indexOf('index.mjs') === -1, 'require resolves to index.js');
+    });
+
+    t.test('resolve-2: import resolves to index.mjs, require to index.js', function (st) {
+        var projectDir = path.join(fixturesDir, 'resolve-2', 'project');
+        st.plan(2);
+        var importResult = resolve('resolve', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('index.mjs') > -1, 'import resolves to index.mjs');
+
+        var requireResult = resolve('resolve', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('index.js') > -1 && requireResult.indexOf('index.mjs') === -1, 'require resolves to index.js');
+    });
+
+    t.test('preact: import resolves to .mjs, require to .js', function (st) {
+        var projectDir = path.join(fixturesDir, 'preact', 'project');
+        st.plan(2);
+        var importResult = resolve('preact', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(importResult.indexOf('preact.mjs') > -1, 'import resolves to preact.mjs');
+
+        var requireResult = resolve('preact', {
+            basedir: __dirname,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(requireResult.indexOf('preact.js') > -1 && requireResult.indexOf('preact.mjs') === -1, 'require resolves to preact.js');
+    });
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem import with self-reference', function (t) {
+    t.test('self-reference with moduleSystem:import uses import conditions', function (st) {
+        var conditionsDir = path.join(fixturesDir, 'ex-conditions', 'project');
+        st.plan(2);
+        var importResult = resolve('ex-conditions/idnr', {
+            basedir: conditionsDir,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs']
+        });
+        st.ok(importResult.indexOf('import.mjs') > -1, 'self-reference with import resolves to import.mjs');
+
+        var requireResult = resolve('ex-conditions/idnr', {
+            basedir: conditionsDir,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs']
+        });
+        st.ok(requireResult.indexOf('default.js') > -1, 'self-reference with require resolves to default.js');
+    });
+
+    t.test('self-reference with moduleSystem:import on TL-object package', function (st) {
+        var projectDir = path.join(fixturesDir, 'ex-exports-TL-object', 'project');
+        st.plan(2);
+        var importResult = resolve('ex-exports-TL-object', {
+            basedir: projectDir,
+            exportsCategory: 'conditions',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs']
+        });
+        st.ok(importResult.indexOf('index.mjs') > -1, 'self-ref import resolves to index.mjs');
+
+        var requireResult = resolve('ex-exports-TL-object', {
+            basedir: projectDir,
+            exportsCategory: 'conditions',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs']
+        });
+        st.ok(requireResult.indexOf('file.js') > -1, 'self-ref require resolves to file.js');
+    });
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem with engines option', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-conditions', 'project');
+
+    t.test('moduleSystem:import works with engines string', function (st) {
+        st.plan(1);
+        var result = resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            engines: '>= 14',
+            moduleSystem: 'import',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(result.indexOf('import.mjs') > -1, 'engines + moduleSystem:import resolves to import.mjs');
+    });
+
+    t.test('moduleSystem:require works with engines string', function (st) {
+        st.plan(1);
+        var result = resolve('ex-conditions/idnr', {
+            basedir: __dirname,
+            engines: '>= 14',
+            moduleSystem: 'require',
+            extensions: ['.js', '.mjs'],
+            packageIterator: function () { return [projectDir]; }
+        });
+        st.ok(result.indexOf('default.js') > -1, 'engines + moduleSystem:require resolves to default.js');
+    });
+
+    t.end();
+});
+
+test('exports resolution - invalid moduleSystem throws', function (t) {
+    t.test('moduleSystem: true throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: true });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: false throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: false });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: empty string throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: '' });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: number throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: 42 });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: random string throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: 'cjs' });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: object throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: {} });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: null throws', function (st) {
+        st.plan(1);
+        try {
+            resolve('tape', { basedir: __dirname, moduleSystem: null });
+            st.fail('should have thrown');
+        } catch (e) {
+            st.ok((/moduleSystem/).test(e.message), 'throws with moduleSystem message: ' + e.message);
+        }
+    });
+
+    t.test('moduleSystem: undefined does not throw', function (st) {
+        st.plan(1);
+        var result = resolve('tape', { basedir: __dirname, moduleSystem: undefined });
+        st.ok(result.indexOf('tape') > -1, 'undefined moduleSystem resolves normally');
+    });
+
+    t.end();
+});
+
+test('exports resolution - moduleSystem does not affect resolution without exports', function (t) {
+    t.plan(1);
+    var result = resolve('tape', {
+        basedir: __dirname,
+        moduleSystem: 'import'
+    });
+    t.ok(result.indexOf('tape') > -1, 'moduleSystem is ignored when no exports options are set');
+});
+
+test('exports resolution - self-reference', function (t) {
+    var projectDir = path.join(fixturesDir, 'ex-exports-string', 'project');
+
+    t.test('self-reference resolves via exports when inside package', function (st) {
+        st.plan(1);
+        // basedir is inside the package, specifier is the package name
+        var result = resolve('ex-exports-string', {
+            basedir: projectDir,
+            exportsCategory: 'conditions'
+        });
+        st.ok(result.indexOf('index.js') > -1, 'self-reference resolves to index.js via exports');
+    });
+
+    t.test('self-reference with subpath resolves via exports', function (st) {
+        var conditionsDir = path.join(fixturesDir, 'ex-conditions', 'project');
+        st.plan(1);
+        var result = resolve('ex-conditions/rdni', {
+            basedir: conditionsDir,
+            exportsCategory: 'conditions'
+        });
+        st.ok(result.indexOf('require.js') > -1, 'self-reference subpath resolves correctly');
+    });
+
+    t.test('self-reference without exports falls back to main', function (st) {
+        // Create a scenario where there's no exports field
+        var mainDotlessDir = path.join(fixturesDir, 'ex-main-dotless', 'project');
+        st.plan(1);
+        try {
+            var result = resolve('ex-main-dotless', {
+                basedir: mainDotlessDir,
+                exportsCategory: 'conditions'
+            });
+            st.ok(result.indexOf('main.js') > -1 || result.indexOf('index.js') > -1, 'self-reference without exports uses main/index');
+        } catch (e) {
+            // If it throws, that's also acceptable behavior (no exports means not exported)
+            st.ok(true, 'self-reference without exports throws or resolves');
+        }
+    });
+
+    t.test('self-reference does not cross node_modules boundary', function (st) {
+        // basedir is inside node_modules, should NOT self-reference parent package
+        var nodeModulesDir = path.join(__dirname, '..', 'node_modules', 'tape');
+        st.plan(1);
+        // Trying to resolve 'resolve' from inside node_modules/tape should NOT
+        // self-reference the root resolve package - it should use normal resolution
+        var result = resolve('resolve', {
+            basedir: nodeModulesDir,
+            exportsCategory: 'conditions'
+        });
+        // The result should be from node_modules, not from a self-reference
+        // (self-reference would give us the current working directory's resolve)
+        st.ok(result.indexOf('node_modules') > -1 || result === 'resolve', 'does not self-reference across node_modules');
+    });
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/faulty_basedir.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/faulty_basedir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/faulty_basedir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,29 @@
+var test = require('tape');
+var path = require('path');
+var resolve = require('../');
+
+test('faulty basedir must produce error in windows', { skip: process.platform !== 'win32' }, function (t) {
+    t.plan(1);
+
+    var resolverDir = 'C:\\a\\b\\c\\d';
+
+    resolve('tape/lib/test.js', { basedir: resolverDir }, function (err, res, pkg) {
+        t.equal(!!err, true);
+    });
+});
+
+test('non-existent basedir should not throw when preserveSymlinks is false', function (t) {
+    t.plan(2);
+
+    var opts = {
+        basedir: path.join(path.sep, 'unreal', 'path', 'that', 'does', 'not', 'exist'),
+        preserveSymlinks: false
+    };
+
+    var module = './dotdot/abc';
+
+    resolve(module, opts, function (err, res) {
+        t.equal(err.code, 'INVALID_BASEDIR');
+        t.equal(res, undefined);
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/filter.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/filter.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/filter.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,37 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('filter', function (t) {
+    t.plan(5);
+    var dir = path.join(__dirname, 'resolver');
+    var packageFilterArgs;
+    resolve('./baz', {
+        basedir: dir,
+        packageFilter: function (pkg, pkgfile, dir) {
+            pkg.main = 'doom'; // eslint-disable-line no-param-reassign
+            packageFilterArgs = [pkg, pkgfile, dir];
+            return pkg;
+        }
+    }, function (err, res, pkg) {
+        if (err) t.fail(err);
+
+        t.equal(res, path.join(dir, 'baz/doom.js'), 'changing the package "main" works');
+
+        var packageData = packageFilterArgs[0];
+        t.equal(pkg, packageData, 'first packageFilter argument is "pkg"');
+        t.equal(packageData.main, 'doom', 'package "main" was altered');
+
+        var packageFile = packageFilterArgs[1];
+        t.equal(
+            packageFile,
+            path.join(dir, 'baz/package.json'),
+            'second packageFilter argument is "pkgfile"'
+        );
+
+        var packageFileDir = packageFilterArgs[2];
+        t.equal(packageFileDir, path.join(dir, 'baz'), 'third packageFilter argument is "dir"');
+
+        t.end();
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/filter_sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/filter_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/filter_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,33 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('filter', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+    var packageFilterArgs;
+    var res = resolve.sync('./baz', {
+        basedir: dir,
+        packageFilter: function (pkg, pkgfile, dir) {
+            pkg.main = 'doom'; // eslint-disable-line no-param-reassign
+            packageFilterArgs = [pkg, pkgfile, dir];
+            return pkg;
+        }
+    });
+
+    t.equal(res, path.join(dir, 'baz/doom.js'), 'changing the package "main" works');
+
+    var packageData = packageFilterArgs[0];
+    t.equal(packageData.main, 'doom', 'package "main" was altered');
+
+    var packageFile = packageFilterArgs[1];
+    t.equal(
+        packageFile,
+        path.join(dir, 'baz/package.json'),
+        'second packageFilter argument is "pkgfile"'
+    );
+
+    var packageDir = packageFilterArgs[2];
+    t.equal(packageDir, path.join(dir, 'baz'), 'third packageFilter argument is "dir"');
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,127 @@
+'use strict';
+
+var fs = require('fs');
+var homedir = require('../lib/homedir');
+var path = require('path');
+
+var test = require('tape');
+var mkdirp = require('mkdirp');
+var rimraf = require('rimraf');
+var mv = require('mv');
+var copyDir = require('copy-dir');
+var tmp = require('tmp');
+
+var HOME = homedir();
+
+var hnm = path.join(HOME, '.node_modules');
+var hnl = path.join(HOME, '.node_libraries');
+
+var resolve = require('../async');
+
+function makeDir(t, dir, cb) {
+    mkdirp(dir, function (err) {
+        if (err) {
+            cb(err);
+        } else {
+            t.teardown(function cleanup() {
+                rimraf.sync(dir);
+            });
+            cb();
+        }
+    });
+}
+
+function makeTempDir(t, dir, cb) {
+    if (fs.existsSync(dir)) {
+        var tmpResult = tmp.dirSync();
+        t.teardown(tmpResult.removeCallback);
+        var backup = path.join(tmpResult.name, path.basename(dir));
+        mv(dir, backup, function (err) {
+            if (err) {
+                cb(err);
+            } else {
+                t.teardown(function () {
+                    mv(backup, dir, cb);
+                });
+                makeDir(t, dir, cb);
+            }
+        });
+    } else {
+        makeDir(t, dir, cb);
+    }
+}
+
+test('homedir module paths', function (t) {
+    t.plan(7);
+
+    makeTempDir(t, hnm, function (err) {
+        t.error(err, 'no error with HNM temp dir');
+        if (err) {
+            return t.end();
+        }
+
+        var bazHNMDir = path.join(hnm, 'baz');
+        var dotMainDir = path.join(hnm, 'dot_main');
+        copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir);
+        copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir);
+
+        var bazPkg = { name: 'baz', main: 'quux.js' };
+        var dotMainPkg = { main: 'index' };
+
+        var bazHNMmain = path.join(bazHNMDir, 'quux.js');
+        t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
+        var dotMainMain = path.join(dotMainDir, 'index.js');
+        t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`');
+
+        makeTempDir(t, hnl, function (err) {
+            t.error(err, 'no error with HNL temp dir');
+            if (err) {
+                return t.end();
+            }
+            var bazHNLDir = path.join(hnl, 'baz');
+            copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir);
+
+            var dotSlashMainDir = path.join(hnl, 'dot_slash_main');
+            var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js');
+            var dotSlashMainPkg = { main: 'index' };
+            copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir);
+
+            t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
+            t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`');
+
+            t.test('with temp dirs', function (st) {
+                st.plan(3);
+
+                st.test('just in `$HOME/.node_modules`', function (s2t) {
+                    s2t.plan(3);
+
+                    resolve('dot_main', function (err, res, pkg) {
+                        s2t.error(err, 'no error resolving `dot_main`');
+                        s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`');
+                        s2t.deepEqual(pkg, dotMainPkg);
+                    });
+                });
+
+                st.test('just in `$HOME/.node_libraries`', function (s2t) {
+                    s2t.plan(3);
+
+                    resolve('dot_slash_main', function (err, res, pkg) {
+                        s2t.error(err, 'no error resolving `dot_slash_main`');
+                        s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`');
+                        s2t.deepEqual(pkg, dotSlashMainPkg);
+                    });
+                });
+
+                st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) {
+                    s2t.plan(3);
+
+                    resolve('baz', function (err, res, pkg) {
+                        s2t.error(err, 'no error resolving `baz`');
+                        s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both');
+                        s2t.deepEqual(pkg, bazPkg);
+                    });
+                });
+            });
+        });
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths_sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,114 @@
+'use strict';
+
+var fs = require('fs');
+var homedir = require('../lib/homedir');
+var path = require('path');
+
+var test = require('tape');
+var mkdirp = require('mkdirp');
+var rimraf = require('rimraf');
+var mv = require('mv');
+var copyDir = require('copy-dir');
+var tmp = require('tmp');
+
+var HOME = homedir();
+
+var hnm = path.join(HOME, '.node_modules');
+var hnl = path.join(HOME, '.node_libraries');
+
+var resolve = require('../sync');
+
+function makeDir(t, dir, cb) {
+    mkdirp(dir, function (err) {
+        if (err) {
+            cb(err);
+        } else {
+            t.teardown(function cleanup() {
+                rimraf.sync(dir);
+            });
+            cb();
+        }
+    });
+}
+
+function makeTempDir(t, dir, cb) {
+    if (fs.existsSync(dir)) {
+        var tmpResult = tmp.dirSync();
+        t.teardown(tmpResult.removeCallback);
+        var backup = path.join(tmpResult.name, path.basename(dir));
+        mv(dir, backup, function (err) {
+            if (err) {
+                cb(err);
+            } else {
+                t.teardown(function () {
+                    mv(backup, dir, cb);
+                });
+                makeDir(t, dir, cb);
+            }
+        });
+    } else {
+        makeDir(t, dir, cb);
+    }
+}
+
+test('homedir module paths', function (t) {
+    t.plan(7);
+
+    makeTempDir(t, hnm, function (err) {
+        t.error(err, 'no error with HNM temp dir');
+        if (err) {
+            return t.end();
+        }
+
+        var bazHNMDir = path.join(hnm, 'baz');
+        var dotMainDir = path.join(hnm, 'dot_main');
+        copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir);
+        copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir);
+
+        var bazHNMmain = path.join(bazHNMDir, 'quux.js');
+        t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
+        var dotMainMain = path.join(dotMainDir, 'index.js');
+        t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`');
+
+        makeTempDir(t, hnl, function (err) {
+            t.error(err, 'no error with HNL temp dir');
+            if (err) {
+                return t.end();
+            }
+            var bazHNLDir = path.join(hnl, 'baz');
+            copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir);
+
+            var dotSlashMainDir = path.join(hnl, 'dot_slash_main');
+            var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js');
+            copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir);
+
+            t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`');
+            t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`');
+
+            t.test('with temp dirs', function (st) {
+                st.plan(3);
+
+                st.test('just in `$HOME/.node_modules`', function (s2t) {
+                    s2t.plan(1);
+
+                    var res = resolve('dot_main');
+                    s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`');
+                });
+
+                st.test('just in `$HOME/.node_libraries`', function (s2t) {
+                    s2t.plan(1);
+
+                    var res = resolve('dot_slash_main');
+                    s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`');
+                });
+
+                st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) {
+                    s2t.plan(1);
+
+                    var res = resolve('baz');
+                    s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both');
+                });
+            });
+        });
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/homedir.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/homedir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/homedir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,112 @@
+'use strict';
+
+var os = require('os');
+var test = require('tape');
+var mockProperty = require('mock-property');
+
+var envKeys = ['HOME', 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH', 'LOGNAME', 'USER', 'LNAME', 'USERNAME'];
+
+function mockEnv(t, key, value) {
+    var has = key in process.env;
+    var orig = process.env[key];
+    if (arguments.length > 2) {
+        process.env[key] = value;
+    } else {
+        delete process.env[key];
+    }
+    t.teardown(function () {
+        if (has) {
+            process.env[key] = orig;
+        } else {
+            delete process.env[key];
+        }
+    });
+}
+
+function clearEnv(t) {
+    for (var i = 0; i < envKeys.length; i++) {
+        mockEnv(t, envKeys[i]);
+    }
+}
+
+function getFallback(t) {
+    t.teardown(mockProperty(os, 'homedir', { value: undefined }));
+
+    var homedirPath = require.resolve('../lib/homedir');
+    t.teardown(mockProperty(require.cache, homedirPath, { 'delete': true }));
+
+    return require('../lib/homedir');
+}
+
+test('homedir fallback', function (t) {
+    t.test('win32: HOMEDRIVE without HOMEPATH does not produce a false concatenation', function (st) {
+        clearEnv(st);
+        st.teardown(mockProperty(process, 'platform', { value: 'win32' }));
+
+        var homedir = getFallback(st);
+
+        mockEnv(st, 'HOMEDRIVE', 'C:');
+
+        st.equal(homedir(), null, 'returns null when only HOMEDRIVE is set');
+
+        st.end();
+    });
+
+    t.test('win32: HOMEPATH without HOMEDRIVE does not produce a false concatenation', function (st) {
+        clearEnv(st);
+        st.teardown(mockProperty(process, 'platform', { value: 'win32' }));
+
+        var homedir = getFallback(st);
+
+        mockEnv(st, 'HOMEPATH', '\\Users\\foo');
+
+        st.equal(homedir(), null, 'returns null when only HOMEPATH is set');
+
+        st.end();
+    });
+
+    t.test('win32: HOMEDRIVE + HOMEPATH both set returns concatenation', function (st) {
+        clearEnv(st);
+        st.teardown(mockProperty(process, 'platform', { value: 'win32' }));
+
+        var homedir = getFallback(st);
+
+        mockEnv(st, 'HOMEDRIVE', 'C:');
+        mockEnv(st, 'HOMEPATH', '\\Users\\foo');
+
+        st.equal(homedir(), 'C:\\Users\\foo', 'returns concatenated drive and path');
+
+        st.end();
+    });
+
+    t.test('win32: USERPROFILE takes precedence over HOMEDRIVE+HOMEPATH', function (st) {
+        clearEnv(st);
+        st.teardown(mockProperty(process, 'platform', { value: 'win32' }));
+
+        var homedir = getFallback(st);
+
+        mockEnv(st, 'USERPROFILE', 'C:\\Users\\bar');
+        mockEnv(st, 'HOMEDRIVE', 'C:');
+        mockEnv(st, 'HOMEPATH', '\\Users\\foo');
+
+        st.equal(homedir(), 'C:\\Users\\bar', 'returns USERPROFILE');
+
+        st.end();
+    });
+
+    t.test('win32: falls back to HOME when HOMEDRIVE/HOMEPATH are partial', function (st) {
+        clearEnv(st);
+        st.teardown(mockProperty(process, 'platform', { value: 'win32' }));
+
+        var homedir = getFallback(st);
+
+        mockEnv(st, 'HOME', 'C:\\Users\\baz');
+        mockEnv(st, 'HOMEDRIVE', 'C:');
+
+        st.equal(homedir(), 'C:\\Users\\baz', 'falls back to HOME');
+
+        st.end();
+    });
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/mock.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/mock.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/mock.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,353 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('mock', function (t) {
+    t.plan(8);
+
+    var files = {};
+    files[path.resolve('/foo/bar/baz.js')] = 'beep';
+
+    var dirs = {};
+    dirs[path.resolve('/foo/bar')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!dirs[path.resolve(dir)]);
+            },
+            readFile: function (file, cb) {
+                cb(null, files[path.resolve(file)]);
+            },
+            realpath: function (file, cb) {
+                cb(null, file);
+            }
+        };
+    }
+
+    resolve('./baz', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/bar/baz.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/bar/baz.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module 'baz' from '" + path.resolve('/foo/bar') + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+
+    resolve('../baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module '../baz' from '" + path.resolve('/foo/bar') + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+});
+
+test('mock from package', function (t) {
+    t.plan(8);
+
+    var files = {};
+    files[path.resolve('/foo/bar/baz.js')] = 'beep';
+
+    var dirs = {};
+    dirs[path.resolve('/foo/bar')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(files, file));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!dirs[path.resolve(dir)]);
+            },
+            'package': { main: 'bar' },
+            readFile: function (file, cb) {
+                cb(null, files[file]);
+            },
+            realpath: function (file, cb) {
+                cb(null, file);
+            }
+        };
+    }
+
+    resolve('./baz', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/bar/baz.js'));
+        t.equal(pkg && pkg.main, 'bar');
+    });
+
+    resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/bar/baz.js'));
+        t.equal(pkg && pkg.main, 'bar');
+    });
+
+    resolve('baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module 'baz' from '" + path.resolve('/foo/bar') + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+
+    resolve('../baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module '../baz' from '" + path.resolve('/foo/bar') + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+});
+
+test('mock package', function (t) {
+    t.plan(2);
+
+    var files = {};
+    files[path.resolve('/foo/node_modules/bar/baz.js')] = 'beep';
+    files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({
+        main: './baz.js'
+    });
+
+    var dirs = {};
+    dirs[path.resolve('/foo')] = true;
+    dirs[path.resolve('/foo/node_modules')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!dirs[path.resolve(dir)]);
+            },
+            readFile: function (file, cb) {
+                cb(null, files[path.resolve(file)]);
+            },
+            realpath: function (file, cb) {
+                cb(null, file);
+            }
+        };
+    }
+
+    resolve('bar', opts('/foo'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/node_modules/bar/baz.js'));
+        t.equal(pkg && pkg.main, './baz.js');
+    });
+});
+
+test('mock package from package', function (t) {
+    t.plan(2);
+
+    var files = {};
+    files[path.resolve('/foo/node_modules/bar/baz.js')] = 'beep';
+    files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({
+        main: './baz.js'
+    });
+
+    var dirs = {};
+    dirs[path.resolve('/foo')] = true;
+    dirs[path.resolve('/foo/node_modules')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!dirs[path.resolve(dir)]);
+            },
+            'package': { main: 'bar' },
+            readFile: function (file, cb) {
+                cb(null, files[path.resolve(file)]);
+            },
+            realpath: function (file, cb) {
+                cb(null, file);
+            }
+        };
+    }
+
+    resolve('bar', opts('/foo'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/node_modules/bar/baz.js'));
+        t.equal(pkg && pkg.main, './baz.js');
+    });
+});
+
+test('symlinked', function (t) {
+    t.plan(4);
+
+    var files = {};
+    files[path.resolve('/foo/bar/baz.js')] = 'beep';
+    files[path.resolve('/foo/bar/symlinked/baz.js')] = 'beep';
+
+    var dirs = {};
+    dirs[path.resolve('/foo/bar')] = true;
+    dirs[path.resolve('/foo/bar/symlinked')] = true;
+
+    function opts(basedir) {
+        return {
+            preserveSymlinks: false,
+            basedir: path.resolve(basedir),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!dirs[path.resolve(dir)]);
+            },
+            readFile: function (file, cb) {
+                cb(null, files[path.resolve(file)]);
+            },
+            realpath: function (file, cb) {
+                var resolved = path.resolve(file);
+
+                if (resolved.indexOf('symlinked') >= 0) {
+                    cb(null, resolved);
+                    return;
+                }
+
+                var ext = path.extname(resolved);
+
+                if (ext) {
+                    var dir = path.dirname(resolved);
+                    var base = path.basename(resolved);
+                    cb(null, path.join(dir, 'symlinked', base));
+                } else {
+                    cb(null, path.join(resolved, 'symlinked'));
+                }
+            }
+        };
+    }
+
+    resolve('./baz', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/bar/symlinked/baz.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) return t.fail(err);
+        t.equal(res, path.resolve('/foo/bar/symlinked/baz.js'));
+        t.equal(pkg, undefined);
+    });
+});
+
+test('readPackage', function (t) {
+    t.plan(4);
+
+    var files = {};
+    files[path.resolve('/foo/node_modules/bar/something-else.js')] = 'beep';
+    files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({
+        main: './baz.js'
+    });
+    files[path.resolve('/foo/node_modules/bar/baz.js')] = 'boop';
+
+    var dirs = {};
+    dirs[path.resolve('/foo')] = true;
+    dirs[path.resolve('/foo/node_modules')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file)));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!dirs[path.resolve(dir)]);
+            },
+            'package': { main: 'bar' },
+            readFile: function (file, cb) {
+                cb(null, files[path.resolve(file)]);
+            },
+            realpath: function (file, cb) {
+                cb(null, file);
+            }
+        };
+    }
+
+    t.test('with readFile', function (st) {
+        st.plan(3);
+
+        resolve('bar', opts('/foo'), function (err, res, pkg) {
+            st.error(err);
+            st.equal(res, path.resolve('/foo/node_modules/bar/baz.js'));
+            st.equal(pkg && pkg.main, './baz.js');
+        });
+    });
+
+    function readPackage(readFile, file, cb) {
+        var barPackage = path.join('bar', 'package.json');
+        if (file.slice(-barPackage.length) === barPackage) {
+            cb(null, { main: './something-else.js' });
+        } else {
+            cb(null, JSON.parse(files[path.resolve(file)]));
+        }
+    }
+
+    t.test('with readPackage', function (st) {
+        st.plan(3);
+
+        var options = opts('/foo');
+        delete options.readFile;
+        options.readPackage = readPackage;
+        resolve('bar', options, function (err, res, pkg) {
+            st.error(err);
+            st.equal(res, path.resolve('/foo/node_modules/bar/something-else.js'));
+            st.equal(pkg && pkg.main, './something-else.js');
+        });
+    });
+
+    t.test('with readFile and readPackage', function (st) {
+        st.plan(1);
+
+        var options = opts('/foo');
+        options.readPackage = readPackage;
+        resolve('bar', options, function (err) {
+            st.throws(function () { throw err; }, TypeError, 'errors when both readFile and readPackage are provided');
+        });
+    });
+
+    t.test('readPackage error in loadpkg does not invoke callback twice', function (st) {
+        st.plan(1);
+
+        var callCount = 0;
+        var readPackageError = new Error('read package error');
+
+        function failReadPackage(rf, file, cb) {
+            cb(readPackageError);
+        }
+
+        var relFiles = {};
+        relFiles[path.resolve('/foo/bar/baz.js')] = 'beep';
+        relFiles[path.resolve('/foo/bar/package.json')] = '{}';
+
+        var relDirs = {};
+        relDirs[path.resolve('/foo/bar')] = true;
+
+        resolve('./baz', {
+            basedir: path.resolve('/foo/bar'),
+            isFile: function (file, cb) {
+                cb(null, Object.prototype.hasOwnProperty.call(relFiles, path.resolve(file)));
+            },
+            isDirectory: function (dir, cb) {
+                cb(null, !!relDirs[path.resolve(dir)]);
+            },
+            readPackage: failReadPackage,
+            realpath: function (file, cb) {
+                cb(null, file);
+            }
+        }, function () {
+            callCount += 1;
+        });
+
+        setTimeout(function () {
+            st.equal(callCount, 1, 'callback is invoked exactly once');
+        }, 50);
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/mock_sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/mock_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/mock_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,235 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('mock', function (t) {
+    t.plan(4);
+
+    var files = {};
+    files[path.resolve('/foo/bar/baz.js')] = 'beep';
+
+    var dirs = {};
+    dirs[path.resolve('/foo/bar')] = true;
+    dirs[path.resolve('/foo/node_modules')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file) {
+                return Object.prototype.hasOwnProperty.call(files, path.resolve(file));
+            },
+            isDirectory: function (dir) {
+                return !!dirs[path.resolve(dir)];
+            },
+            readFileSync: function (file) {
+                return files[path.resolve(file)];
+            },
+            realpathSync: function (file) {
+                return file;
+            }
+        };
+    }
+
+    t.equal(
+        resolve.sync('./baz', opts('/foo/bar')),
+        path.resolve('/foo/bar/baz.js')
+    );
+
+    t.equal(
+        resolve.sync('./baz.js', opts('/foo/bar')),
+        path.resolve('/foo/bar/baz.js')
+    );
+
+    t.throws(function () {
+        resolve.sync('baz', opts('/foo/bar'));
+    });
+
+    t.throws(function () {
+        resolve.sync('../baz', opts('/foo/bar'));
+    });
+});
+
+test('mock package', function (t) {
+    t.plan(1);
+
+    var files = {};
+    files[path.resolve('/foo/node_modules/bar/baz.js')] = 'beep';
+    files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({
+        main: './baz.js'
+    });
+
+    var dirs = {};
+    dirs[path.resolve('/foo')] = true;
+    dirs[path.resolve('/foo/node_modules')] = true;
+
+    function opts(basedir) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file) {
+                return Object.prototype.hasOwnProperty.call(files, path.resolve(file));
+            },
+            isDirectory: function (dir) {
+                return !!dirs[path.resolve(dir)];
+            },
+            readFileSync: function (file) {
+                return files[path.resolve(file)];
+            },
+            realpathSync: function (file) {
+                return file;
+            }
+        };
+    }
+
+    t.equal(
+        resolve.sync('bar', opts('/foo')),
+        path.resolve('/foo/node_modules/bar/baz.js')
+    );
+});
+
+test('symlinked', function (t) {
+    t.plan(2);
+
+    var files = {};
+    files[path.resolve('/foo/bar/baz.js')] = 'beep';
+    files[path.resolve('/foo/bar/symlinked/baz.js')] = 'beep';
+
+    var dirs = {};
+    dirs[path.resolve('/foo/bar')] = true;
+    dirs[path.resolve('/foo/bar/symlinked')] = true;
+
+    function opts(basedir) {
+        return {
+            preserveSymlinks: false,
+            basedir: path.resolve(basedir),
+            isFile: function (file) {
+                return Object.prototype.hasOwnProperty.call(files, path.resolve(file));
+            },
+            isDirectory: function (dir) {
+                return !!dirs[path.resolve(dir)];
+            },
+            readFileSync: function (file) {
+                return files[path.resolve(file)];
+            },
+            realpathSync: function (file) {
+                var resolved = path.resolve(file);
+
+                if (resolved.indexOf('symlinked') >= 0) {
+                    return resolved;
+                }
+
+                var ext = path.extname(resolved);
+
+                if (ext) {
+                    var dir = path.dirname(resolved);
+                    var base = path.basename(resolved);
+                    return path.join(dir, 'symlinked', base);
+                }
+                return path.join(resolved, 'symlinked');
+            }
+        };
+    }
+
+    t.equal(
+        resolve.sync('./baz', opts('/foo/bar')),
+        path.resolve('/foo/bar/symlinked/baz.js')
+    );
+
+    t.equal(
+        resolve.sync('./baz.js', opts('/foo/bar')),
+        path.resolve('/foo/bar/symlinked/baz.js')
+    );
+});
+
+test('readPackageSync', function (t) {
+    t.plan(4);
+
+    var files = {};
+    files[path.resolve('/foo/node_modules/bar/something-else.js')] = 'beep';
+    files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({
+        main: './baz.js'
+    });
+    files[path.resolve('/foo/node_modules/bar/baz.js')] = 'boop';
+
+    var dirs = {};
+    dirs[path.resolve('/foo')] = true;
+    dirs[path.resolve('/foo/node_modules')] = true;
+
+    function opts(basedir, useReadPackage) {
+        return {
+            basedir: path.resolve(basedir),
+            isFile: function (file) {
+                return Object.prototype.hasOwnProperty.call(files, path.resolve(file));
+            },
+            isDirectory: function (dir) {
+                return !!dirs[path.resolve(dir)];
+            },
+            readFileSync: useReadPackage ? null : function (file) {
+                return files[path.resolve(file)];
+            },
+            realpathSync: function (file) {
+                return file;
+            }
+        };
+    }
+    t.test('with readFile', function (st) {
+        st.plan(1);
+
+        st.equal(
+            resolve.sync('bar', opts('/foo')),
+            path.resolve('/foo/node_modules/bar/baz.js')
+        );
+    });
+
+    function readPackageSync(readFileSync, file) {
+        if (file.indexOf(path.join('bar', 'package.json')) >= 0) {
+            return { main: './something-else.js' };
+        }
+        return JSON.parse(files[path.resolve(file)]);
+    }
+
+    t.test('with readPackage', function (st) {
+        st.plan(1);
+
+        var options = opts('/foo');
+        delete options.readFileSync;
+        options.readPackageSync = readPackageSync;
+
+        st.equal(
+            resolve.sync('bar', options),
+            path.resolve('/foo/node_modules/bar/something-else.js')
+        );
+    });
+
+    t.test('with readFile and readPackage', function (st) {
+        st.plan(1);
+
+        var options = opts('/foo');
+        options.readPackageSync = readPackageSync;
+        st.throws(
+            function () { resolve.sync('bar', options); },
+            TypeError,
+            'errors when both readFile and readPackage are provided'
+        );
+    });
+
+    t.test('readPackageSync error propagates correctly', function (st) {
+        st.plan(1);
+
+        var readPackageError = new Error('read package error');
+
+        function failReadPackageSync() {
+            throw readPackageError;
+        }
+
+        var options = opts('/foo');
+        delete options.readFileSync;
+        options.readPackageSync = failReadPackageSync;
+
+        st.throws(
+            function () { resolve.sync('bar', options); },
+            readPackageError,
+            'readPackageSync error is thrown'
+        );
+    });
+});
+
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,56 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('moduleDirectory strings', function (t) {
+    t.plan(4);
+    var dir = path.join(__dirname, 'module_dir');
+    var xopts = {
+        basedir: dir,
+        moduleDirectory: 'xmodules'
+    };
+    resolve('aaa', xopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, '/xmodules/aaa/index.js'));
+    });
+
+    var yopts = {
+        basedir: dir,
+        moduleDirectory: 'ymodules'
+    };
+    resolve('aaa', yopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, '/ymodules/aaa/index.js'));
+    });
+});
+
+test('moduleDirectory array', function (t) {
+    t.plan(6);
+    var dir = path.join(__dirname, 'module_dir');
+    var aopts = {
+        basedir: dir,
+        moduleDirectory: ['xmodules', 'ymodules', 'zmodules']
+    };
+    resolve('aaa', aopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, '/xmodules/aaa/index.js'));
+    });
+
+    var bopts = {
+        basedir: dir,
+        moduleDirectory: ['zmodules', 'ymodules', 'xmodules']
+    };
+    resolve('aaa', bopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, '/ymodules/aaa/index.js'));
+    });
+
+    var copts = {
+        basedir: dir,
+        moduleDirectory: ['xmodules', 'ymodules', 'zmodules']
+    };
+    resolve('bbb', copts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, '/zmodules/bbb/main.js'));
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+  "main": "main.js"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/node-modules-paths.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/node-modules-paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/node-modules-paths.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,143 @@
+var test = require('tape');
+var path = require('path');
+var parse = path.parse || require('path-parse');
+var keys = require('object-keys');
+
+var nodeModulesPaths = require('../lib/node-modules-paths');
+
+function verifyDirs(t, start, dirs, moduleDirectories, paths) {
+    var moduleDirs = [].concat(moduleDirectories || 'node_modules');
+    if (paths) {
+        for (var k = 0; k < paths.length; ++k) {
+            moduleDirs.push(path.basename(paths[k]));
+        }
+    }
+
+    var foundModuleDirs = {};
+    var uniqueDirs = {};
+    var parsedDirs = {};
+    for (var i = 0; i < dirs.length; ++i) {
+        var parsed = parse(dirs[i]);
+        if (!foundModuleDirs[parsed.base]) { foundModuleDirs[parsed.base] = 0; }
+        foundModuleDirs[parsed.base] += 1;
+        parsedDirs[parsed.dir] = true;
+        uniqueDirs[dirs[i]] = true;
+    }
+    t.equal(keys(parsedDirs).length >= start.split(path.sep).length, true, 'there are >= dirs than "start" has');
+    var foundModuleDirNames = keys(foundModuleDirs);
+    t.deepEqual(foundModuleDirNames, moduleDirs, 'all desired module dirs were found');
+    t.equal(keys(uniqueDirs).length, dirs.length, 'all dirs provided were unique');
+
+    var counts = {};
+    for (var j = 0; j < foundModuleDirNames.length; ++j) {
+        counts[foundModuleDirs[j]] = true;
+    }
+    t.equal(keys(counts).length, 1, 'all found module directories had the same count');
+}
+
+test('node-modules-paths', function (t) {
+    t.test('no options', function (t) {
+        var start = path.join(__dirname, 'resolver');
+        var dirs = nodeModulesPaths(start);
+
+        verifyDirs(t, start, dirs);
+
+        t.end();
+    });
+
+    t.test('empty options', function (t) {
+        var start = path.join(__dirname, 'resolver');
+        var dirs = nodeModulesPaths(start, {});
+
+        verifyDirs(t, start, dirs);
+
+        t.end();
+    });
+
+    t.test('with paths=array option', function (t) {
+        var start = path.join(__dirname, 'resolver');
+        var paths = ['a', 'b'];
+        var dirs = nodeModulesPaths(start, { paths: paths });
+
+        verifyDirs(t, start, dirs, null, paths);
+
+        t.end();
+    });
+
+    t.test('with paths=function option', function (t) {
+        function paths(request, absoluteStart, getNodeModulesDirs, opts) {
+            return getNodeModulesDirs().concat(path.join(absoluteStart, 'not node modules', request));
+        }
+
+        var start = path.join(__dirname, 'resolver');
+        var dirs = nodeModulesPaths(start, { paths: paths }, 'pkg');
+
+        verifyDirs(t, start, dirs, null, [path.join(start, 'not node modules', 'pkg')]);
+
+        t.end();
+    });
+
+    t.test('with paths=function skipping node modules resolution', function (t) {
+        function paths(request, absoluteStart, getNodeModulesDirs, opts) {
+            return [];
+        }
+        var start = path.join(__dirname, 'resolver');
+        var dirs = nodeModulesPaths(start, { paths: paths });
+        t.deepEqual(dirs, [], 'no node_modules was computed');
+        t.end();
+    });
+
+    t.test('with moduleDirectory option', function (t) {
+        var start = path.join(__dirname, 'resolver');
+        var moduleDirectory = 'not node modules';
+        var dirs = nodeModulesPaths(start, { moduleDirectory: moduleDirectory });
+
+        verifyDirs(t, start, dirs, moduleDirectory);
+
+        t.end();
+    });
+
+    t.test('with 1 moduleDirectory and paths options', function (t) {
+        var start = path.join(__dirname, 'resolver');
+        var paths = ['a', 'b'];
+        var moduleDirectory = 'not node modules';
+        var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectory });
+
+        verifyDirs(t, start, dirs, moduleDirectory, paths);
+
+        t.end();
+    });
+
+    t.test('with 1+ moduleDirectory and paths options', function (t) {
+        var start = path.join(__dirname, 'resolver');
+        var paths = ['a', 'b'];
+        var moduleDirectories = ['not node modules', 'other modules'];
+        var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectories });
+
+        verifyDirs(t, start, dirs, moduleDirectories, paths);
+
+        t.end();
+    });
+
+    t.test('combine paths correctly on Windows', function (t) {
+        var start = 'C:\\Users\\username\\myProject\\src';
+        var paths = [];
+        var moduleDirectories = ['node_modules', start];
+        var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectories });
+
+        t.equal(dirs.indexOf(path.resolve(start)) > -1, true, 'should contain start dir');
+
+        t.end();
+    });
+
+    t.test('combine paths correctly on non-Windows', { skip: process.platform === 'win32' }, function (t) {
+        var start = '/Users/username/git/myProject/src';
+        var paths = [];
+        var moduleDirectories = ['node_modules', '/Users/username/git/myProject/src'];
+        var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectories });
+
+        t.equal(dirs.indexOf(path.resolve(start)) > -1, true, 'should contain start dir');
+
+        t.end();
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,70 @@
+var fs = require('fs');
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('$NODE_PATH', function (t) {
+    t.plan(8);
+
+    function isDir(dir, cb) {
+        if (dir === '/node_path' || dir === 'node_path/x') {
+            return cb(null, true);
+        }
+        fs.stat(dir, function (err, stat) {
+            if (!err) {
+                return cb(null, stat.isDirectory());
+            }
+            if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false);
+            return cb(err);
+        });
+    }
+
+    resolve('aaa', {
+        paths: [
+            path.join(__dirname, '/node_path/x'),
+            path.join(__dirname, '/node_path/y')
+        ],
+        basedir: __dirname,
+        isDirectory: isDir
+    }, function (err, res) {
+        t.error(err);
+        t.equal(res, path.join(__dirname, '/node_path/x/aaa/index.js'), 'aaa resolves');
+    });
+
+    resolve('bbb', {
+        paths: [
+            path.join(__dirname, '/node_path/x'),
+            path.join(__dirname, '/node_path/y')
+        ],
+        basedir: __dirname,
+        isDirectory: isDir
+    }, function (err, res) {
+        t.error(err);
+        t.equal(res, path.join(__dirname, '/node_path/y/bbb/index.js'), 'bbb resolves');
+    });
+
+    resolve('ccc', {
+        paths: [
+            path.join(__dirname, '/node_path/x'),
+            path.join(__dirname, '/node_path/y')
+        ],
+        basedir: __dirname,
+        isDirectory: isDir
+    }, function (err, res) {
+        t.error(err);
+        t.equal(res, path.join(__dirname, '/node_path/x/ccc/index.js'), 'ccc resolves');
+    });
+
+    // ensure that relative paths still resolve against the regular `node_modules` correctly
+    resolve('tap', {
+        paths: [
+            'node_path'
+        ],
+        basedir: path.join(__dirname, 'node_path/x'),
+        isDirectory: isDir
+    }, function (err, res) {
+        var root = require('tap/package.json').main; // eslint-disable-line global-require
+        t.error(err);
+        t.equal(res.replace('/node_modules/.vlt/··tap@0.4.13/', '/'), path.resolve(__dirname, '..', 'node_modules/tap', root), 'tap resolves');
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/nonstring.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/nonstring.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/nonstring.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,9 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('nonstring', function (t) {
+    t.plan(1);
+    resolve(555, function (err, res, pkg) {
+        t.ok(err);
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,75 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+var resolverDir = path.join(__dirname, '/pathfilter/deep_ref');
+
+function pathFilterFactory(t) {
+    return function (pkg, x, remainder) {
+        t.equal(pkg.version, '1.2.3');
+        t.equal(x, path.join(resolverDir, 'node_modules/deep/ref'));
+        t.equal(remainder, 'ref');
+        return 'alt';
+    };
+}
+
+test('#62: deep module references and the pathFilter', function (t) {
+    t.test('deep/ref.js', function (st) {
+        st.plan(3);
+
+        resolve('deep/ref', { basedir: resolverDir }, function (err, res, pkg) {
+            if (err) st.fail(err);
+
+            st.equal(pkg.version, '1.2.3');
+            st.equal(res, path.join(resolverDir, 'node_modules/deep/ref.js'));
+        });
+
+        var res = resolve.sync('deep/ref', { basedir: resolverDir });
+        st.equal(res, path.join(resolverDir, 'node_modules/deep/ref.js'));
+    });
+
+    t.test('deep/deeper/ref', function (st) {
+        st.plan(4);
+
+        resolve(
+            'deep/deeper/ref',
+            { basedir: resolverDir },
+            function (err, res, pkg) {
+                if (err) t.fail(err);
+                st.notEqual(pkg, undefined);
+                st.equal(pkg.version, '1.2.3');
+                st.equal(res, path.join(resolverDir, 'node_modules/deep/deeper/ref.js'));
+            }
+        );
+
+        var res = resolve.sync(
+            'deep/deeper/ref',
+            { basedir: resolverDir }
+        );
+        st.equal(res, path.join(resolverDir, 'node_modules/deep/deeper/ref.js'));
+    });
+
+    t.test('deep/ref alt', function (st) {
+        st.plan(8);
+
+        var pathFilter = pathFilterFactory(st);
+
+        var res = resolve.sync(
+            'deep/ref',
+            { basedir: resolverDir, pathFilter: pathFilter }
+        );
+        st.equal(res, path.join(resolverDir, 'node_modules/deep/alt.js'));
+
+        resolve(
+            'deep/ref',
+            { basedir: resolverDir, pathFilter: pathFilter },
+            function (err, res, pkg) {
+                if (err) st.fail(err);
+                st.equal(res, path.join(resolverDir, 'node_modules/deep/alt.js'));
+                st.end();
+            }
+        );
+    });
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter_sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,24 @@
+var test = require('tape');
+var path = require('path');
+var resolve = require('../');
+
+test('synchronous pathfilter', function (t) {
+    var res;
+    var resolverDir = __dirname + '/pathfilter/deep_ref';
+    function pathFilter(pkg, x, remainder) {
+        t.equal(pkg.version, '1.2.3');
+        t.equal(x, path.join(resolverDir, 'node_modules', 'deep', 'ref'));
+        t.equal(remainder, 'ref');
+        return 'alt';
+    }
+
+    res = resolve.sync('deep/ref', { basedir: resolverDir });
+    t.equal(res, path.join(resolverDir, 'node_modules', 'deep', 'ref.js'));
+
+    res = resolve.sync('deep/deeper/ref', { basedir: resolverDir });
+    t.equal(res, path.join(resolverDir, 'node_modules', 'deep', 'deeper', 'ref.js'));
+
+    res = resolve.sync('deep/ref', { basedir: resolverDir, pathFilter: pathFilter });
+    t.equal(res, path.join(resolverDir, 'node_modules', 'deep', 'alt.js'));
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,23 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('precedence', function (t) {
+    t.plan(3);
+    var dir = path.join(__dirname, 'precedence/aaa');
+
+    resolve('./', { basedir: dir }, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, 'index.js'));
+        t.equal(pkg.name, 'resolve');
+    });
+});
+
+test('./ should not load ${dir}.js', function (t) { // eslint-disable-line no-template-curly-in-string
+    t.plan(1);
+    var dir = path.join(__dirname, 'precedence/bbb');
+
+    resolve('./', { basedir: dir }, function (err, res, pkg) {
+        t.ok(err);
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,613 @@
+var path = require('path');
+var fs = require('fs');
+var test = require('tape');
+var resolve = require('../');
+var async = require('../async');
+
+test('`./async` entry point', function (t) {
+    t.equal(resolve, async, '`./async` entry point is the same as `main`');
+    t.end();
+});
+
+test('async foo', function (t) {
+    t.plan(12);
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./foo', { basedir: dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'foo.js'));
+        t.equal(pkg && pkg.name, 'resolve');
+    });
+
+    resolve('./foo.js', { basedir: dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'foo.js'));
+        t.equal(pkg && pkg.name, 'resolve');
+    });
+
+    resolve('./foo', { basedir: dir, 'package': { main: 'resolver' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'foo.js'));
+        t.equal(pkg && pkg.main, 'resolver');
+    });
+
+    resolve('./foo.js', { basedir: dir, 'package': { main: 'resolver' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'foo.js'));
+        t.equal(pkg.main, 'resolver');
+    });
+
+    resolve('./foo', { basedir: dir, filename: path.join(dir, 'baz.js') }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'foo.js'));
+    });
+
+    resolve('foo', { basedir: dir }, function (err) {
+        t.equal(err.message, "Cannot find module 'foo' from '" + path.resolve(dir) + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+
+    // Test that filename is reported as the "from" value when passed.
+    resolve('foo', { basedir: dir, filename: path.join(dir, 'baz.js') }, function (err) {
+        t.equal(err.message, "Cannot find module 'foo' from '" + path.join(dir, 'baz.js') + "'");
+    });
+});
+
+test('bar', function (t) {
+    t.plan(6);
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('foo', { basedir: dir + '/bar' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'bar/node_modules/foo/index.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('foo', { basedir: dir + '/bar' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'bar/node_modules/foo/index.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('foo', { basedir: dir + '/bar', 'package': { main: 'bar' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'bar/node_modules/foo/index.js'));
+        t.equal(pkg.main, 'bar');
+    });
+});
+
+test('baz', function (t) {
+    t.plan(4);
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./baz', { basedir: dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'baz/quux.js'));
+        t.equal(pkg.main, 'quux.js');
+    });
+
+    resolve('./baz', { basedir: dir, 'package': { main: 'resolver' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'baz/quux.js'));
+        t.equal(pkg.main, 'quux.js');
+    });
+});
+
+test('biz', function (t) {
+    t.plan(24);
+    var dir = path.join(__dirname, 'resolver/biz/node_modules');
+
+    resolve('./grux', { basedir: dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'grux/index.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('./grux', { basedir: dir, 'package': { main: 'biz' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'grux/index.js'));
+        t.equal(pkg.main, 'biz');
+    });
+
+    resolve('./garply', { basedir: dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'garply/lib/index.js'));
+        t.equal(pkg.main, './lib');
+    });
+
+    resolve('./garply', { basedir: dir, 'package': { main: 'biz' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'garply/lib/index.js'));
+        t.equal(pkg.main, './lib');
+    });
+
+    resolve('tiv', { basedir: dir + '/grux' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'tiv/index.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('tiv', { basedir: dir + '/grux', 'package': { main: 'grux' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'tiv/index.js'));
+        t.equal(pkg.main, 'grux');
+    });
+
+    resolve('tiv', { basedir: dir + '/garply' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'tiv/index.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('tiv', { basedir: dir + '/garply', 'package': { main: './lib' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'tiv/index.js'));
+        t.equal(pkg.main, './lib');
+    });
+
+    resolve('grux', { basedir: dir + '/tiv' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'grux/index.js'));
+        t.equal(pkg, undefined);
+    });
+
+    resolve('grux', { basedir: dir + '/tiv', 'package': { main: 'tiv' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'grux/index.js'));
+        t.equal(pkg.main, 'tiv');
+    });
+
+    resolve('garply', { basedir: dir + '/tiv' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'garply/lib/index.js'));
+        t.equal(pkg.main, './lib');
+    });
+
+    resolve('garply', { basedir: dir + '/tiv', 'package': { main: 'tiv' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'garply/lib/index.js'));
+        t.equal(pkg.main, './lib');
+    });
+});
+
+test('quux', function (t) {
+    t.plan(2);
+    var dir = path.join(__dirname, 'resolver/quux');
+
+    resolve('./foo', { basedir: dir, 'package': { main: 'quux' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'foo/index.js'));
+        t.equal(pkg.main, 'quux');
+    });
+});
+
+test('normalize', function (t) {
+    t.plan(2);
+    var dir = path.join(__dirname, 'resolver/biz/node_modules/grux');
+
+    resolve('../grux', { basedir: dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'index.js'));
+        t.equal(pkg, undefined);
+    });
+});
+
+test('cup', function (t) {
+    t.plan(5);
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./cup', { basedir: dir, extensions: ['.js', '.coffee'] }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'cup.coffee'));
+    });
+
+    resolve('./cup.coffee', { basedir: dir }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'cup.coffee'));
+    });
+
+    resolve('./cup', { basedir: dir, extensions: ['.js'] }, function (err, res) {
+        t.equal(err.message, "Cannot find module './cup' from '" + path.resolve(dir) + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+
+    // Test that filename is reported as the "from" value when passed.
+    resolve('./cup', { basedir: dir, extensions: ['.js'], filename: path.join(dir, 'cupboard.js') }, function (err, res) {
+        t.equal(err.message, "Cannot find module './cup' from '" + path.join(dir, 'cupboard.js') + "'");
+    });
+});
+
+test('mug', function (t) {
+    t.plan(3);
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./mug', { basedir: dir }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'mug.js'));
+    });
+
+    resolve('./mug', { basedir: dir, extensions: ['.coffee', '.js'] }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, '/mug.coffee'));
+    });
+
+    resolve('./mug', { basedir: dir, extensions: ['.js', '.coffee'] }, function (err, res) {
+        t.equal(res, path.join(dir, '/mug.js'));
+    });
+});
+
+test('other path', function (t) {
+    t.plan(6);
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'bar');
+    var otherDir = path.join(resolverDir, 'other_path');
+
+    resolve('root', { basedir: dir, paths: [otherDir] }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(resolverDir, 'other_path/root.js'));
+    });
+
+    resolve('lib/other-lib', { basedir: dir, paths: [otherDir] }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(resolverDir, 'other_path/lib/other-lib.js'));
+    });
+
+    resolve('root', { basedir: dir }, function (err, res) {
+        t.equal(err.message, "Cannot find module 'root' from '" + path.resolve(dir) + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+
+    resolve('zzz', { basedir: dir, paths: [otherDir] }, function (err, res) {
+        t.equal(err.message, "Cannot find module 'zzz' from '" + path.resolve(dir) + "'");
+        t.equal(err.code, 'MODULE_NOT_FOUND');
+    });
+});
+
+test('path iterator', function (t) {
+    t.plan(2);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+
+    function exactIterator(x, start, getPackageCandidates, opts) {
+        return [path.join(resolverDir, x)];
+    }
+
+    resolve('baz', { packageIterator: exactIterator }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(resolverDir, 'baz/quux.js'));
+        t.equal(pkg && pkg.name, 'baz');
+    });
+});
+
+test('empty main', function (t) {
+    t.plan(1);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'empty_main');
+
+    resolve('./empty_main', { basedir: resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'index.js'));
+    });
+});
+
+test('incorrect main', function (t) {
+    t.plan(1);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'incorrect_main');
+
+    resolve('./incorrect_main', { basedir: resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'index.js'));
+    });
+});
+
+test('missing index', function (t) {
+    t.plan(2);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+    resolve('./missing_index', { basedir: resolverDir }, function (err, res, pkg) {
+        t.ok(err instanceof Error);
+        t.equal(err && err.code, 'INCORRECT_PACKAGE_MAIN', 'error has correct error code');
+    });
+});
+
+test('missing main', function (t) {
+    t.plan(1);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'missing_main');
+
+    resolve('./missing_main', { basedir: resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'index.js'));
+    });
+});
+
+test('null main', function (t) {
+    t.plan(1);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'null_main');
+
+    resolve('./null_main', { basedir: resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'index.js'));
+    });
+});
+
+test('main: false', function (t) {
+    t.plan(2);
+
+    var basedir = path.join(__dirname, 'resolver');
+    var dir = path.join(basedir, 'false_main');
+    resolve('./false_main', { basedir: basedir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(
+            res,
+            path.join(dir, 'index.js'),
+            '`"main": false`: resolves to `index.js`'
+        );
+        t.deepEqual(pkg, {
+            name: 'false_main',
+            main: false
+        });
+    });
+});
+
+test('without basedir', function (t) {
+    t.plan(1);
+
+    var dir = path.join(__dirname, 'resolver/without_basedir');
+    var tester = require(path.join(dir, 'main.js')); // eslint-disable-line global-require
+
+    tester(t, function (err, res, pkg) {
+        if (err) {
+            t.fail(err);
+        } else {
+            t.equal(res, path.join(dir, 'node_modules/mymodule.js'));
+        }
+    });
+});
+
+test('#52 - incorrectly resolves module-paths like "./someFolder/" when there is a file of the same name', function (t) {
+    t.plan(2);
+
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./foo', { basedir: path.join(dir, 'same_names') }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'same_names/foo.js'));
+    });
+
+    resolve('./foo/', { basedir: path.join(dir, 'same_names') }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'same_names/foo/index.js'));
+    });
+});
+
+test('#211 - incorrectly resolves module-paths like "." when from inside a folder with a sibling file of the same name', function (t) {
+    t.plan(2);
+
+    var dir = path.join(__dirname, 'resolver');
+
+    resolve('./', { basedir: path.join(dir, 'same_names/foo') }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'same_names/foo/index.js'));
+    });
+
+    resolve('.', { basedir: path.join(dir, 'same_names/foo') }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, path.join(dir, 'same_names/foo/index.js'));
+    });
+});
+
+test('async: #121 - treating an existing file as a dir when no basedir', function (t) {
+    var testFile = path.basename(__filename);
+
+    t.test('sanity check', function (st) {
+        st.plan(1);
+        resolve('./' + testFile, function (err, res, pkg) {
+            if (err) t.fail(err);
+            st.equal(res, __filename, 'sanity check');
+        });
+    });
+
+    t.test('with a fake directory', function (st) {
+        st.plan(4);
+
+        resolve('./' + testFile + '/blah', function (err, res, pkg) {
+            st.ok(err, 'there is an error');
+            st.notOk(res, 'no result');
+
+            st.equal(err && err.code, 'MODULE_NOT_FOUND', 'error code matches require.resolve');
+            st.equal(
+                err && err.message,
+                'Cannot find module \'./' + testFile + '/blah\' from \'' + __dirname + '\'',
+                'can not find nonexistent module'
+            );
+            st.end();
+        });
+    });
+
+    t.end();
+});
+
+test('async dot main', function (t) {
+    var start = new Date();
+    t.plan(3);
+    resolve('./resolver/dot_main', function (err, ret) {
+        t.notOk(err);
+        t.equal(ret, path.join(__dirname, 'resolver/dot_main/index.js'));
+        t.ok(new Date() - start < 50, 'resolve.sync timedout');
+        t.end();
+    });
+});
+
+test('async dot slash main', function (t) {
+    var start = new Date();
+    t.plan(3);
+    resolve('./resolver/dot_slash_main', function (err, ret) {
+        t.notOk(err);
+        t.equal(ret, path.join(__dirname, 'resolver/dot_slash_main/index.js'));
+        t.ok(new Date() - start < 50, 'resolve.sync timedout');
+        t.end();
+    });
+});
+
+test('not a directory', function (t) {
+    t.plan(6);
+    var path = './foo';
+    resolve(path, { basedir: __filename }, function (err, res, pkg) {
+        t.ok(err, 'a non-directory errors');
+        t.equal(arguments.length, 1);
+        t.equal(res, undefined);
+        t.equal(pkg, undefined);
+
+        t.equal(err && err.message, 'Provided basedir "' + __filename + '" is not a directory, or a symlink to a directory');
+        t.equal(err && err.code, 'INVALID_BASEDIR');
+    });
+});
+
+test('non-string "main" field in package.json', function (t) {
+    t.plan(5);
+
+    var dir = path.join(__dirname, 'resolver');
+    resolve('./invalid_main', { basedir: dir }, function (err, res, pkg) {
+        t.ok(err, 'errors on non-string main');
+        t.equal(err.message, 'package “invalid_main” `main` must be a string');
+        t.equal(err.code, 'INVALID_PACKAGE_MAIN');
+        t.equal(res, undefined, 'res is undefined');
+        t.equal(pkg, undefined, 'pkg is undefined');
+    });
+});
+
+test('non-string "main" field in package.json', function (t) {
+    t.plan(5);
+
+    var dir = path.join(__dirname, 'resolver');
+    resolve('./invalid_main', { basedir: dir }, function (err, res, pkg) {
+        t.ok(err, 'errors on non-string main');
+        t.equal(err.message, 'package “invalid_main” `main` must be a string');
+        t.equal(err.code, 'INVALID_PACKAGE_MAIN');
+        t.equal(res, undefined, 'res is undefined');
+        t.equal(pkg, undefined, 'pkg is undefined');
+    });
+});
+
+test('browser field in package.json', function (t) {
+    t.plan(3);
+
+    var dir = path.join(__dirname, 'resolver');
+    resolve(
+        './browser_field',
+        {
+            basedir: dir,
+            packageFilter: function packageFilter(pkg) {
+                if (pkg.browser) {
+                    pkg.main = pkg.browser; // eslint-disable-line no-param-reassign
+                    delete pkg.browser; // eslint-disable-line no-param-reassign
+                }
+                return pkg;
+            }
+        },
+        function (err, res, pkg) {
+            if (err) t.fail(err);
+            t.equal(res, path.join(dir, 'browser_field', 'b.js'));
+            t.equal(pkg && pkg.main, 'b');
+            t.equal(pkg && pkg.browser, undefined);
+        }
+    );
+});
+
+test('absolute paths', function (t) {
+    t.plan(4);
+
+    var extensionless = __filename.slice(0, -path.extname(__filename).length);
+
+    resolve(__filename, function (err, res) {
+        t.equal(
+            res,
+            __filename,
+            'absolute path to this file resolves'
+        );
+    });
+    resolve(extensionless, function (err, res) {
+        t.equal(
+            res,
+            __filename,
+            'extensionless absolute path to this file resolves'
+        );
+    });
+    resolve(__filename, { basedir: process.cwd() }, function (err, res) {
+        t.equal(
+            res,
+            __filename,
+            'absolute path to this file with a basedir resolves'
+        );
+    });
+    resolve(extensionless, { basedir: process.cwd() }, function (err, res) {
+        t.equal(
+            res,
+            __filename,
+            'extensionless absolute path to this file with a basedir resolves'
+        );
+    });
+});
+
+var malformedDir = path.join(__dirname, 'resolver/malformed_package_json');
+test('malformed package.json', { skip: !fs.existsSync(malformedDir) }, function (t) {
+    /* eslint operator-linebreak: ["error", "before"], function-paren-newline: "off" */
+    t.plan(
+        (3 * 3) // 3 sets of 3 assertions in the final callback
+        + 2 // 1 readPackage call with malformed package.json
+    );
+
+    var basedir = malformedDir;
+    var expected = path.join(basedir, 'index.js');
+
+    resolve('./index.js', { basedir: basedir }, function (err, res, pkg) {
+        t.error(err, 'no error');
+        t.equal(res, expected, 'malformed package.json is silently ignored');
+        t.equal(pkg, undefined, 'malformed package.json gives an undefined `pkg` argument');
+    });
+
+    resolve(
+        './index.js',
+        {
+            basedir: basedir,
+            packageFilter: function (pkg, pkgfile, dir) {
+                t.fail('should not reach here');
+            }
+        },
+        function (err, res, pkg) {
+            t.error(err, 'with packageFilter: no error');
+            t.equal(res, expected, 'with packageFilter: malformed package.json is silently ignored');
+            t.equal(pkg, undefined, 'with packageFilter: malformed package.json gives an undefined `pkg` argument');
+        }
+    );
+
+    resolve(
+        './index.js',
+        {
+            basedir: basedir,
+            readPackage: function (readFile, pkgfile, cb) {
+                t.equal(pkgfile, path.join(basedir, 'package.json'), 'readPackageSync: `pkgfile` is package.json path');
+                readFile(pkgfile, function (err, result) {
+                    try {
+                        cb(null, JSON.parse(result));
+                    } catch (e) {
+                        t.ok(e instanceof SyntaxError, 'readPackage: malformed package.json parses as a syntax error');
+                        cb(e);
+                    }
+                });
+            }
+        },
+        function (err, res, pkg) {
+            t.error(err, 'with readPackage: no error');
+            t.equal(res, expected, 'with readPackage: malformed package.json is silently ignored');
+            t.equal(pkg, undefined, 'with readPackage: malformed package.json gives an undefined `pkg` argument');
+        }
+    );
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4 @@
+{
+    "name": "baz",
+    "main": "quux.js"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,5 @@
+{
+  "name": "browser_field",
+  "main": "a",
+  "browser": "b"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/cup.coffee
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/cup.coffee	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/cup.coffee	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+    "main": "."
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+    "main": "./"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+  "main": ""
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,4 @@
+{
+	"name": "false_main",
+	"main": false
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+    "main": "wrong.js"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/invalid_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/invalid_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/invalid_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+{
+  "name": "invalid_main",
+  "main": [
+    "why is this a thing",
+    "srsly omg wtf"
+  ]
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_index/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_index/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_index/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+  "main": "index.js"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+  "notmain": "index.js"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/lerna.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/lerna.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/lerna.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,6 @@
+{
+  "packages": [
+    "packages/*"
+  ],
+  "version": "0.0.0"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+{
+  "name": "ljharb-monorepo-symlink-test",
+  "private": true,
+  "version": "0.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "postinstall": "lerna bootstrap",
+    "test": "node packages/package-a"
+  },
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "jquery": "^3.3.1",
+    "resolve": "../../../"
+  },
+  "devDependencies": {
+    "lerna": "^3.4.3"
+  }
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,35 @@
+'use strict';
+
+var assert = require('assert');
+var path = require('path');
+var resolve = require('resolve');
+
+var basedir = __dirname + '/node_modules/@my-scope/package-b';
+
+var expected = path.join(__dirname, '../../node_modules/jquery/dist/jquery.js');
+
+/*
+ * preserveSymlinks === false
+ * will search NPM package from
+ * - packages/package-b/node_modules
+ * - packages/node_modules
+ * - node_modules
+ */
+assert.equal(resolve.sync('jquery', { basedir: basedir, preserveSymlinks: false }), expected);
+assert.equal(resolve.sync('../../node_modules/jquery', { basedir: basedir, preserveSymlinks: false }), expected);
+
+/*
+ * preserveSymlinks === true
+ * will search NPM package from
+ * - packages/package-a/node_modules/@my-scope/packages/package-b/node_modules
+ * - packages/package-a/node_modules/@my-scope/packages/node_modules
+ * - packages/package-a/node_modules/@my-scope/node_modules
+ * - packages/package-a/node_modules/node_modules
+ * - packages/package-a/node_modules
+ * - packages/node_modules
+ * - node_modules
+ */
+assert.equal(resolve.sync('jquery', { basedir: basedir, preserveSymlinks: true }), expected);
+assert.equal(resolve.sync('../../../../../node_modules/jquery', { basedir: basedir, preserveSymlinks: true }), expected);
+
+console.log(' * all monorepo paths successfully resolved through symlinks');
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,14 @@
+{
+  "name": "@my-scope/package-a",
+  "version": "0.0.0",
+  "private": true,
+  "description": "",
+  "license": "MIT",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: run tests from root\" && exit 1"
+  },
+  "dependencies": {
+    "@my-scope/package-b": "^0.0.0"
+  }
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,14 @@
+{
+  "name": "@my-scope/package-b",
+  "private": true,
+  "version": "0.0.0",
+  "description": "",
+  "license": "MIT",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: run tests from root\" && exit 1"
+  },
+  "dependencies": {
+    "@my-scope/package-a": "^0.0.0"
+  }
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,26 @@
+var a = require.resolve('buffer/').replace(process.cwd(), '$CWD');
+var b;
+var c;
+
+function test() {
+    console.log(a, ': require.resolve, preserveSymlinks ' + (process.execArgv.indexOf('preserve-symlinks') > -1 ? 'true' : 'false'));
+    console.log(b, ': preserveSymlinks true');
+    console.log(c, ': preserveSymlinks false');
+
+    if (a !== b && a !== c) {
+        throw 'async: no match';
+    }
+    console.log('async: success! a matched either b or c\n');
+}
+
+require('resolve')('buffer/', { preserveSymlinks: true }, function (err, result) {
+    if (err) { throw err; }
+    b = result.replace(process.cwd(), '$CWD');
+    if (b && c) { test(); }
+});
+require('resolve')('buffer/', { preserveSymlinks: false }, function (err, result) {
+    if (err) { throw err; }
+    c = result.replace(process.cwd(), '$CWD');
+    if (b && c) { test(); }
+});
+
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,15 @@
+{
+  "name": "mylib",
+  "version": "0.0.0",
+  "description": "",
+  "private": true,
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "buffer": "*"
+  }
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,12 @@
+var a = require.resolve('buffer/').replace(process.cwd(), '$CWD');
+var b = require('resolve').sync('buffer/', { preserveSymlinks: true }).replace(process.cwd(), '$CWD');
+var c = require('resolve').sync('buffer/', { preserveSymlinks: false }).replace(process.cwd(), '$CWD');
+
+console.log(a, ': require.resolve, preserveSymlinks ' + (process.execArgv.indexOf('preserve-symlinks') > -1 ? 'true' : 'false'));
+console.log(b, ': preserveSymlinks true');
+console.log(c, ': preserveSymlinks false');
+
+if (a !== b && a !== c) {
+    throw 'sync: no match';
+}
+console.log('sync: success! a matched either b or c\n');
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+  "main": null
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/package/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/package/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/package/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+{
+    "main": "bar.js"
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/without_basedir/main.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/without_basedir/main.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/without_basedir/main.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,5 @@
+var resolve = require('../../../');
+
+module.exports = function (t, cb) {
+    resolve('mymodule', null, cb);
+};
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver_sync.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver_sync.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,727 @@
+var path = require('path');
+var fs = require('fs');
+var test = require('tape');
+
+var resolve = require('../');
+var sync = require('../sync');
+
+var requireResolveSupportsPaths = require.resolve.length > 1
+    && !(/^v12\.[012]\./).test(process.version); // broken in v12.0-12.2, see https://github.com/nodejs/node/issues/27794
+
+var requireResolveDefaultPathsBroken = (/^v8\.9\.|^v9\.[01]\.0|^v9\.2\./).test(process.version);
+// broken in node v8.9.x, v9.0, v9.1, v9.2.x. see https://github.com/nodejs/node/pull/17113
+
+test('`./sync` entry point', function (t) {
+    t.equal(resolve.sync, sync, '`./sync` entry point is the same as `.sync` on `main`');
+    t.end();
+});
+
+test('foo', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+
+    t.equal(
+        resolve.sync('./foo', { basedir: dir }),
+        path.join(dir, 'foo.js'),
+        './foo'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./foo', { basedir: dir }),
+            require.resolve('./foo', { paths: [dir] }),
+            './foo: resolve.sync === require.resolve'
+        );
+    }
+
+    t.equal(
+        resolve.sync('./foo.js', { basedir: dir }),
+        path.join(dir, 'foo.js'),
+        './foo.js'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./foo.js', { basedir: dir }),
+            require.resolve('./foo.js', { paths: [dir] }),
+            './foo.js: resolve.sync === require.resolve'
+        );
+    }
+
+    t.equal(
+        resolve.sync('./foo.js', { basedir: dir, filename: path.join(dir, 'bar.js') }),
+        path.join(dir, 'foo.js')
+    );
+
+    t.throws(function () {
+        resolve.sync('foo', { basedir: dir });
+    });
+
+    // Test that filename is reported as the "from" value when passed.
+    t.throws(
+        function () {
+            resolve.sync('foo', { basedir: dir, filename: path.join(dir, 'bar.js') });
+        },
+        {
+            name: 'Error',
+            message: "Cannot find module 'foo' from '" + path.join(dir, 'bar.js') + "'"
+        }
+    );
+
+    t.end();
+});
+
+test('bar', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+
+    var basedir = path.join(dir, 'bar');
+
+    t.equal(
+        resolve.sync('foo', { basedir: basedir }),
+        path.join(dir, 'bar/node_modules/foo/index.js'),
+        'foo in bar'
+    );
+    if (!requireResolveDefaultPathsBroken && requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('foo', { basedir: basedir }),
+            require.resolve('foo', { paths: [basedir] }),
+            'foo in bar: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('baz', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+
+    t.equal(
+        resolve.sync('./baz', { basedir: dir }),
+        path.join(dir, 'baz/quux.js'),
+        './baz'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./baz', { basedir: dir }),
+            require.resolve('./baz', { paths: [dir] }),
+            './baz: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('biz', function (t) {
+    var dir = path.join(__dirname, 'resolver/biz/node_modules');
+
+    t.equal(
+        resolve.sync('./grux', { basedir: dir }),
+        path.join(dir, 'grux/index.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./grux', { basedir: dir }),
+            require.resolve('./grux', { paths: [dir] }),
+            './grux: resolve.sync === require.resolve'
+        );
+    }
+
+    var tivDir = path.join(dir, 'grux');
+    t.equal(
+        resolve.sync('tiv', { basedir: tivDir }),
+        path.join(dir, 'tiv/index.js')
+    );
+    if (!requireResolveDefaultPathsBroken && requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('tiv', { basedir: tivDir }),
+            require.resolve('tiv', { paths: [tivDir] }),
+            'tiv: resolve.sync === require.resolve'
+        );
+    }
+
+    var gruxDir = path.join(dir, 'tiv');
+    t.equal(
+        resolve.sync('grux', { basedir: gruxDir }),
+        path.join(dir, 'grux/index.js')
+    );
+    if (!requireResolveDefaultPathsBroken && requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('grux', { basedir: gruxDir }),
+            require.resolve('grux', { paths: [gruxDir] }),
+            'grux: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('normalize', function (t) {
+    var dir = path.join(__dirname, 'resolver/biz/node_modules/grux');
+
+    t.equal(
+        resolve.sync('../grux', { basedir: dir }),
+        path.join(dir, 'index.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('../grux', { basedir: dir }),
+            require.resolve('../grux', { paths: [dir] }),
+            '../grux: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('cup', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+
+    t.equal(
+        resolve.sync('./cup', {
+            basedir: dir,
+            extensions: ['.js', '.coffee']
+        }),
+        path.join(dir, 'cup.coffee'),
+        './cup -> ./cup.coffee'
+    );
+
+    t.equal(
+        resolve.sync('./cup.coffee', { basedir: dir }),
+        path.join(dir, 'cup.coffee'),
+        './cup.coffee'
+    );
+
+    t.throws(function () {
+        resolve.sync('./cup', {
+            basedir: dir,
+            extensions: ['.js']
+        });
+    });
+
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./cup.coffee', { basedir: dir, extensions: ['.js', '.coffee'] }),
+            require.resolve('./cup.coffee', { paths: [dir] }),
+            './cup.coffee: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('mug', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+
+    t.equal(
+        resolve.sync('./mug', { basedir: dir }),
+        path.join(dir, 'mug.js'),
+        './mug -> ./mug.js'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./mug', { basedir: dir }),
+            require.resolve('./mug', { paths: [dir] }),
+            './mug: resolve.sync === require.resolve'
+        );
+    }
+
+    t.equal(
+        resolve.sync('./mug', {
+            basedir: dir,
+            extensions: ['.coffee', '.js']
+        }),
+        path.join(dir, 'mug.coffee'),
+        './mug -> ./mug.coffee'
+    );
+
+    t.equal(
+        resolve.sync('./mug', {
+            basedir: dir,
+            extensions: ['.js', '.coffee']
+        }),
+        path.join(dir, 'mug.js'),
+        './mug -> ./mug.js'
+    );
+
+    t.end();
+});
+
+test('other path', function (t) {
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'bar');
+    var otherDir = path.join(resolverDir, 'other_path');
+
+    t.equal(
+        resolve.sync('root', {
+            basedir: dir,
+            paths: [otherDir]
+        }),
+        path.join(resolverDir, 'other_path/root.js')
+    );
+
+    t.equal(
+        resolve.sync('lib/other-lib', {
+            basedir: dir,
+            paths: [otherDir]
+        }),
+        path.join(resolverDir, 'other_path/lib/other-lib.js')
+    );
+
+    t.throws(function () {
+        resolve.sync('root', { basedir: dir });
+    });
+
+    t.throws(function () {
+        resolve.sync('zzz', {
+            basedir: dir,
+            paths: [otherDir]
+        });
+    });
+
+    t.end();
+});
+
+test('path iterator', function (t) {
+    var resolverDir = path.join(__dirname, 'resolver');
+
+    function exactIterator(x, start, getPackageCandidates, opts) {
+        return [path.join(resolverDir, x)];
+    }
+
+    t.equal(
+        resolve.sync('baz', { packageIterator: exactIterator }),
+        path.join(resolverDir, 'baz/quux.js')
+    );
+
+    t.end();
+});
+
+test('incorrect main', function (t) {
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'incorrect_main');
+
+    t.equal(
+        resolve.sync('./incorrect_main', { basedir: resolverDir }),
+        path.join(dir, 'index.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./incorrect_main', { basedir: resolverDir }),
+            require.resolve('./incorrect_main', { paths: [resolverDir] }),
+            './incorrect_main: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('missing index', function (t) {
+    t.plan(requireResolveSupportsPaths ? 2 : 1);
+
+    var resolverDir = path.join(__dirname, 'resolver');
+    try {
+        resolve.sync('./missing_index', { basedir: resolverDir });
+        t.fail('did not fail');
+    } catch (err) {
+        t.equal(err && err.code, 'INCORRECT_PACKAGE_MAIN', 'error has correct error code');
+    }
+    if (requireResolveSupportsPaths) {
+        try {
+            require.resolve('./missing_index', { basedir: resolverDir });
+            t.fail('require.resolve did not fail');
+        } catch (err) {
+            t.equal(err && err.code, 'MODULE_NOT_FOUND', 'error has correct error code');
+        }
+    }
+});
+
+test('missing main', function (t) {
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'missing_main');
+
+    t.equal(
+        resolve.sync('./missing_main', { basedir: resolverDir }),
+        path.join(dir, 'index.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./missing_main', { basedir: resolverDir }),
+            require.resolve('./missing_main', { paths: [resolverDir] }),
+            '"main" missing: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('null main', function (t) {
+    var resolverDir = path.join(__dirname, 'resolver');
+    var dir = path.join(resolverDir, 'null_main');
+
+    t.equal(
+        resolve.sync('./null_main', { basedir: resolverDir }),
+        path.join(dir, 'index.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./null_main', { basedir: resolverDir }),
+            require.resolve('./null_main', { paths: [resolverDir] }),
+            '`"main": null`: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('main: false', function (t) {
+    var basedir = path.join(__dirname, 'resolver');
+    var dir = path.join(basedir, 'false_main');
+    t.equal(
+        resolve.sync('./false_main', { basedir: basedir }),
+        path.join(dir, 'index.js'),
+        '`"main": false`: resolves to `index.js`'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./false_main', { basedir: basedir }),
+            require.resolve('./false_main', { paths: [basedir] }),
+            '`"main": false`: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+function stubStatSync(fn) {
+    var statSync = fs.statSync;
+    try {
+        fs.statSync = function () {
+            throw new EvalError('Unknown Error');
+        };
+        return fn();
+    } finally {
+        fs.statSync = statSync;
+    }
+}
+
+test('#79 - re-throw non ENOENT errors from stat', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+
+    stubStatSync(function () {
+        t.throws(function () {
+            resolve.sync('foo', { basedir: dir });
+        }, /Unknown Error/);
+    });
+
+    t.end();
+});
+
+test('#52 - incorrectly resolves module-paths like "./someFolder/" when there is a file of the same name', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+    var basedir = path.join(dir, 'same_names');
+
+    t.equal(
+        resolve.sync('./foo', { basedir: basedir }),
+        path.join(dir, 'same_names/foo.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./foo', { basedir: basedir }),
+            require.resolve('./foo', { paths: [basedir] }),
+            './foo: resolve.sync === require.resolve'
+        );
+    }
+
+    t.equal(
+        resolve.sync('./foo/', { basedir: basedir }),
+        path.join(dir, 'same_names/foo/index.js')
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./foo/', { basedir: basedir }),
+            require.resolve('./foo/', { paths: [basedir] }),
+            './foo/: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+test('#211 - incorrectly resolves module-paths like "." when from inside a folder with a sibling file of the same name', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+    var basedir = path.join(dir, 'same_names/foo');
+
+    t.equal(
+        resolve.sync('./', { basedir: basedir }),
+        path.join(dir, 'same_names/foo/index.js'),
+        './'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./', { basedir: basedir }),
+            require.resolve('./', { paths: [basedir] }),
+            './: resolve.sync === require.resolve'
+        );
+    }
+
+    t.equal(
+        resolve.sync('.', { basedir: basedir }),
+        path.join(dir, 'same_names/foo/index.js'),
+        '.'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('.', { basedir: basedir }),
+            require.resolve('.', { paths: [basedir] }),
+            '.: resolve.sync === require.resolve',
+            { todo: true }
+        );
+    }
+
+    t.end();
+});
+
+test('sync: #121 - treating an existing file as a dir when no basedir', function (t) {
+    var testFile = path.basename(__filename);
+
+    t.test('sanity check', function (st) {
+        st.equal(
+            resolve.sync('./' + testFile),
+            __filename,
+            'sanity check'
+        );
+        st.equal(
+            resolve.sync('./' + testFile),
+            require.resolve('./' + testFile),
+            'sanity check: resolve.sync === require.resolve'
+        );
+
+        st.end();
+    });
+
+    t.test('with a fake directory', function (st) {
+        function run() { return resolve.sync('./' + testFile + '/blah'); }
+
+        st.throws(run, 'throws an error');
+
+        try {
+            run();
+        } catch (e) {
+            st.equal(e.code, 'MODULE_NOT_FOUND', 'error code matches require.resolve');
+            st.equal(
+                e.message,
+                'Cannot find module \'./' + testFile + '/blah\' from \'' + __dirname + '\'',
+                'can not find nonexistent module'
+            );
+        }
+
+        st.end();
+    });
+
+    t.end();
+});
+
+test('sync dot main', function (t) {
+    var start = new Date();
+
+    t.equal(
+        resolve.sync('./resolver/dot_main'),
+        path.join(__dirname, 'resolver/dot_main/index.js'),
+        './resolver/dot_main'
+    );
+    t.equal(
+        resolve.sync('./resolver/dot_main'),
+        require.resolve('./resolver/dot_main'),
+        './resolver/dot_main: resolve.sync === require.resolve'
+    );
+
+    t.ok(new Date() - start < 50, 'resolve.sync timedout');
+
+    t.end();
+});
+
+test('sync dot slash main', function (t) {
+    var start = new Date();
+
+    t.equal(
+        resolve.sync('./resolver/dot_slash_main'),
+        path.join(__dirname, 'resolver/dot_slash_main/index.js')
+    );
+    t.equal(
+        resolve.sync('./resolver/dot_slash_main'),
+        require.resolve('./resolver/dot_slash_main'),
+        './resolver/dot_slash_main: resolve.sync === require.resolve'
+    );
+
+    t.ok(new Date() - start < 50, 'resolve.sync timedout');
+
+    t.end();
+});
+
+test('not a directory', function (t) {
+    var path = './foo';
+    try {
+        resolve.sync(path, { basedir: __filename });
+        t.fail();
+    } catch (err) {
+        t.ok(err, 'a non-directory errors');
+        t.equal(err && err.message, 'Provided basedir "' + __filename + '" is not a directory, or a symlink to a directory');
+        t.equal(err && err.code, 'INVALID_BASEDIR');
+    }
+    t.end();
+});
+
+test('non-string "main" field in package.json', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+    try {
+        var result = resolve.sync('./invalid_main', { basedir: dir });
+        t.equal(result, undefined, 'result should not exist');
+        t.fail('should not get here');
+    } catch (err) {
+        t.ok(err, 'errors on non-string main');
+        t.equal(err.message, 'package “invalid_main” `main` must be a string');
+        t.equal(err.code, 'INVALID_PACKAGE_MAIN');
+    }
+    t.end();
+});
+
+test('non-string "main" field in package.json', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+    try {
+        var result = resolve.sync('./invalid_main', { basedir: dir });
+        t.equal(result, undefined, 'result should not exist');
+        t.fail('should not get here');
+    } catch (err) {
+        t.ok(err, 'errors on non-string main');
+        t.equal(err.message, 'package “invalid_main” `main` must be a string');
+        t.equal(err.code, 'INVALID_PACKAGE_MAIN');
+    }
+    t.end();
+});
+
+test('browser field in package.json', function (t) {
+    var dir = path.join(__dirname, 'resolver');
+    var res = resolve.sync('./browser_field', {
+        basedir: dir,
+        packageFilter: function packageFilter(pkg) {
+            if (pkg.browser) {
+                pkg.main = pkg.browser; // eslint-disable-line no-param-reassign
+                delete pkg.browser; // eslint-disable-line no-param-reassign
+            }
+            return pkg;
+        }
+    });
+    t.equal(res, path.join(dir, 'browser_field', 'b.js'));
+    t.end();
+});
+
+test('absolute paths', function (t) {
+    var extensionless = __filename.slice(0, -path.extname(__filename).length);
+
+    t.equal(
+        resolve.sync(__filename),
+        __filename,
+        'absolute path to this file resolves'
+    );
+    t.equal(
+        resolve.sync(__filename),
+        require.resolve(__filename),
+        'absolute path to this file: resolve.sync === require.resolve'
+    );
+
+    t.equal(
+        resolve.sync(extensionless),
+        __filename,
+        'extensionless absolute path to this file resolves'
+    );
+    t.equal(
+        resolve.sync(__filename),
+        require.resolve(__filename),
+        'absolute path to this file: resolve.sync === require.resolve'
+    );
+
+    t.equal(
+        resolve.sync(__filename, { basedir: process.cwd() }),
+        __filename,
+        'absolute path to this file with a basedir resolves'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync(__filename, { basedir: process.cwd() }),
+            require.resolve(__filename, { paths: [process.cwd()] }),
+            'absolute path to this file + basedir: resolve.sync === require.resolve'
+        );
+    }
+
+    t.equal(
+        resolve.sync(extensionless, { basedir: process.cwd() }),
+        __filename,
+        'extensionless absolute path to this file with a basedir resolves'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync(extensionless, { basedir: process.cwd() }),
+            require.resolve(extensionless, { paths: [process.cwd()] }),
+            'extensionless absolute path to this file + basedir: resolve.sync === require.resolve'
+        );
+    }
+
+    t.end();
+});
+
+var malformedDir = path.join(__dirname, 'resolver/malformed_package_json');
+test('malformed package.json', { skip: !fs.existsSync(malformedDir) }, function (t) {
+    t.plan(5 + (requireResolveSupportsPaths ? 1 : 0));
+
+    var basedir = malformedDir;
+    var expected = path.join(basedir, 'index.js');
+
+    t.equal(
+        resolve.sync('./index.js', { basedir: basedir }),
+        expected,
+        'malformed package.json is silently ignored'
+    );
+    if (requireResolveSupportsPaths) {
+        t.equal(
+            resolve.sync('./index.js', { basedir: basedir }),
+            require.resolve('./index.js', { paths: [basedir] }),
+            'malformed package.json: resolve.sync === require.resolve'
+        );
+    }
+
+    var res1 = resolve.sync(
+        './index.js',
+        {
+            basedir: basedir,
+            packageFilter: function (pkg, pkgfile, dir) {
+                t.fail('should not reach here');
+            }
+        }
+    );
+
+    t.equal(
+        res1,
+        expected,
+        'with packageFilter: malformed package.json is silently ignored'
+    );
+
+    var res2 = resolve.sync(
+        './index.js',
+        {
+            basedir: basedir,
+            readPackageSync: function (readFileSync, pkgfile) {
+                t.equal(pkgfile, path.join(basedir, 'package.json'), 'readPackageSync: `pkgfile` is package.json path');
+                var result = String(readFileSync(pkgfile));
+                try {
+                    return JSON.parse(result);
+                } catch (e) {
+                    t.ok(e instanceof SyntaxError, 'readPackageSync: malformed package.json parses as a syntax error');
+                    throw e;
+                }
+            }
+        }
+    );
+
+    t.equal(
+        res2,
+        expected,
+        'with readPackageSync: malformed package.json is silently ignored'
+    );
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/shadowed_core.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/shadowed_core.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/shadowed_core.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,54 @@
+var test = require('tape');
+var resolve = require('../');
+var path = require('path');
+
+test('shadowed core modules still return core module', function (t) {
+    t.plan(2);
+
+    resolve('util', { basedir: path.join(__dirname, 'shadowed_core') }, function (err, res) {
+        t.ifError(err);
+        t.equal(res, 'util');
+    });
+});
+
+test('shadowed core modules still return core module [sync]', function (t) {
+    t.plan(1);
+
+    var res = resolve.sync('util', { basedir: path.join(__dirname, 'shadowed_core') });
+
+    t.equal(res, 'util');
+});
+
+test('shadowed core modules return shadow when appending `/`', function (t) {
+    t.plan(2);
+
+    resolve('util/', { basedir: path.join(__dirname, 'shadowed_core') }, function (err, res) {
+        t.ifError(err);
+        t.equal(res, path.join(__dirname, 'shadowed_core/node_modules/util/index.js'));
+    });
+});
+
+test('shadowed core modules return shadow when appending `/` [sync]', function (t) {
+    t.plan(1);
+
+    var res = resolve.sync('util/', { basedir: path.join(__dirname, 'shadowed_core') });
+
+    t.equal(res, path.join(__dirname, 'shadowed_core/node_modules/util/index.js'));
+});
+
+test('shadowed core modules return shadow with `includeCoreModules: false`', function (t) {
+    t.plan(2);
+
+    resolve('util', { basedir: path.join(__dirname, 'shadowed_core'), includeCoreModules: false }, function (err, res) {
+        t.ifError(err);
+        t.equal(res, path.join(__dirname, 'shadowed_core/node_modules/util/index.js'));
+    });
+});
+
+test('shadowed core modules return shadow with `includeCoreModules: false` [sync]', function (t) {
+    t.plan(1);
+
+    var res = resolve.sync('util', { basedir: path.join(__dirname, 'shadowed_core'), includeCoreModules: false });
+
+    t.equal(res, path.join(__dirname, 'shadowed_core/node_modules/util/index.js'));
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/subdirs.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/subdirs.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/subdirs.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,13 @@
+var test = require('tape');
+var resolve = require('../');
+var path = require('path');
+
+test('subdirs', function (t) {
+    t.plan(2);
+
+    var dir = path.join(__dirname, '/subdirs');
+    resolve('a/b/c/x.json', { basedir: dir }, function (err, res) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, 'node_modules/a/b/c/x.json'));
+    });
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/symlinks.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/symlinks.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/test/symlinks.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,175 @@
+var path = require('path');
+var fs = require('fs');
+var test = require('tape');
+var map = require('array.prototype.map');
+var resolve = require('../');
+
+var symlinkDir = path.join(__dirname, 'resolver', 'symlinked', 'symlink');
+var packageDir = path.join(__dirname, 'resolver', 'symlinked', '_', 'node_modules', 'package');
+var modADir = path.join(__dirname, 'symlinks', 'source', 'node_modules', 'mod-a');
+var symlinkModADir = path.join(__dirname, 'symlinks', 'dest', 'node_modules', 'mod-a');
+try {
+    fs.unlinkSync(symlinkDir);
+} catch (err) {}
+try {
+    fs.unlinkSync(packageDir);
+} catch (err) {}
+try {
+    fs.unlinkSync(modADir);
+} catch (err) {}
+try {
+    fs.unlinkSync(symlinkModADir);
+} catch (err) {}
+
+try {
+    fs.symlinkSync('./_/symlink_target', symlinkDir, 'dir');
+} catch (err) {
+    if (err.code !== 'EEXIST') {
+        // if fails then it is probably on Windows and lets try to create a junction
+        fs.symlinkSync(path.join(__dirname, 'resolver', 'symlinked', '_', 'symlink_target') + '\\', symlinkDir, 'junction');
+    }
+}
+try {
+    fs.symlinkSync('../../package', packageDir, 'dir');
+} catch (err) {
+    // if fails then it is probably on Windows and lets try to create a junction
+    fs.symlinkSync(path.join(__dirname, '..', '..', 'package') + '\\', packageDir, 'junction');
+}
+try {
+    fs.symlinkSync('../../source/node_modules/mod-a', symlinkModADir, 'dir');
+} catch (err) {
+    // if fails then it is probably on Windows and lets try to create a junction
+    fs.symlinkSync(path.join(__dirname, '..', '..', 'source', 'node_modules', 'mod-a') + '\\', symlinkModADir, 'junction');
+}
+
+test('symlink', function (t) {
+    t.plan(2);
+
+    resolve('foo', { basedir: symlinkDir }, function (err, res, pkg) {
+        t.error(err);
+        t.equal(res, path.join(__dirname, 'resolver', 'symlinked', '_', 'node_modules', 'foo.js'));
+    });
+});
+
+test('sync symlink when preserveSymlinks = true', function (t) {
+    t.plan(4);
+
+    resolve('foo', { basedir: symlinkDir, preserveSymlinks: true }, function (err, res, pkg) {
+        t.ok(err, 'there is an error');
+        t.notOk(res, 'no result');
+
+        t.equal(err && err.code, 'MODULE_NOT_FOUND', 'error code matches require.resolve');
+        t.equal(
+            err && err.message,
+            'Cannot find module \'foo\' from \'' + symlinkDir + '\'',
+            'can not find nonexistent module'
+        );
+    });
+});
+
+test('sync symlink', function (t) {
+    var start = new Date();
+    t.doesNotThrow(function () {
+        t.equal(
+            resolve.sync('foo', { basedir: symlinkDir, preserveSymlinks: false }),
+            path.join(__dirname, 'resolver', 'symlinked', '_', 'node_modules', 'foo.js')
+        );
+    });
+    t.ok(new Date() - start < 50, 'resolve.sync timedout');
+    t.end();
+});
+
+test('sync symlink when preserveSymlinks = true', function (t) {
+    t.throws(function () {
+        resolve.sync('foo', { basedir: symlinkDir, preserveSymlinks: true });
+    }, /Cannot find module 'foo'/);
+    t.end();
+});
+
+test('sync symlink from node_modules to other dir when preserveSymlinks = false', function (t) {
+    var basedir = path.join(__dirname, 'resolver', 'symlinked', '_');
+    var fn = resolve.sync('package', { basedir: basedir, preserveSymlinks: false });
+
+    t.equal(fn, path.resolve(__dirname, 'resolver/symlinked/package/bar.js'));
+    t.end();
+});
+
+test('async symlink from node_modules to other dir when preserveSymlinks = false', function (t) {
+    t.plan(2);
+    var basedir = path.join(__dirname, 'resolver', 'symlinked', '_');
+    resolve('package', { basedir: basedir, preserveSymlinks: false }, function (err, result) {
+        t.notOk(err, 'no error');
+        t.equal(result, path.resolve(__dirname, 'resolver/symlinked/package/bar.js'));
+    });
+});
+
+test('packageFilter', function (t) {
+    function relative(x) {
+        return path.relative(__dirname, x);
+    }
+
+    function testPackageFilter(preserveSymlinks) {
+        return function (st) {
+            st.plan(5);
+
+            var destMain = 'symlinks/dest/node_modules/mod-a/index.js';
+            var destPkg = 'symlinks/dest/node_modules/mod-a/package.json';
+            var sourceMain = 'symlinks/source/node_modules/mod-a/index.js';
+            var sourcePkg = 'symlinks/source/node_modules/mod-a/package.json';
+            var destDir = path.join(__dirname, 'symlinks', 'dest');
+
+            var packageFilterPath = [];
+            var actualPath = resolve.sync('mod-a', {
+                basedir: destDir,
+                preserveSymlinks: preserveSymlinks,
+                packageFilter: function (pkg, pkgfile, dir) {
+                    packageFilterPath.push(pkgfile);
+                }
+            });
+            st.equal(
+                relative(actualPath),
+                path.normalize(preserveSymlinks ? destMain : sourceMain),
+                'sync: actual path is correct'
+            );
+            st.deepEqual(
+                map(packageFilterPath, relative),
+                map(preserveSymlinks ? [destPkg, destPkg] : [sourcePkg, sourcePkg], path.normalize),
+                'sync: packageFilter pkgfile arg is correct'
+            );
+
+            var asyncPackageFilterPath = [];
+            resolve(
+                'mod-a',
+                {
+                    basedir: destDir,
+                    preserveSymlinks: preserveSymlinks,
+                    packageFilter: function (pkg, pkgfile) {
+                        asyncPackageFilterPath.push(pkgfile);
+                    }
+                },
+                function (err, actualPath) {
+                    st.error(err, 'no error');
+                    st.equal(
+                        relative(actualPath),
+                        path.normalize(preserveSymlinks ? destMain : sourceMain),
+                        'async: actual path is correct'
+                    );
+                    st.deepEqual(
+                        map(asyncPackageFilterPath, relative),
+                        map(
+                            preserveSymlinks ? [destPkg, destPkg, destPkg] : [sourcePkg, sourcePkg, sourcePkg],
+                            path.normalize
+                        ),
+                        'async: packageFilter pkgfile arg is correct'
+                    );
+                }
+            );
+        };
+    }
+
+    t.test('preserveSymlinks: false', testPackageFilter(false));
+
+    t.test('preserveSymlinks: true', testPackageFilter(true));
+
+    t.end();
+});
Index: frontend/node_modules/eslint-plugin-react/node_modules/resolve/tsconfig.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/resolve/tsconfig.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/resolve/tsconfig.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,14 @@
+{
+  "extends": "@ljharb/tsconfig",
+  "compilerOptions": {
+    "target": "ES2021",
+    "checkJs": false,
+    "maxNodeModuleJsDepth": 0,
+    "types": ["node"]
+  },
+  "exclude": [
+    "coverage",
+    "example",
+    "test"
+  ]
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/semver/LICENSE
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/semver/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/semver/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Index: frontend/node_modules/eslint-plugin-react/node_modules/semver/README.md
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/semver/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/semver/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,443 @@
+semver(1) -- The semantic versioner for npm
+===========================================
+
+## Install
+
+```bash
+npm install semver
+````
+
+## Usage
+
+As a node module:
+
+```js
+const semver = require('semver')
+
+semver.valid('1.2.3') // '1.2.3'
+semver.valid('a.b.c') // null
+semver.clean('  =v1.2.3   ') // '1.2.3'
+semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
+semver.gt('1.2.3', '9.8.7') // false
+semver.lt('1.2.3', '9.8.7') // true
+semver.minVersion('>=1.0.0') // '1.0.0'
+semver.valid(semver.coerce('v2')) // '2.0.0'
+semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7'
+```
+
+As a command-line utility:
+
+```
+$ semver -h
+
+A JavaScript implementation of the https://semver.org/ specification
+Copyright Isaac Z. Schlueter
+
+Usage: semver [options] <version> [<version> [...]]
+Prints valid versions sorted by SemVer precedence
+
+Options:
+-r --range <range>
+        Print versions that match the specified range.
+
+-i --increment [<level>]
+        Increment a version by the specified level.  Level can
+        be one of: major, minor, patch, premajor, preminor,
+        prepatch, or prerelease.  Default level is 'patch'.
+        Only one version may be specified.
+
+--preid <identifier>
+        Identifier to be used to prefix premajor, preminor,
+        prepatch or prerelease version increments.
+
+-l --loose
+        Interpret versions and ranges loosely
+
+-p --include-prerelease
+        Always include prerelease versions in range matching
+
+-c --coerce
+        Coerce a string into SemVer if possible
+        (does not imply --loose)
+
+--rtl
+        Coerce version strings right to left
+
+--ltr
+        Coerce version strings left to right (default)
+
+Program exits successfully if any valid version satisfies
+all supplied ranges, and prints all satisfying versions.
+
+If no satisfying versions are found, then exits failure.
+
+Versions are printed in ascending order, so supplying
+multiple versions to the utility will just sort them.
+```
+
+## Versions
+
+A "version" is described by the `v2.0.0` specification found at
+<https://semver.org/>.
+
+A leading `"="` or `"v"` character is stripped off and ignored.
+
+## Ranges
+
+A `version range` is a set of `comparators` which specify versions
+that satisfy the range.
+
+A `comparator` is composed of an `operator` and a `version`.  The set
+of primitive `operators` is:
+
+* `<` Less than
+* `<=` Less than or equal to
+* `>` Greater than
+* `>=` Greater than or equal to
+* `=` Equal.  If no operator is specified, then equality is assumed,
+  so this operator is optional, but MAY be included.
+
+For example, the comparator `>=1.2.7` would match the versions
+`1.2.7`, `1.2.8`, `2.5.3`, and `1.3.9`, but not the versions `1.2.6`
+or `1.1.0`.
+
+Comparators can be joined by whitespace to form a `comparator set`,
+which is satisfied by the **intersection** of all of the comparators
+it includes.
+
+A range is composed of one or more comparator sets, joined by `||`.  A
+version matches a range if and only if every comparator in at least
+one of the `||`-separated comparator sets is satisfied by the version.
+
+For example, the range `>=1.2.7 <1.3.0` would match the versions
+`1.2.7`, `1.2.8`, and `1.2.99`, but not the versions `1.2.6`, `1.3.0`,
+or `1.1.0`.
+
+The range `1.2.7 || >=1.2.9 <2.0.0` would match the versions `1.2.7`,
+`1.2.9`, and `1.4.6`, but not the versions `1.2.8` or `2.0.0`.
+
+### Prerelease Tags
+
+If a version has a prerelease tag (for example, `1.2.3-alpha.3`) then
+it will only be allowed to satisfy comparator sets if at least one
+comparator with the same `[major, minor, patch]` tuple also has a
+prerelease tag.
+
+For example, the range `>1.2.3-alpha.3` would be allowed to match the
+version `1.2.3-alpha.7`, but it would *not* be satisfied by
+`3.4.5-alpha.9`, even though `3.4.5-alpha.9` is technically "greater
+than" `1.2.3-alpha.3` according to the SemVer sort rules.  The version
+range only accepts prerelease tags on the `1.2.3` version.  The
+version `3.4.5` *would* satisfy the range, because it does not have a
+prerelease flag, and `3.4.5` is greater than `1.2.3-alpha.7`.
+
+The purpose for this behavior is twofold.  First, prerelease versions
+frequently are updated very quickly, and contain many breaking changes
+that are (by the author's design) not yet fit for public consumption.
+Therefore, by default, they are excluded from range matching
+semantics.
+
+Second, a user who has opted into using a prerelease version has
+clearly indicated the intent to use *that specific* set of
+alpha/beta/rc versions.  By including a prerelease tag in the range,
+the user is indicating that they are aware of the risk.  However, it
+is still not appropriate to assume that they have opted into taking a
+similar risk on the *next* set of prerelease versions.
+
+Note that this behavior can be suppressed (treating all prerelease
+versions as if they were normal versions, for the purpose of range
+matching) by setting the `includePrerelease` flag on the options
+object to any
+[functions](https://github.com/npm/node-semver#functions) that do
+range matching.
+
+#### Prerelease Identifiers
+
+The method `.inc` takes an additional `identifier` string argument that
+will append the value of the string as a prerelease identifier:
+
+```javascript
+semver.inc('1.2.3', 'prerelease', 'beta')
+// '1.2.4-beta.0'
+```
+
+command-line example:
+
+```bash
+$ semver 1.2.3 -i prerelease --preid beta
+1.2.4-beta.0
+```
+
+Which then can be used to increment further:
+
+```bash
+$ semver 1.2.4-beta.0 -i prerelease
+1.2.4-beta.1
+```
+
+### Advanced Range Syntax
+
+Advanced range syntax desugars to primitive comparators in
+deterministic ways.
+
+Advanced ranges may be combined in the same way as primitive
+comparators using white space or `||`.
+
+#### Hyphen Ranges `X.Y.Z - A.B.C`
+
+Specifies an inclusive set.
+
+* `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4`
+
+If a partial version is provided as the first version in the inclusive
+range, then the missing pieces are replaced with zeroes.
+
+* `1.2 - 2.3.4` := `>=1.2.0 <=2.3.4`
+
+If a partial version is provided as the second version in the
+inclusive range, then all versions that start with the supplied parts
+of the tuple are accepted, but nothing that would be greater than the
+provided tuple parts.
+
+* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0`
+* `1.2.3 - 2` := `>=1.2.3 <3.0.0`
+
+#### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
+
+Any of `X`, `x`, or `*` may be used to "stand in" for one of the
+numeric values in the `[major, minor, patch]` tuple.
+
+* `*` := `>=0.0.0` (Any version satisfies)
+* `1.x` := `>=1.0.0 <2.0.0` (Matching major version)
+* `1.2.x` := `>=1.2.0 <1.3.0` (Matching major and minor versions)
+
+A partial version range is treated as an X-Range, so the special
+character is in fact optional.
+
+* `""` (empty string) := `*` := `>=0.0.0`
+* `1` := `1.x.x` := `>=1.0.0 <2.0.0`
+* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0`
+
+#### Tilde Ranges `~1.2.3` `~1.2` `~1`
+
+Allows patch-level changes if a minor version is specified on the
+comparator.  Allows minor-level changes if not.
+
+* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0`
+* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0` (Same as `1.2.x`)
+* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0` (Same as `1.x`)
+* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0`
+* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0` (Same as `0.2.x`)
+* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0` (Same as `0.x`)
+* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0` Note that prereleases in
+  the `1.2.3` version will be allowed, if they are greater than or
+  equal to `beta.2`.  So, `1.2.3-beta.4` would be allowed, but
+  `1.2.4-beta.2` would not, because it is a prerelease of a
+  different `[major, minor, patch]` tuple.
+
+#### Caret Ranges `^1.2.3` `^0.2.5` `^0.0.4`
+
+Allows changes that do not modify the left-most non-zero element in the
+`[major, minor, patch]` tuple.  In other words, this allows patch and
+minor updates for versions `1.0.0` and above, patch updates for
+versions `0.X >=0.1.0`, and *no* updates for versions `0.0.X`.
+
+Many authors treat a `0.x` version as if the `x` were the major
+"breaking-change" indicator.
+
+Caret ranges are ideal when an author may make breaking changes
+between `0.2.4` and `0.3.0` releases, which is a common practice.
+However, it presumes that there will *not* be breaking changes between
+`0.2.4` and `0.2.5`.  It allows for changes that are presumed to be
+additive (but non-breaking), according to commonly observed practices.
+
+* `^1.2.3` := `>=1.2.3 <2.0.0`
+* `^0.2.3` := `>=0.2.3 <0.3.0`
+* `^0.0.3` := `>=0.0.3 <0.0.4`
+* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0` Note that prereleases in
+  the `1.2.3` version will be allowed, if they are greater than or
+  equal to `beta.2`.  So, `1.2.3-beta.4` would be allowed, but
+  `1.2.4-beta.2` would not, because it is a prerelease of a
+  different `[major, minor, patch]` tuple.
+* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4`  Note that prereleases in the
+  `0.0.3` version *only* will be allowed, if they are greater than or
+  equal to `beta`.  So, `0.0.3-pr.2` would be allowed.
+
+When parsing caret ranges, a missing `patch` value desugars to the
+number `0`, but will allow flexibility within that value, even if the
+major and minor versions are both `0`.
+
+* `^1.2.x` := `>=1.2.0 <2.0.0`
+* `^0.0.x` := `>=0.0.0 <0.1.0`
+* `^0.0` := `>=0.0.0 <0.1.0`
+
+A missing `minor` and `patch` values will desugar to zero, but also
+allow flexibility within those values, even if the major version is
+zero.
+
+* `^1.x` := `>=1.0.0 <2.0.0`
+* `^0.x` := `>=0.0.0 <1.0.0`
+
+### Range Grammar
+
+Putting all this together, here is a Backus-Naur grammar for ranges,
+for the benefit of parser authors:
+
+```bnf
+range-set  ::= range ( logical-or range ) *
+logical-or ::= ( ' ' ) * '||' ( ' ' ) *
+range      ::= hyphen | simple ( ' ' simple ) * | ''
+hyphen     ::= partial ' - ' partial
+simple     ::= primitive | partial | tilde | caret
+primitive  ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
+partial    ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
+xr         ::= 'x' | 'X' | '*' | nr
+nr         ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
+tilde      ::= '~' partial
+caret      ::= '^' partial
+qualifier  ::= ( '-' pre )? ( '+' build )?
+pre        ::= parts
+build      ::= parts
+parts      ::= part ( '.' part ) *
+part       ::= nr | [-0-9A-Za-z]+
+```
+
+## Functions
+
+All methods and classes take a final `options` object argument.  All
+options in this object are `false` by default.  The options supported
+are:
+
+- `loose`  Be more forgiving about not-quite-valid semver strings.
+  (Any resulting output will always be 100% strict compliant, of
+  course.)  For backwards compatibility reasons, if the `options`
+  argument is a boolean value instead of an object, it is interpreted
+  to be the `loose` param.
+- `includePrerelease`  Set to suppress the [default
+  behavior](https://github.com/npm/node-semver#prerelease-tags) of
+  excluding prerelease tagged versions from ranges unless they are
+  explicitly opted into.
+
+Strict-mode Comparators and Ranges will be strict about the SemVer
+strings that they parse.
+
+* `valid(v)`: Return the parsed version, or null if it's not valid.
+* `inc(v, release)`: Return the version incremented by the release
+  type (`major`,   `premajor`, `minor`, `preminor`, `patch`,
+  `prepatch`, or `prerelease`), or null if it's not valid
+  * `premajor` in one call will bump the version up to the next major
+    version and down to a prerelease of that major version.
+    `preminor`, and `prepatch` work the same way.
+  * If called from a non-prerelease version, the `prerelease` will work the
+    same as `prepatch`. It increments the patch version, then makes a
+    prerelease. If the input version is already a prerelease it simply
+    increments it.
+* `prerelease(v)`: Returns an array of prerelease components, or null
+  if none exist. Example: `prerelease('1.2.3-alpha.1') -> ['alpha', 1]`
+* `major(v)`: Return the major version number.
+* `minor(v)`: Return the minor version number.
+* `patch(v)`: Return the patch version number.
+* `intersects(r1, r2, loose)`: Return true if the two supplied ranges
+  or comparators intersect.
+* `parse(v)`: Attempt to parse a string as a semantic version, returning either
+  a `SemVer` object or `null`.
+
+### Comparison
+
+* `gt(v1, v2)`: `v1 > v2`
+* `gte(v1, v2)`: `v1 >= v2`
+* `lt(v1, v2)`: `v1 < v2`
+* `lte(v1, v2)`: `v1 <= v2`
+* `eq(v1, v2)`: `v1 == v2` This is true if they're logically equivalent,
+  even if they're not the exact same string.  You already know how to
+  compare strings.
+* `neq(v1, v2)`: `v1 != v2` The opposite of `eq`.
+* `cmp(v1, comparator, v2)`: Pass in a comparison string, and it'll call
+  the corresponding function above.  `"==="` and `"!=="` do simple
+  string comparison, but are included for completeness.  Throws if an
+  invalid comparison string is provided.
+* `compare(v1, v2)`: Return `0` if `v1 == v2`, or `1` if `v1` is greater, or `-1` if
+  `v2` is greater.  Sorts in ascending order if passed to `Array.sort()`.
+* `rcompare(v1, v2)`: The reverse of compare.  Sorts an array of versions
+  in descending order when passed to `Array.sort()`.
+* `compareBuild(v1, v2)`: The same as `compare` but considers `build` when two versions
+  are equal.  Sorts in ascending order if passed to `Array.sort()`.
+  `v2` is greater.  Sorts in ascending order if passed to `Array.sort()`.
+* `diff(v1, v2)`: Returns difference between two versions by the release type
+  (`major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, or `prerelease`),
+  or null if the versions are the same.
+
+### Comparators
+
+* `intersects(comparator)`: Return true if the comparators intersect
+
+### Ranges
+
+* `validRange(range)`: Return the valid range or null if it's not valid
+* `satisfies(version, range)`: Return true if the version satisfies the
+  range.
+* `maxSatisfying(versions, range)`: Return the highest version in the list
+  that satisfies the range, or `null` if none of them do.
+* `minSatisfying(versions, range)`: Return the lowest version in the list
+  that satisfies the range, or `null` if none of them do.
+* `minVersion(range)`: Return the lowest version that can possibly match
+  the given range.
+* `gtr(version, range)`: Return `true` if version is greater than all the
+  versions possible in the range.
+* `ltr(version, range)`: Return `true` if version is less than all the
+  versions possible in the range.
+* `outside(version, range, hilo)`: Return true if the version is outside
+  the bounds of the range in either the high or low direction.  The
+  `hilo` argument must be either the string `'>'` or `'<'`.  (This is
+  the function called by `gtr` and `ltr`.)
+* `intersects(range)`: Return true if any of the ranges comparators intersect
+
+Note that, since ranges may be non-contiguous, a version might not be
+greater than a range, less than a range, *or* satisfy a range!  For
+example, the range `1.2 <1.2.9 || >2.0.0` would have a hole from `1.2.9`
+until `2.0.0`, so the version `1.2.10` would not be greater than the
+range (because `2.0.1` satisfies, which is higher), nor less than the
+range (since `1.2.8` satisfies, which is lower), and it also does not
+satisfy the range.
+
+If you want to know if a version satisfies or does not satisfy a
+range, use the `satisfies(version, range)` function.
+
+### Coercion
+
+* `coerce(version, options)`: Coerces a string to semver if possible
+
+This aims to provide a very forgiving translation of a non-semver string to
+semver. It looks for the first digit in a string, and consumes all
+remaining characters which satisfy at least a partial semver (e.g., `1`,
+`1.2`, `1.2.3`) up to the max permitted length (256 characters).  Longer
+versions are simply truncated (`4.6.3.9.2-alpha2` becomes `4.6.3`).  All
+surrounding text is simply ignored (`v3.4 replaces v3.3.1` becomes
+`3.4.0`).  Only text which lacks digits will fail coercion (`version one`
+is not valid).  The maximum  length for any semver component considered for
+coercion is 16 characters; longer components will be ignored
+(`10000000000000000.4.7.4` becomes `4.7.4`).  The maximum value for any
+semver component is `Integer.MAX_SAFE_INTEGER || (2**53 - 1)`; higher value
+components are invalid (`9999999999999999.4.7.4` is likely invalid).
+
+If the `options.rtl` flag is set, then `coerce` will return the right-most
+coercible tuple that does not share an ending index with a longer coercible
+tuple.  For example, `1.2.3.4` will return `2.3.4` in rtl mode, not
+`4.0.0`.  `1.2.3/4` will return `4.0.0`, because the `4` is not a part of
+any other overlapping SemVer tuple.
+
+### Clean
+
+* `clean(version)`: Clean a string to be a valid semver if possible
+
+This will return a cleaned and trimmed semver version. If the provided version is not valid a null will be returned. This does not work for ranges. 
+
+ex.
+* `s.clean(' = v 2.1.5foo')`: `null`
+* `s.clean(' = v 2.1.5foo', { loose: true })`: `'2.1.5-foo'`
+* `s.clean(' = v 2.1.5-foo')`: `null`
+* `s.clean(' = v 2.1.5-foo', { loose: true })`: `'2.1.5-foo'`
+* `s.clean('=v2.1.5')`: `'2.1.5'`
+* `s.clean('  =v2.1.5')`: `2.1.5`
+* `s.clean('      2.1.5   ')`: `'2.1.5'`
+* `s.clean('~1.0.0')`: `null`
Index: frontend/node_modules/eslint-plugin-react/node_modules/semver/bin/semver.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/semver/bin/semver.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/semver/bin/semver.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,174 @@
+#!/usr/bin/env node
+// Standalone semver comparison program.
+// Exits successfully and prints matching version(s) if
+// any supplied version is valid and passes all tests.
+
+var argv = process.argv.slice(2)
+
+var versions = []
+
+var range = []
+
+var inc = null
+
+var version = require('../package.json').version
+
+var loose = false
+
+var includePrerelease = false
+
+var coerce = false
+
+var rtl = false
+
+var identifier
+
+var semver = require('../semver')
+
+var reverse = false
+
+var options = {}
+
+main()
+
+function main () {
+  if (!argv.length) return help()
+  while (argv.length) {
+    var a = argv.shift()
+    var indexOfEqualSign = a.indexOf('=')
+    if (indexOfEqualSign !== -1) {
+      a = a.slice(0, indexOfEqualSign)
+      argv.unshift(a.slice(indexOfEqualSign + 1))
+    }
+    switch (a) {
+      case '-rv': case '-rev': case '--rev': case '--reverse':
+        reverse = true
+        break
+      case '-l': case '--loose':
+        loose = true
+        break
+      case '-p': case '--include-prerelease':
+        includePrerelease = true
+        break
+      case '-v': case '--version':
+        versions.push(argv.shift())
+        break
+      case '-i': case '--inc': case '--increment':
+        switch (argv[0]) {
+          case 'major': case 'minor': case 'patch': case 'prerelease':
+          case 'premajor': case 'preminor': case 'prepatch':
+            inc = argv.shift()
+            break
+          default:
+            inc = 'patch'
+            break
+        }
+        break
+      case '--preid':
+        identifier = argv.shift()
+        break
+      case '-r': case '--range':
+        range.push(argv.shift())
+        break
+      case '-c': case '--coerce':
+        coerce = true
+        break
+      case '--rtl':
+        rtl = true
+        break
+      case '--ltr':
+        rtl = false
+        break
+      case '-h': case '--help': case '-?':
+        return help()
+      default:
+        versions.push(a)
+        break
+    }
+  }
+
+  var options = { loose: loose, includePrerelease: includePrerelease, rtl: rtl }
+
+  versions = versions.map(function (v) {
+    return coerce ? (semver.coerce(v, options) || { version: v }).version : v
+  }).filter(function (v) {
+    return semver.valid(v)
+  })
+  if (!versions.length) return fail()
+  if (inc && (versions.length !== 1 || range.length)) { return failInc() }
+
+  for (var i = 0, l = range.length; i < l; i++) {
+    versions = versions.filter(function (v) {
+      return semver.satisfies(v, range[i], options)
+    })
+    if (!versions.length) return fail()
+  }
+  return success(versions)
+}
+
+function failInc () {
+  console.error('--inc can only be used on a single version with no range')
+  fail()
+}
+
+function fail () { process.exit(1) }
+
+function success () {
+  var compare = reverse ? 'rcompare' : 'compare'
+  versions.sort(function (a, b) {
+    return semver[compare](a, b, options)
+  }).map(function (v) {
+    return semver.clean(v, options)
+  }).map(function (v) {
+    return inc ? semver.inc(v, inc, options, identifier) : v
+  }).forEach(function (v, i, _) { console.log(v) })
+}
+
+function help () {
+  console.log(['SemVer ' + version,
+    '',
+    'A JavaScript implementation of the https://semver.org/ specification',
+    'Copyright Isaac Z. Schlueter',
+    '',
+    'Usage: semver [options] <version> [<version> [...]]',
+    'Prints valid versions sorted by SemVer precedence',
+    '',
+    'Options:',
+    '-r --range <range>',
+    '        Print versions that match the specified range.',
+    '',
+    '-i --increment [<level>]',
+    '        Increment a version by the specified level.  Level can',
+    '        be one of: major, minor, patch, premajor, preminor,',
+    "        prepatch, or prerelease.  Default level is 'patch'.",
+    '        Only one version may be specified.',
+    '',
+    '--preid <identifier>',
+    '        Identifier to be used to prefix premajor, preminor,',
+    '        prepatch or prerelease version increments.',
+    '',
+    '-l --loose',
+    '        Interpret versions and ranges loosely',
+    '',
+    '-p --include-prerelease',
+    '        Always include prerelease versions in range matching',
+    '',
+    '-c --coerce',
+    '        Coerce a string into SemVer if possible',
+    '        (does not imply --loose)',
+    '',
+    '--rtl',
+    '        Coerce version strings right to left',
+    '',
+    '--ltr',
+    '        Coerce version strings left to right (default)',
+    '',
+    'Program exits successfully if any valid version satisfies',
+    'all supplied ranges, and prints all satisfying versions.',
+    '',
+    'If no satisfying versions are found, then exits failure.',
+    '',
+    'Versions are printed in ascending order, so supplying',
+    'multiple versions to the utility will just sort them.'
+  ].join('\n'))
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/semver/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/semver/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/semver/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,38 @@
+{
+  "name": "semver",
+  "version": "6.3.1",
+  "description": "The semantic version parser used by npm.",
+  "main": "semver.js",
+  "scripts": {
+    "test": "tap test/ --100 --timeout=30",
+    "lint": "echo linting disabled",
+    "postlint": "template-oss-check",
+    "template-oss-apply": "template-oss-apply --force",
+    "lintfix": "npm run lint -- --fix",
+    "snap": "tap test/ --100 --timeout=30",
+    "posttest": "npm run lint"
+  },
+  "devDependencies": {
+    "@npmcli/template-oss": "4.17.0",
+    "tap": "^12.7.0"
+  },
+  "license": "ISC",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/npm/node-semver.git"
+  },
+  "bin": {
+    "semver": "./bin/semver.js"
+  },
+  "files": [
+    "bin",
+    "range.bnf",
+    "semver.js"
+  ],
+  "author": "GitHub Inc.",
+  "templateOSS": {
+    "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
+    "content": "./scripts/template-oss",
+    "version": "4.17.0"
+  }
+}
Index: frontend/node_modules/eslint-plugin-react/node_modules/semver/range.bnf
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/semver/range.bnf	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/semver/range.bnf	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+range-set  ::= range ( logical-or range ) *
+logical-or ::= ( ' ' ) * '||' ( ' ' ) *
+range      ::= hyphen | simple ( ' ' simple ) * | ''
+hyphen     ::= partial ' - ' partial
+simple     ::= primitive | partial | tilde | caret
+primitive  ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
+partial    ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
+xr         ::= 'x' | 'X' | '*' | nr
+nr         ::= '0' | [1-9] ( [0-9] ) *
+tilde      ::= '~' partial
+caret      ::= '^' partial
+qualifier  ::= ( '-' pre )? ( '+' build )?
+pre        ::= parts
+build      ::= parts
+parts      ::= part ( '.' part ) *
+part       ::= nr | [-0-9A-Za-z]+
Index: frontend/node_modules/eslint-plugin-react/node_modules/semver/semver.js
===================================================================
--- frontend/node_modules/eslint-plugin-react/node_modules/semver/semver.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/node_modules/semver/semver.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1643 @@
+exports = module.exports = SemVer
+
+var debug
+/* istanbul ignore next */
+if (typeof process === 'object' &&
+    process.env &&
+    process.env.NODE_DEBUG &&
+    /\bsemver\b/i.test(process.env.NODE_DEBUG)) {
+  debug = function () {
+    var args = Array.prototype.slice.call(arguments, 0)
+    args.unshift('SEMVER')
+    console.log.apply(console, args)
+  }
+} else {
+  debug = function () {}
+}
+
+// Note: this is the semver.org version of the spec that it implements
+// Not necessarily the package version of this code.
+exports.SEMVER_SPEC_VERSION = '2.0.0'
+
+var MAX_LENGTH = 256
+var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
+  /* istanbul ignore next */ 9007199254740991
+
+// Max safe segment length for coercion.
+var MAX_SAFE_COMPONENT_LENGTH = 16
+
+var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
+
+// The actual regexps go on exports.re
+var re = exports.re = []
+var safeRe = exports.safeRe = []
+var src = exports.src = []
+var t = exports.tokens = {}
+var R = 0
+
+function tok (n) {
+  t[n] = R++
+}
+
+var LETTERDASHNUMBER = '[a-zA-Z0-9-]'
+
+// Replace some greedy regex tokens to prevent regex dos issues. These regex are
+// used internally via the safeRe object since all inputs in this library get
+// normalized first to trim and collapse all extra whitespace. The original
+// regexes are exported for userland consumption and lower level usage. A
+// future breaking change could export the safer regex only with a note that
+// all input should have extra whitespace removed.
+var safeRegexReplacements = [
+  ['\\s', 1],
+  ['\\d', MAX_LENGTH],
+  [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
+]
+
+function makeSafeRe (value) {
+  for (var i = 0; i < safeRegexReplacements.length; i++) {
+    var token = safeRegexReplacements[i][0]
+    var max = safeRegexReplacements[i][1]
+    value = value
+      .split(token + '*').join(token + '{0,' + max + '}')
+      .split(token + '+').join(token + '{1,' + max + '}')
+  }
+  return value
+}
+
+// The following Regular Expressions can be used for tokenizing,
+// validating, and parsing SemVer version strings.
+
+// ## Numeric Identifier
+// A single `0`, or a non-zero digit followed by zero or more digits.
+
+tok('NUMERICIDENTIFIER')
+src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*'
+tok('NUMERICIDENTIFIERLOOSE')
+src[t.NUMERICIDENTIFIERLOOSE] = '\\d+'
+
+// ## Non-numeric Identifier
+// Zero or more digits, followed by a letter or hyphen, and then zero or
+// more letters, digits, or hyphens.
+
+tok('NONNUMERICIDENTIFIER')
+src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*'
+
+// ## Main Version
+// Three dot-separated numeric identifiers.
+
+tok('MAINVERSION')
+src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' +
+                   '(' + src[t.NUMERICIDENTIFIER] + ')\\.' +
+                   '(' + src[t.NUMERICIDENTIFIER] + ')'
+
+tok('MAINVERSIONLOOSE')
+src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' +
+                        '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' +
+                        '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')'
+
+// ## Pre-release Version Identifier
+// A numeric identifier, or a non-numeric identifier.
+
+tok('PRERELEASEIDENTIFIER')
+src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] +
+                            '|' + src[t.NONNUMERICIDENTIFIER] + ')'
+
+tok('PRERELEASEIDENTIFIERLOOSE')
+src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] +
+                                 '|' + src[t.NONNUMERICIDENTIFIER] + ')'
+
+// ## Pre-release Version
+// Hyphen, followed by one or more dot-separated pre-release version
+// identifiers.
+
+tok('PRERELEASE')
+src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] +
+                  '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))'
+
+tok('PRERELEASELOOSE')
+src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] +
+                       '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))'
+
+// ## Build Metadata Identifier
+// Any combination of digits, letters, or hyphens.
+
+tok('BUILDIDENTIFIER')
+src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+'
+
+// ## Build Metadata
+// Plus sign, followed by one or more period-separated build metadata
+// identifiers.
+
+tok('BUILD')
+src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] +
+             '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))'
+
+// ## Full Version String
+// A main version, followed optionally by a pre-release version and
+// build metadata.
+
+// Note that the only major, minor, patch, and pre-release sections of
+// the version string are capturing groups.  The build metadata is not a
+// capturing group, because it should not ever be used in version
+// comparison.
+
+tok('FULL')
+tok('FULLPLAIN')
+src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] +
+                  src[t.PRERELEASE] + '?' +
+                  src[t.BUILD] + '?'
+
+src[t.FULL] = '^' + src[t.FULLPLAIN] + '$'
+
+// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
+// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
+// common in the npm registry.
+tok('LOOSEPLAIN')
+src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] +
+                  src[t.PRERELEASELOOSE] + '?' +
+                  src[t.BUILD] + '?'
+
+tok('LOOSE')
+src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$'
+
+tok('GTLT')
+src[t.GTLT] = '((?:<|>)?=?)'
+
+// Something like "2.*" or "1.2.x".
+// Note that "x.x" is a valid xRange identifer, meaning "any version"
+// Only the first item is strictly required.
+tok('XRANGEIDENTIFIERLOOSE')
+src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'
+tok('XRANGEIDENTIFIER')
+src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*'
+
+tok('XRANGEPLAIN')
+src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' +
+                   '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' +
+                   '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' +
+                   '(?:' + src[t.PRERELEASE] + ')?' +
+                   src[t.BUILD] + '?' +
+                   ')?)?'
+
+tok('XRANGEPLAINLOOSE')
+src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:' + src[t.PRERELEASELOOSE] + ')?' +
+                        src[t.BUILD] + '?' +
+                        ')?)?'
+
+tok('XRANGE')
+src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$'
+tok('XRANGELOOSE')
+src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$'
+
+// Coercion.
+// Extract anything that could conceivably be a part of a valid semver
+tok('COERCE')
+src[t.COERCE] = '(^|[^\\d])' +
+              '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' +
+              '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' +
+              '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' +
+              '(?:$|[^\\d])'
+tok('COERCERTL')
+re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g')
+safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g')
+
+// Tilde ranges.
+// Meaning is "reasonably at or greater than"
+tok('LONETILDE')
+src[t.LONETILDE] = '(?:~>?)'
+
+tok('TILDETRIM')
+src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+'
+re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g')
+safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g')
+var tildeTrimReplace = '$1~'
+
+tok('TILDE')
+src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$'
+tok('TILDELOOSE')
+src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$'
+
+// Caret ranges.
+// Meaning is "at least and backwards compatible with"
+tok('LONECARET')
+src[t.LONECARET] = '(?:\\^)'
+
+tok('CARETTRIM')
+src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+'
+re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g')
+safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g')
+var caretTrimReplace = '$1^'
+
+tok('CARET')
+src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$'
+tok('CARETLOOSE')
+src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$'
+
+// A simple gt/lt/eq thing, or just "" to indicate "any version"
+tok('COMPARATORLOOSE')
+src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$'
+tok('COMPARATOR')
+src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$'
+
+// An expression to strip any whitespace between the gtlt and the thing
+// it modifies, so that `> 1.2.3` ==> `>1.2.3`
+tok('COMPARATORTRIM')
+src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] +
+                      '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')'
+
+// this one has to use the /g flag
+re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g')
+safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g')
+var comparatorTrimReplace = '$1$2$3'
+
+// Something like `1.2.3 - 1.2.4`
+// Note that these all use the loose form, because they'll be
+// checked against either the strict or loose comparator form
+// later.
+tok('HYPHENRANGE')
+src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' +
+                   '\\s+-\\s+' +
+                   '(' + src[t.XRANGEPLAIN] + ')' +
+                   '\\s*$'
+
+tok('HYPHENRANGELOOSE')
+src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' +
+                        '\\s+-\\s+' +
+                        '(' + src[t.XRANGEPLAINLOOSE] + ')' +
+                        '\\s*$'
+
+// Star ranges basically just allow anything at all.
+tok('STAR')
+src[t.STAR] = '(<|>)?=?\\s*\\*'
+
+// Compile to actual regexp objects.
+// All are flag-free, unless they were created above with a flag.
+for (var i = 0; i < R; i++) {
+  debug(i, src[i])
+  if (!re[i]) {
+    re[i] = new RegExp(src[i])
+
+    // Replace all greedy whitespace to prevent regex dos issues. These regex are
+    // used internally via the safeRe object since all inputs in this library get
+    // normalized first to trim and collapse all extra whitespace. The original
+    // regexes are exported for userland consumption and lower level usage. A
+    // future breaking change could export the safer regex only with a note that
+    // all input should have extra whitespace removed.
+    safeRe[i] = new RegExp(makeSafeRe(src[i]))
+  }
+}
+
+exports.parse = parse
+function parse (version, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  if (version instanceof SemVer) {
+    return version
+  }
+
+  if (typeof version !== 'string') {
+    return null
+  }
+
+  if (version.length > MAX_LENGTH) {
+    return null
+  }
+
+  var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]
+  if (!r.test(version)) {
+    return null
+  }
+
+  try {
+    return new SemVer(version, options)
+  } catch (er) {
+    return null
+  }
+}
+
+exports.valid = valid
+function valid (version, options) {
+  var v = parse(version, options)
+  return v ? v.version : null
+}
+
+exports.clean = clean
+function clean (version, options) {
+  var s = parse(version.trim().replace(/^[=v]+/, ''), options)
+  return s ? s.version : null
+}
+
+exports.SemVer = SemVer
+
+function SemVer (version, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+  if (version instanceof SemVer) {
+    if (version.loose === options.loose) {
+      return version
+    } else {
+      version = version.version
+    }
+  } else if (typeof version !== 'string') {
+    throw new TypeError('Invalid Version: ' + version)
+  }
+
+  if (version.length > MAX_LENGTH) {
+    throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters')
+  }
+
+  if (!(this instanceof SemVer)) {
+    return new SemVer(version, options)
+  }
+
+  debug('SemVer', version, options)
+  this.options = options
+  this.loose = !!options.loose
+
+  var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL])
+
+  if (!m) {
+    throw new TypeError('Invalid Version: ' + version)
+  }
+
+  this.raw = version
+
+  // these are actually numbers
+  this.major = +m[1]
+  this.minor = +m[2]
+  this.patch = +m[3]
+
+  if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
+    throw new TypeError('Invalid major version')
+  }
+
+  if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
+    throw new TypeError('Invalid minor version')
+  }
+
+  if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
+    throw new TypeError('Invalid patch version')
+  }
+
+  // numberify any prerelease numeric ids
+  if (!m[4]) {
+    this.prerelease = []
+  } else {
+    this.prerelease = m[4].split('.').map(function (id) {
+      if (/^[0-9]+$/.test(id)) {
+        var num = +id
+        if (num >= 0 && num < MAX_SAFE_INTEGER) {
+          return num
+        }
+      }
+      return id
+    })
+  }
+
+  this.build = m[5] ? m[5].split('.') : []
+  this.format()
+}
+
+SemVer.prototype.format = function () {
+  this.version = this.major + '.' + this.minor + '.' + this.patch
+  if (this.prerelease.length) {
+    this.version += '-' + this.prerelease.join('.')
+  }
+  return this.version
+}
+
+SemVer.prototype.toString = function () {
+  return this.version
+}
+
+SemVer.prototype.compare = function (other) {
+  debug('SemVer.compare', this.version, this.options, other)
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  return this.compareMain(other) || this.comparePre(other)
+}
+
+SemVer.prototype.compareMain = function (other) {
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  return compareIdentifiers(this.major, other.major) ||
+         compareIdentifiers(this.minor, other.minor) ||
+         compareIdentifiers(this.patch, other.patch)
+}
+
+SemVer.prototype.comparePre = function (other) {
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  // NOT having a prerelease is > having one
+  if (this.prerelease.length && !other.prerelease.length) {
+    return -1
+  } else if (!this.prerelease.length && other.prerelease.length) {
+    return 1
+  } else if (!this.prerelease.length && !other.prerelease.length) {
+    return 0
+  }
+
+  var i = 0
+  do {
+    var a = this.prerelease[i]
+    var b = other.prerelease[i]
+    debug('prerelease compare', i, a, b)
+    if (a === undefined && b === undefined) {
+      return 0
+    } else if (b === undefined) {
+      return 1
+    } else if (a === undefined) {
+      return -1
+    } else if (a === b) {
+      continue
+    } else {
+      return compareIdentifiers(a, b)
+    }
+  } while (++i)
+}
+
+SemVer.prototype.compareBuild = function (other) {
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  var i = 0
+  do {
+    var a = this.build[i]
+    var b = other.build[i]
+    debug('prerelease compare', i, a, b)
+    if (a === undefined && b === undefined) {
+      return 0
+    } else if (b === undefined) {
+      return 1
+    } else if (a === undefined) {
+      return -1
+    } else if (a === b) {
+      continue
+    } else {
+      return compareIdentifiers(a, b)
+    }
+  } while (++i)
+}
+
+// preminor will bump the version up to the next minor release, and immediately
+// down to pre-release. premajor and prepatch work the same way.
+SemVer.prototype.inc = function (release, identifier) {
+  switch (release) {
+    case 'premajor':
+      this.prerelease.length = 0
+      this.patch = 0
+      this.minor = 0
+      this.major++
+      this.inc('pre', identifier)
+      break
+    case 'preminor':
+      this.prerelease.length = 0
+      this.patch = 0
+      this.minor++
+      this.inc('pre', identifier)
+      break
+    case 'prepatch':
+      // If this is already a prerelease, it will bump to the next version
+      // drop any prereleases that might already exist, since they are not
+      // relevant at this point.
+      this.prerelease.length = 0
+      this.inc('patch', identifier)
+      this.inc('pre', identifier)
+      break
+    // If the input is a non-prerelease version, this acts the same as
+    // prepatch.
+    case 'prerelease':
+      if (this.prerelease.length === 0) {
+        this.inc('patch', identifier)
+      }
+      this.inc('pre', identifier)
+      break
+
+    case 'major':
+      // If this is a pre-major version, bump up to the same major version.
+      // Otherwise increment major.
+      // 1.0.0-5 bumps to 1.0.0
+      // 1.1.0 bumps to 2.0.0
+      if (this.minor !== 0 ||
+          this.patch !== 0 ||
+          this.prerelease.length === 0) {
+        this.major++
+      }
+      this.minor = 0
+      this.patch = 0
+      this.prerelease = []
+      break
+    case 'minor':
+      // If this is a pre-minor version, bump up to the same minor version.
+      // Otherwise increment minor.
+      // 1.2.0-5 bumps to 1.2.0
+      // 1.2.1 bumps to 1.3.0
+      if (this.patch !== 0 || this.prerelease.length === 0) {
+        this.minor++
+      }
+      this.patch = 0
+      this.prerelease = []
+      break
+    case 'patch':
+      // If this is not a pre-release version, it will increment the patch.
+      // If it is a pre-release it will bump up to the same patch version.
+      // 1.2.0-5 patches to 1.2.0
+      // 1.2.0 patches to 1.2.1
+      if (this.prerelease.length === 0) {
+        this.patch++
+      }
+      this.prerelease = []
+      break
+    // This probably shouldn't be used publicly.
+    // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
+    case 'pre':
+      if (this.prerelease.length === 0) {
+        this.prerelease = [0]
+      } else {
+        var i = this.prerelease.length
+        while (--i >= 0) {
+          if (typeof this.prerelease[i] === 'number') {
+            this.prerelease[i]++
+            i = -2
+          }
+        }
+        if (i === -1) {
+          // didn't increment anything
+          this.prerelease.push(0)
+        }
+      }
+      if (identifier) {
+        // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
+        // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
+        if (this.prerelease[0] === identifier) {
+          if (isNaN(this.prerelease[1])) {
+            this.prerelease = [identifier, 0]
+          }
+        } else {
+          this.prerelease = [identifier, 0]
+        }
+      }
+      break
+
+    default:
+      throw new Error('invalid increment argument: ' + release)
+  }
+  this.format()
+  this.raw = this.version
+  return this
+}
+
+exports.inc = inc
+function inc (version, release, loose, identifier) {
+  if (typeof (loose) === 'string') {
+    identifier = loose
+    loose = undefined
+  }
+
+  try {
+    return new SemVer(version, loose).inc(release, identifier).version
+  } catch (er) {
+    return null
+  }
+}
+
+exports.diff = diff
+function diff (version1, version2) {
+  if (eq(version1, version2)) {
+    return null
+  } else {
+    var v1 = parse(version1)
+    var v2 = parse(version2)
+    var prefix = ''
+    if (v1.prerelease.length || v2.prerelease.length) {
+      prefix = 'pre'
+      var defaultResult = 'prerelease'
+    }
+    for (var key in v1) {
+      if (key === 'major' || key === 'minor' || key === 'patch') {
+        if (v1[key] !== v2[key]) {
+          return prefix + key
+        }
+      }
+    }
+    return defaultResult // may be undefined
+  }
+}
+
+exports.compareIdentifiers = compareIdentifiers
+
+var numeric = /^[0-9]+$/
+function compareIdentifiers (a, b) {
+  var anum = numeric.test(a)
+  var bnum = numeric.test(b)
+
+  if (anum && bnum) {
+    a = +a
+    b = +b
+  }
+
+  return a === b ? 0
+    : (anum && !bnum) ? -1
+    : (bnum && !anum) ? 1
+    : a < b ? -1
+    : 1
+}
+
+exports.rcompareIdentifiers = rcompareIdentifiers
+function rcompareIdentifiers (a, b) {
+  return compareIdentifiers(b, a)
+}
+
+exports.major = major
+function major (a, loose) {
+  return new SemVer(a, loose).major
+}
+
+exports.minor = minor
+function minor (a, loose) {
+  return new SemVer(a, loose).minor
+}
+
+exports.patch = patch
+function patch (a, loose) {
+  return new SemVer(a, loose).patch
+}
+
+exports.compare = compare
+function compare (a, b, loose) {
+  return new SemVer(a, loose).compare(new SemVer(b, loose))
+}
+
+exports.compareLoose = compareLoose
+function compareLoose (a, b) {
+  return compare(a, b, true)
+}
+
+exports.compareBuild = compareBuild
+function compareBuild (a, b, loose) {
+  var versionA = new SemVer(a, loose)
+  var versionB = new SemVer(b, loose)
+  return versionA.compare(versionB) || versionA.compareBuild(versionB)
+}
+
+exports.rcompare = rcompare
+function rcompare (a, b, loose) {
+  return compare(b, a, loose)
+}
+
+exports.sort = sort
+function sort (list, loose) {
+  return list.sort(function (a, b) {
+    return exports.compareBuild(a, b, loose)
+  })
+}
+
+exports.rsort = rsort
+function rsort (list, loose) {
+  return list.sort(function (a, b) {
+    return exports.compareBuild(b, a, loose)
+  })
+}
+
+exports.gt = gt
+function gt (a, b, loose) {
+  return compare(a, b, loose) > 0
+}
+
+exports.lt = lt
+function lt (a, b, loose) {
+  return compare(a, b, loose) < 0
+}
+
+exports.eq = eq
+function eq (a, b, loose) {
+  return compare(a, b, loose) === 0
+}
+
+exports.neq = neq
+function neq (a, b, loose) {
+  return compare(a, b, loose) !== 0
+}
+
+exports.gte = gte
+function gte (a, b, loose) {
+  return compare(a, b, loose) >= 0
+}
+
+exports.lte = lte
+function lte (a, b, loose) {
+  return compare(a, b, loose) <= 0
+}
+
+exports.cmp = cmp
+function cmp (a, op, b, loose) {
+  switch (op) {
+    case '===':
+      if (typeof a === 'object')
+        a = a.version
+      if (typeof b === 'object')
+        b = b.version
+      return a === b
+
+    case '!==':
+      if (typeof a === 'object')
+        a = a.version
+      if (typeof b === 'object')
+        b = b.version
+      return a !== b
+
+    case '':
+    case '=':
+    case '==':
+      return eq(a, b, loose)
+
+    case '!=':
+      return neq(a, b, loose)
+
+    case '>':
+      return gt(a, b, loose)
+
+    case '>=':
+      return gte(a, b, loose)
+
+    case '<':
+      return lt(a, b, loose)
+
+    case '<=':
+      return lte(a, b, loose)
+
+    default:
+      throw new TypeError('Invalid operator: ' + op)
+  }
+}
+
+exports.Comparator = Comparator
+function Comparator (comp, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  if (comp instanceof Comparator) {
+    if (comp.loose === !!options.loose) {
+      return comp
+    } else {
+      comp = comp.value
+    }
+  }
+
+  if (!(this instanceof Comparator)) {
+    return new Comparator(comp, options)
+  }
+
+  comp = comp.trim().split(/\s+/).join(' ')
+  debug('comparator', comp, options)
+  this.options = options
+  this.loose = !!options.loose
+  this.parse(comp)
+
+  if (this.semver === ANY) {
+    this.value = ''
+  } else {
+    this.value = this.operator + this.semver.version
+  }
+
+  debug('comp', this)
+}
+
+var ANY = {}
+Comparator.prototype.parse = function (comp) {
+  var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR]
+  var m = comp.match(r)
+
+  if (!m) {
+    throw new TypeError('Invalid comparator: ' + comp)
+  }
+
+  this.operator = m[1] !== undefined ? m[1] : ''
+  if (this.operator === '=') {
+    this.operator = ''
+  }
+
+  // if it literally is just '>' or '' then allow anything.
+  if (!m[2]) {
+    this.semver = ANY
+  } else {
+    this.semver = new SemVer(m[2], this.options.loose)
+  }
+}
+
+Comparator.prototype.toString = function () {
+  return this.value
+}
+
+Comparator.prototype.test = function (version) {
+  debug('Comparator.test', version, this.options.loose)
+
+  if (this.semver === ANY || version === ANY) {
+    return true
+  }
+
+  if (typeof version === 'string') {
+    try {
+      version = new SemVer(version, this.options)
+    } catch (er) {
+      return false
+    }
+  }
+
+  return cmp(version, this.operator, this.semver, this.options)
+}
+
+Comparator.prototype.intersects = function (comp, options) {
+  if (!(comp instanceof Comparator)) {
+    throw new TypeError('a Comparator is required')
+  }
+
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  var rangeTmp
+
+  if (this.operator === '') {
+    if (this.value === '') {
+      return true
+    }
+    rangeTmp = new Range(comp.value, options)
+    return satisfies(this.value, rangeTmp, options)
+  } else if (comp.operator === '') {
+    if (comp.value === '') {
+      return true
+    }
+    rangeTmp = new Range(this.value, options)
+    return satisfies(comp.semver, rangeTmp, options)
+  }
+
+  var sameDirectionIncreasing =
+    (this.operator === '>=' || this.operator === '>') &&
+    (comp.operator === '>=' || comp.operator === '>')
+  var sameDirectionDecreasing =
+    (this.operator === '<=' || this.operator === '<') &&
+    (comp.operator === '<=' || comp.operator === '<')
+  var sameSemVer = this.semver.version === comp.semver.version
+  var differentDirectionsInclusive =
+    (this.operator === '>=' || this.operator === '<=') &&
+    (comp.operator === '>=' || comp.operator === '<=')
+  var oppositeDirectionsLessThan =
+    cmp(this.semver, '<', comp.semver, options) &&
+    ((this.operator === '>=' || this.operator === '>') &&
+    (comp.operator === '<=' || comp.operator === '<'))
+  var oppositeDirectionsGreaterThan =
+    cmp(this.semver, '>', comp.semver, options) &&
+    ((this.operator === '<=' || this.operator === '<') &&
+    (comp.operator === '>=' || comp.operator === '>'))
+
+  return sameDirectionIncreasing || sameDirectionDecreasing ||
+    (sameSemVer && differentDirectionsInclusive) ||
+    oppositeDirectionsLessThan || oppositeDirectionsGreaterThan
+}
+
+exports.Range = Range
+function Range (range, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  if (range instanceof Range) {
+    if (range.loose === !!options.loose &&
+        range.includePrerelease === !!options.includePrerelease) {
+      return range
+    } else {
+      return new Range(range.raw, options)
+    }
+  }
+
+  if (range instanceof Comparator) {
+    return new Range(range.value, options)
+  }
+
+  if (!(this instanceof Range)) {
+    return new Range(range, options)
+  }
+
+  this.options = options
+  this.loose = !!options.loose
+  this.includePrerelease = !!options.includePrerelease
+
+  // First reduce all whitespace as much as possible so we do not have to rely
+  // on potentially slow regexes like \s*. This is then stored and used for
+  // future error messages as well.
+  this.raw = range
+    .trim()
+    .split(/\s+/)
+    .join(' ')
+
+  // First, split based on boolean or ||
+  this.set = this.raw.split('||').map(function (range) {
+    return this.parseRange(range.trim())
+  }, this).filter(function (c) {
+    // throw out any that are not relevant for whatever reason
+    return c.length
+  })
+
+  if (!this.set.length) {
+    throw new TypeError('Invalid SemVer Range: ' + this.raw)
+  }
+
+  this.format()
+}
+
+Range.prototype.format = function () {
+  this.range = this.set.map(function (comps) {
+    return comps.join(' ').trim()
+  }).join('||').trim()
+  return this.range
+}
+
+Range.prototype.toString = function () {
+  return this.range
+}
+
+Range.prototype.parseRange = function (range) {
+  var loose = this.options.loose
+  // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
+  var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE]
+  range = range.replace(hr, hyphenReplace)
+  debug('hyphen replace', range)
+  // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
+  range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace)
+  debug('comparator trim', range, safeRe[t.COMPARATORTRIM])
+
+  // `~ 1.2.3` => `~1.2.3`
+  range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace)
+
+  // `^ 1.2.3` => `^1.2.3`
+  range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace)
+
+  // normalize spaces
+  range = range.split(/\s+/).join(' ')
+
+  // At this point, the range is completely trimmed and
+  // ready to be split into comparators.
+
+  var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR]
+  var set = range.split(' ').map(function (comp) {
+    return parseComparator(comp, this.options)
+  }, this).join(' ').split(/\s+/)
+  if (this.options.loose) {
+    // in loose mode, throw out any that are not valid comparators
+    set = set.filter(function (comp) {
+      return !!comp.match(compRe)
+    })
+  }
+  set = set.map(function (comp) {
+    return new Comparator(comp, this.options)
+  }, this)
+
+  return set
+}
+
+Range.prototype.intersects = function (range, options) {
+  if (!(range instanceof Range)) {
+    throw new TypeError('a Range is required')
+  }
+
+  return this.set.some(function (thisComparators) {
+    return (
+      isSatisfiable(thisComparators, options) &&
+      range.set.some(function (rangeComparators) {
+        return (
+          isSatisfiable(rangeComparators, options) &&
+          thisComparators.every(function (thisComparator) {
+            return rangeComparators.every(function (rangeComparator) {
+              return thisComparator.intersects(rangeComparator, options)
+            })
+          })
+        )
+      })
+    )
+  })
+}
+
+// take a set of comparators and determine whether there
+// exists a version which can satisfy it
+function isSatisfiable (comparators, options) {
+  var result = true
+  var remainingComparators = comparators.slice()
+  var testComparator = remainingComparators.pop()
+
+  while (result && remainingComparators.length) {
+    result = remainingComparators.every(function (otherComparator) {
+      return testComparator.intersects(otherComparator, options)
+    })
+
+    testComparator = remainingComparators.pop()
+  }
+
+  return result
+}
+
+// Mostly just for testing and legacy API reasons
+exports.toComparators = toComparators
+function toComparators (range, options) {
+  return new Range(range, options).set.map(function (comp) {
+    return comp.map(function (c) {
+      return c.value
+    }).join(' ').trim().split(' ')
+  })
+}
+
+// comprised of xranges, tildes, stars, and gtlt's at this point.
+// already replaced the hyphen ranges
+// turn into a set of JUST comparators.
+function parseComparator (comp, options) {
+  debug('comp', comp, options)
+  comp = replaceCarets(comp, options)
+  debug('caret', comp)
+  comp = replaceTildes(comp, options)
+  debug('tildes', comp)
+  comp = replaceXRanges(comp, options)
+  debug('xrange', comp)
+  comp = replaceStars(comp, options)
+  debug('stars', comp)
+  return comp
+}
+
+function isX (id) {
+  return !id || id.toLowerCase() === 'x' || id === '*'
+}
+
+// ~, ~> --> * (any, kinda silly)
+// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
+// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
+// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
+// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
+// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
+function replaceTildes (comp, options) {
+  return comp.trim().split(/\s+/).map(function (comp) {
+    return replaceTilde(comp, options)
+  }).join(' ')
+}
+
+function replaceTilde (comp, options) {
+  var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE]
+  return comp.replace(r, function (_, M, m, p, pr) {
+    debug('tilde', comp, _, M, m, p, pr)
+    var ret
+
+    if (isX(M)) {
+      ret = ''
+    } else if (isX(m)) {
+      ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'
+    } else if (isX(p)) {
+      // ~1.2 == >=1.2.0 <1.3.0
+      ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'
+    } else if (pr) {
+      debug('replaceTilde pr', pr)
+      ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+            ' <' + M + '.' + (+m + 1) + '.0'
+    } else {
+      // ~1.2.3 == >=1.2.3 <1.3.0
+      ret = '>=' + M + '.' + m + '.' + p +
+            ' <' + M + '.' + (+m + 1) + '.0'
+    }
+
+    debug('tilde return', ret)
+    return ret
+  })
+}
+
+// ^ --> * (any, kinda silly)
+// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
+// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
+// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
+// ^1.2.3 --> >=1.2.3 <2.0.0
+// ^1.2.0 --> >=1.2.0 <2.0.0
+function replaceCarets (comp, options) {
+  return comp.trim().split(/\s+/).map(function (comp) {
+    return replaceCaret(comp, options)
+  }).join(' ')
+}
+
+function replaceCaret (comp, options) {
+  debug('caret', comp, options)
+  var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET]
+  return comp.replace(r, function (_, M, m, p, pr) {
+    debug('caret', comp, _, M, m, p, pr)
+    var ret
+
+    if (isX(M)) {
+      ret = ''
+    } else if (isX(m)) {
+      ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'
+    } else if (isX(p)) {
+      if (M === '0') {
+        ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'
+      } else {
+        ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'
+      }
+    } else if (pr) {
+      debug('replaceCaret pr', pr)
+      if (M === '0') {
+        if (m === '0') {
+          ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+                ' <' + M + '.' + m + '.' + (+p + 1)
+        } else {
+          ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+                ' <' + M + '.' + (+m + 1) + '.0'
+        }
+      } else {
+        ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+              ' <' + (+M + 1) + '.0.0'
+      }
+    } else {
+      debug('no pr')
+      if (M === '0') {
+        if (m === '0') {
+          ret = '>=' + M + '.' + m + '.' + p +
+                ' <' + M + '.' + m + '.' + (+p + 1)
+        } else {
+          ret = '>=' + M + '.' + m + '.' + p +
+                ' <' + M + '.' + (+m + 1) + '.0'
+        }
+      } else {
+        ret = '>=' + M + '.' + m + '.' + p +
+              ' <' + (+M + 1) + '.0.0'
+      }
+    }
+
+    debug('caret return', ret)
+    return ret
+  })
+}
+
+function replaceXRanges (comp, options) {
+  debug('replaceXRanges', comp, options)
+  return comp.split(/\s+/).map(function (comp) {
+    return replaceXRange(comp, options)
+  }).join(' ')
+}
+
+function replaceXRange (comp, options) {
+  comp = comp.trim()
+  var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE]
+  return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
+    debug('xRange', comp, ret, gtlt, M, m, p, pr)
+    var xM = isX(M)
+    var xm = xM || isX(m)
+    var xp = xm || isX(p)
+    var anyX = xp
+
+    if (gtlt === '=' && anyX) {
+      gtlt = ''
+    }
+
+    // if we're including prereleases in the match, then we need
+    // to fix this to -0, the lowest possible prerelease value
+    pr = options.includePrerelease ? '-0' : ''
+
+    if (xM) {
+      if (gtlt === '>' || gtlt === '<') {
+        // nothing is allowed
+        ret = '<0.0.0-0'
+      } else {
+        // nothing is forbidden
+        ret = '*'
+      }
+    } else if (gtlt && anyX) {
+      // we know patch is an x, because we have any x at all.
+      // replace X with 0
+      if (xm) {
+        m = 0
+      }
+      p = 0
+
+      if (gtlt === '>') {
+        // >1 => >=2.0.0
+        // >1.2 => >=1.3.0
+        // >1.2.3 => >= 1.2.4
+        gtlt = '>='
+        if (xm) {
+          M = +M + 1
+          m = 0
+          p = 0
+        } else {
+          m = +m + 1
+          p = 0
+        }
+      } else if (gtlt === '<=') {
+        // <=0.7.x is actually <0.8.0, since any 0.7.x should
+        // pass.  Similarly, <=7.x is actually <8.0.0, etc.
+        gtlt = '<'
+        if (xm) {
+          M = +M + 1
+        } else {
+          m = +m + 1
+        }
+      }
+
+      ret = gtlt + M + '.' + m + '.' + p + pr
+    } else if (xm) {
+      ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr
+    } else if (xp) {
+      ret = '>=' + M + '.' + m + '.0' + pr +
+        ' <' + M + '.' + (+m + 1) + '.0' + pr
+    }
+
+    debug('xRange return', ret)
+
+    return ret
+  })
+}
+
+// Because * is AND-ed with everything else in the comparator,
+// and '' means "any version", just remove the *s entirely.
+function replaceStars (comp, options) {
+  debug('replaceStars', comp, options)
+  // Looseness is ignored here.  star is always as loose as it gets!
+  return comp.trim().replace(safeRe[t.STAR], '')
+}
+
+// This function is passed to string.replace(re[t.HYPHENRANGE])
+// M, m, patch, prerelease, build
+// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
+// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
+// 1.2 - 3.4 => >=1.2.0 <3.5.0
+function hyphenReplace ($0,
+  from, fM, fm, fp, fpr, fb,
+  to, tM, tm, tp, tpr, tb) {
+  if (isX(fM)) {
+    from = ''
+  } else if (isX(fm)) {
+    from = '>=' + fM + '.0.0'
+  } else if (isX(fp)) {
+    from = '>=' + fM + '.' + fm + '.0'
+  } else {
+    from = '>=' + from
+  }
+
+  if (isX(tM)) {
+    to = ''
+  } else if (isX(tm)) {
+    to = '<' + (+tM + 1) + '.0.0'
+  } else if (isX(tp)) {
+    to = '<' + tM + '.' + (+tm + 1) + '.0'
+  } else if (tpr) {
+    to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr
+  } else {
+    to = '<=' + to
+  }
+
+  return (from + ' ' + to).trim()
+}
+
+// if ANY of the sets match ALL of its comparators, then pass
+Range.prototype.test = function (version) {
+  if (!version) {
+    return false
+  }
+
+  if (typeof version === 'string') {
+    try {
+      version = new SemVer(version, this.options)
+    } catch (er) {
+      return false
+    }
+  }
+
+  for (var i = 0; i < this.set.length; i++) {
+    if (testSet(this.set[i], version, this.options)) {
+      return true
+    }
+  }
+  return false
+}
+
+function testSet (set, version, options) {
+  for (var i = 0; i < set.length; i++) {
+    if (!set[i].test(version)) {
+      return false
+    }
+  }
+
+  if (version.prerelease.length && !options.includePrerelease) {
+    // Find the set of versions that are allowed to have prereleases
+    // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
+    // That should allow `1.2.3-pr.2` to pass.
+    // However, `1.2.4-alpha.notready` should NOT be allowed,
+    // even though it's within the range set by the comparators.
+    for (i = 0; i < set.length; i++) {
+      debug(set[i].semver)
+      if (set[i].semver === ANY) {
+        continue
+      }
+
+      if (set[i].semver.prerelease.length > 0) {
+        var allowed = set[i].semver
+        if (allowed.major === version.major &&
+            allowed.minor === version.minor &&
+            allowed.patch === version.patch) {
+          return true
+        }
+      }
+    }
+
+    // Version has a -pre, but it's not one of the ones we like.
+    return false
+  }
+
+  return true
+}
+
+exports.satisfies = satisfies
+function satisfies (version, range, options) {
+  try {
+    range = new Range(range, options)
+  } catch (er) {
+    return false
+  }
+  return range.test(version)
+}
+
+exports.maxSatisfying = maxSatisfying
+function maxSatisfying (versions, range, options) {
+  var max = null
+  var maxSV = null
+  try {
+    var rangeObj = new Range(range, options)
+  } catch (er) {
+    return null
+  }
+  versions.forEach(function (v) {
+    if (rangeObj.test(v)) {
+      // satisfies(v, range, options)
+      if (!max || maxSV.compare(v) === -1) {
+        // compare(max, v, true)
+        max = v
+        maxSV = new SemVer(max, options)
+      }
+    }
+  })
+  return max
+}
+
+exports.minSatisfying = minSatisfying
+function minSatisfying (versions, range, options) {
+  var min = null
+  var minSV = null
+  try {
+    var rangeObj = new Range(range, options)
+  } catch (er) {
+    return null
+  }
+  versions.forEach(function (v) {
+    if (rangeObj.test(v)) {
+      // satisfies(v, range, options)
+      if (!min || minSV.compare(v) === 1) {
+        // compare(min, v, true)
+        min = v
+        minSV = new SemVer(min, options)
+      }
+    }
+  })
+  return min
+}
+
+exports.minVersion = minVersion
+function minVersion (range, loose) {
+  range = new Range(range, loose)
+
+  var minver = new SemVer('0.0.0')
+  if (range.test(minver)) {
+    return minver
+  }
+
+  minver = new SemVer('0.0.0-0')
+  if (range.test(minver)) {
+    return minver
+  }
+
+  minver = null
+  for (var i = 0; i < range.set.length; ++i) {
+    var comparators = range.set[i]
+
+    comparators.forEach(function (comparator) {
+      // Clone to avoid manipulating the comparator's semver object.
+      var compver = new SemVer(comparator.semver.version)
+      switch (comparator.operator) {
+        case '>':
+          if (compver.prerelease.length === 0) {
+            compver.patch++
+          } else {
+            compver.prerelease.push(0)
+          }
+          compver.raw = compver.format()
+          /* fallthrough */
+        case '':
+        case '>=':
+          if (!minver || gt(minver, compver)) {
+            minver = compver
+          }
+          break
+        case '<':
+        case '<=':
+          /* Ignore maximum versions */
+          break
+        /* istanbul ignore next */
+        default:
+          throw new Error('Unexpected operation: ' + comparator.operator)
+      }
+    })
+  }
+
+  if (minver && range.test(minver)) {
+    return minver
+  }
+
+  return null
+}
+
+exports.validRange = validRange
+function validRange (range, options) {
+  try {
+    // Return '*' instead of '' so that truthiness works.
+    // This will throw if it's invalid anyway
+    return new Range(range, options).range || '*'
+  } catch (er) {
+    return null
+  }
+}
+
+// Determine if version is less than all the versions possible in the range
+exports.ltr = ltr
+function ltr (version, range, options) {
+  return outside(version, range, '<', options)
+}
+
+// Determine if version is greater than all the versions possible in the range.
+exports.gtr = gtr
+function gtr (version, range, options) {
+  return outside(version, range, '>', options)
+}
+
+exports.outside = outside
+function outside (version, range, hilo, options) {
+  version = new SemVer(version, options)
+  range = new Range(range, options)
+
+  var gtfn, ltefn, ltfn, comp, ecomp
+  switch (hilo) {
+    case '>':
+      gtfn = gt
+      ltefn = lte
+      ltfn = lt
+      comp = '>'
+      ecomp = '>='
+      break
+    case '<':
+      gtfn = lt
+      ltefn = gte
+      ltfn = gt
+      comp = '<'
+      ecomp = '<='
+      break
+    default:
+      throw new TypeError('Must provide a hilo val of "<" or ">"')
+  }
+
+  // If it satisifes the range it is not outside
+  if (satisfies(version, range, options)) {
+    return false
+  }
+
+  // From now on, variable terms are as if we're in "gtr" mode.
+  // but note that everything is flipped for the "ltr" function.
+
+  for (var i = 0; i < range.set.length; ++i) {
+    var comparators = range.set[i]
+
+    var high = null
+    var low = null
+
+    comparators.forEach(function (comparator) {
+      if (comparator.semver === ANY) {
+        comparator = new Comparator('>=0.0.0')
+      }
+      high = high || comparator
+      low = low || comparator
+      if (gtfn(comparator.semver, high.semver, options)) {
+        high = comparator
+      } else if (ltfn(comparator.semver, low.semver, options)) {
+        low = comparator
+      }
+    })
+
+    // If the edge version comparator has a operator then our version
+    // isn't outside it
+    if (high.operator === comp || high.operator === ecomp) {
+      return false
+    }
+
+    // If the lowest version comparator has an operator and our version
+    // is less than it then it isn't higher than the range
+    if ((!low.operator || low.operator === comp) &&
+        ltefn(version, low.semver)) {
+      return false
+    } else if (low.operator === ecomp && ltfn(version, low.semver)) {
+      return false
+    }
+  }
+  return true
+}
+
+exports.prerelease = prerelease
+function prerelease (version, options) {
+  var parsed = parse(version, options)
+  return (parsed && parsed.prerelease.length) ? parsed.prerelease : null
+}
+
+exports.intersects = intersects
+function intersects (r1, r2, options) {
+  r1 = new Range(r1, options)
+  r2 = new Range(r2, options)
+  return r1.intersects(r2)
+}
+
+exports.coerce = coerce
+function coerce (version, options) {
+  if (version instanceof SemVer) {
+    return version
+  }
+
+  if (typeof version === 'number') {
+    version = String(version)
+  }
+
+  if (typeof version !== 'string') {
+    return null
+  }
+
+  options = options || {}
+
+  var match = null
+  if (!options.rtl) {
+    match = version.match(safeRe[t.COERCE])
+  } else {
+    // Find the right-most coercible string that does not share
+    // a terminus with a more left-ward coercible string.
+    // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4'
+    //
+    // Walk through the string checking with a /g regexp
+    // Manually set the index so as to pick up overlapping matches.
+    // Stop when we get a match that ends at the string end, since no
+    // coercible string can be more right-ward without the same terminus.
+    var next
+    while ((next = safeRe[t.COERCERTL].exec(version)) &&
+      (!match || match.index + match[0].length !== version.length)
+    ) {
+      if (!match ||
+          next.index + next[0].length !== match.index + match[0].length) {
+        match = next
+      }
+      safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length
+    }
+    // leave it in a clean state
+    safeRe[t.COERCERTL].lastIndex = -1
+  }
+
+  if (match === null) {
+    return null
+  }
+
+  return parse(match[2] +
+    '.' + (match[3] || '0') +
+    '.' + (match[4] || '0'), options)
+}
Index: frontend/node_modules/eslint-plugin-react/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,125 @@
+{
+  "name": "eslint-plugin-react",
+  "version": "7.37.5",
+  "author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",
+  "description": "React specific linting rules for ESLint",
+  "main": "index.js",
+  "types": "index.d.ts",
+  "scripts": {
+    "clean-built-types": "rm -f $(find . -maxdepth 1 -type f -name '*.d.ts*') $(find lib -type f -name '*.d.ts*' ! -name 'types.d.ts')",
+    "prebuild-types": "npm run clean-built-types",
+    "build-types": "tsc -p build.tsconfig.json",
+    "prepack": "npm run build-types && npmignore --auto --commentLines=autogenerated",
+    "prelint": "npm run lint:docs",
+    "lint:docs": "markdownlint \"**/*.md\"",
+    "postlint:docs": "npm run update:eslint-docs -- --check",
+    "lint": "eslint .",
+    "postlint": "npm run type-check",
+    "pretest": "npm run lint",
+    "test": "npm run unit-test",
+    "posttest": "npx npm@'>= 10.2' audit --production",
+    "type-check": "tsc",
+    "unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js tests/flat-config.js",
+    "update:eslint-docs": "eslint-doc-generator"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jsx-eslint/eslint-plugin-react"
+  },
+  "directories": {
+    "test": [
+      "test",
+      "tests",
+      "test-published-types"
+    ]
+  },
+  "homepage": "https://github.com/jsx-eslint/eslint-plugin-react",
+  "bugs": "https://github.com/jsx-eslint/eslint-plugin-react/issues",
+  "dependencies": {
+    "array-includes": "^3.1.8",
+    "array.prototype.findlast": "^1.2.5",
+    "array.prototype.flatmap": "^1.3.3",
+    "array.prototype.tosorted": "^1.1.4",
+    "doctrine": "^2.1.0",
+    "es-iterator-helpers": "^1.2.1",
+    "estraverse": "^5.3.0",
+    "hasown": "^2.0.2",
+    "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+    "minimatch": "^3.1.2",
+    "object.entries": "^1.1.9",
+    "object.fromentries": "^2.0.8",
+    "object.values": "^1.2.1",
+    "prop-types": "^15.8.1",
+    "resolve": "^2.0.0-next.5",
+    "semver": "^6.3.1",
+    "string.prototype.matchall": "^4.0.12",
+    "string.prototype.repeat": "^1.0.0"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.26.10",
+    "@babel/eslint-parser": "^7.27.0",
+    "@babel/plugin-syntax-decorators": "^7.25.9",
+    "@babel/plugin-syntax-do-expressions": "^7.25.9",
+    "@babel/plugin-syntax-function-bind": "^7.25.9",
+    "@babel/preset-react": "^7.26.3",
+    "@types/eslint": "=7.2.10",
+    "@types/estree": "0.0.52",
+    "@types/node": "^4.9.5",
+    "@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4 || ^5 || ^6.20 || ^7.14.1 || 8.4 - 8.17",
+    "babel-eslint": "^8 || ^9 || ^10.1.0",
+    "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7",
+    "eslint-config-airbnb-base": "^15.0.0",
+    "eslint-doc-generator": "^1.7.1",
+    "eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1 || ^5.0.5",
+    "eslint-plugin-import": "^2.31.0",
+    "eslint-remote-tester": "^3.0.1",
+    "eslint-remote-tester-repositories": "^1.0.1",
+    "eslint-scope": "^3.7.3",
+    "espree": "^3.5.4",
+    "gfm-footnotes": "^1.0.1",
+    "glob": "=10.3.7",
+    "istanbul": "^0.4.5",
+    "jackspeak": "=2.1.1",
+    "ls-engines": "^0.8.1",
+    "markdownlint-cli": "^0.8.0 || ^0.32.2",
+    "mocha": "^5.2.0",
+    "npmignore": "^0.3.1",
+    "sinon": "^7.5.0",
+    "typescript": "^3.9.9",
+    "typescript-eslint-parser": "^20.1.1"
+  },
+  "peerDependencies": {
+    "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+  },
+  "engines": {
+    "node": ">=4"
+  },
+  "keywords": [
+    "eslint",
+    "eslint-plugin",
+    "eslintplugin",
+    "react"
+  ],
+  "license": "MIT",
+  "publishConfig": {
+    "ignore": [
+      ".github/",
+      "!lib",
+      "docs/",
+      "test/",
+      "test-published-types/",
+      "tests/",
+      "*.md",
+      "*.config.js",
+      ".eslint-doc-generatorrc.js",
+      ".eslintrc",
+      ".editorconfig",
+      "tsconfig.json",
+      "build.tsconfig.json",
+      ".markdownlint*",
+      "types",
+      "!*.d.ts",
+      "!*.d.ts.map"
+    ]
+  }
+}
