Browse Source

[web] Add ESLint to enforce HMR how vite likes it (#1433)

Configuration changes taken from the vite starter (react-ts).

From the [vite
docs](https://github.com/vitejs/vite-plugin-react-swc?tab=readme-ov-file#consistent-components-exports)

> For React refresh to work correctly, your file should only export
React components.
>
> If an incompatible change in exports is found, the module will be
invalidated. To make it easier to export simple constants alongside your
component the module is only invalidated when their value changes.

While we're not currently using vite for our main apps, it would be
worthwhile to write code in a way that a future migration to it is
painless if needed.

About the what and the why, the Gatsby docs are illuminating:

https://www.gatsbyjs.com/docs/reference/local-development/fast-refresh/#how-it-works

Some other refs:
- https://github.com/ArnaudBarre/eslint-plugin-react-refresh
-
https://github.com/vitejs/vite/discussions/4577#discussioncomment-1161007
- https://github.com/vitejs/vite-plugin-react/issues/34
Manav Rathi 1 year ago
parent
commit
a6887e3037

+ 0 - 2
web/apps/photos/src/services/collectionService.ts

@@ -1385,7 +1385,6 @@ export async function moveToHiddenCollection(files: EnteFile[]) {
             hiddenCollection = await createHiddenCollection();
             hiddenCollection = await createHiddenCollection();
         }
         }
         const groupiedFiles = groupFilesBasedOnCollectionID(files);
         const groupiedFiles = groupFilesBasedOnCollectionID(files);
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
         for (const [collectionID, files] of groupiedFiles.entries()) {
         for (const [collectionID, files] of groupiedFiles.entries()) {
             if (collectionID === hiddenCollection.id) {
             if (collectionID === hiddenCollection.id) {
                 continue;
                 continue;
@@ -1404,7 +1403,6 @@ export async function unhideToCollection(
 ) {
 ) {
     try {
     try {
         const groupiedFiles = groupFilesBasedOnCollectionID(files);
         const groupiedFiles = groupFilesBasedOnCollectionID(files);
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
         for (const [collectionID, files] of groupiedFiles.entries()) {
         for (const [collectionID, files] of groupiedFiles.entries()) {
             if (collectionID === collection.id) {
             if (collectionID === collection.id) {
                 continue;
                 continue;

+ 2 - 0
web/docs/dependencies.md

@@ -17,6 +17,8 @@ package:
 -   "eslint-plugin-react-hooks", "eslint-plugin-react-namespace-import" - Some
 -   "eslint-plugin-react-hooks", "eslint-plugin-react-namespace-import" - Some
     React specific ESLint rules and configurations that are used by the
     React specific ESLint rules and configurations that are used by the
     workspaces that have React code.
     workspaces that have React code.
+-   "eslint-plugin-react-refresh" - A plugin to ensure that React components are
+    exported in a way that they can be HMR-ed.
 -   "prettier-plugin-organize-imports" - A Prettier plugin to sort imports.
 -   "prettier-plugin-organize-imports" - A Prettier plugin to sort imports.
 -   "prettier-plugin-packagejson" - A Prettier plugin to also prettify
 -   "prettier-plugin-packagejson" - A Prettier plugin to also prettify
     `package.json`.
     `package.json`.

+ 1 - 1
web/package.json

@@ -27,7 +27,7 @@
         "dev:payments": "yarn workspace payments dev",
         "dev:payments": "yarn workspace payments dev",
         "dev:photos": "yarn workspace photos next dev",
         "dev:photos": "yarn workspace photos next dev",
         "dev:staff": "yarn workspace staff dev",
         "dev:staff": "yarn workspace staff dev",
-        "lint": "yarn prettier --check . && yarn workspaces run eslint --report-unused-disable-directives",
+        "lint": "yarn prettier --check . && yarn workspaces run eslint --report-unused-disable-directives .",
         "lint-fix": "yarn prettier --write . && yarn workspaces run eslint --fix .",
         "lint-fix": "yarn prettier --write . && yarn workspaces run eslint --fix .",
         "preview": "yarn preview:photos",
         "preview": "yarn preview:photos",
         "preview:accounts": "yarn build:accounts && python3 -m http.server -d apps/accounts/out 3001",
         "preview:accounts": "yarn build:accounts && python3 -m http.server -d apps/accounts/out 3001",

+ 7 - 0
web/packages/build-config/eslintrc-react.js

@@ -5,5 +5,12 @@ module.exports = {
         "plugin:react/recommended",
         "plugin:react/recommended",
         "plugin:react-hooks/recommended",
         "plugin:react-hooks/recommended",
     ],
     ],
+    plugins: ["react-refresh"],
     settings: { react: { version: "18.2" } },
     settings: { react: { version: "18.2" } },
+    rules: {
+        "react-refresh/only-export-components": [
+            "warn",
+            { allowConstantExport: true },
+        ],
+    },
 };
 };

+ 1 - 0
web/packages/build-config/package.json

@@ -7,6 +7,7 @@
         "@typescript-eslint/parser": "^7",
         "@typescript-eslint/parser": "^7",
         "eslint-plugin-react": "^7.34",
         "eslint-plugin-react": "^7.34",
         "eslint-plugin-react-hooks": "^4.6",
         "eslint-plugin-react-hooks": "^4.6",
+        "eslint-plugin-react-refresh": "^0.4.6",
         "prettier-plugin-organize-imports": "^3.2",
         "prettier-plugin-organize-imports": "^3.2",
         "prettier-plugin-packagejson": "^2.4"
         "prettier-plugin-packagejson": "^2.4"
     }
     }

+ 1 - 0
web/packages/eslint-config/index.js

@@ -24,6 +24,7 @@ module.exports = {
         "max-len": "off",
         "max-len": "off",
         "new-cap": "off",
         "new-cap": "off",
         "no-invalid-this": "off",
         "no-invalid-this": "off",
+        "no-throw-literal": "error",
         // TODO(MR): We want this off anyway, for now forcing it here
         // TODO(MR): We want this off anyway, for now forcing it here
         eqeqeq: "off",
         eqeqeq: "off",
         "object-curly-spacing": ["error", "always"],
         "object-curly-spacing": ["error", "always"],

+ 0 - 1
web/packages/next/blob-cache.ts

@@ -148,7 +148,6 @@ const openOPFSCacheWeb = async (name: BlobCacheNamespace) => {
 
 
     const root = await navigator.storage.getDirectory();
     const root = await navigator.storage.getDirectory();
     const caches = await root.getDirectoryHandle("cache", { create: true });
     const caches = await root.getDirectoryHandle("cache", { create: true });
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     const cache = await caches.getDirectoryHandle(name, { create: true });
     const cache = await caches.getDirectoryHandle(name, { create: true });
 
 
     return {
     return {

+ 0 - 2
web/packages/shared/network/HTTPService.ts

@@ -125,7 +125,6 @@ class HTTPService {
     /**
     /**
      * Returns axios interceptors.
      * Returns axios interceptors.
      */
      */
-    // eslint-disable-next-line class-methods-use-this
     public getInterceptors() {
     public getInterceptors() {
         return axios.interceptors;
         return axios.interceptors;
     }
     }
@@ -137,7 +136,6 @@ class HTTPService {
      * over what was sent in config.
      * over what was sent in config.
      */
      */
     public async request(config: AxiosRequestConfig, customConfig?: any) {
     public async request(config: AxiosRequestConfig, customConfig?: any) {
-        // eslint-disable-next-line no-param-reassign
         config.headers = {
         config.headers = {
             ...this.headers,
             ...this.headers,
             ...config.headers,
             ...config.headers,

+ 0 - 1
web/packages/shared/themes/components.ts

@@ -2,7 +2,6 @@ import { Shadow, ThemeColorsOptions } from "@mui/material";
 import { Components } from "@mui/material/styles/components";
 import { Components } from "@mui/material/styles/components";
 import { TypographyOptions } from "@mui/material/styles/createTypography";
 import { TypographyOptions } from "@mui/material/styles/createTypography";
 
 
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
 export const getComponents = (
 export const getComponents = (
     colors: ThemeColorsOptions,
     colors: ThemeColorsOptions,
     typography: TypographyOptions,
     typography: TypographyOptions,

+ 6 - 1
web/yarn.lock

@@ -2290,6 +2290,11 @@ eslint-plugin-jsx-a11y@^6.7.1:
   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
   integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
   integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
 
 
+eslint-plugin-react-refresh@^0.4.6:
+  version "0.4.6"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz#e8e8accab681861baed00c5c12da70267db0936f"
+  integrity sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==
+
 eslint-plugin-react@^7.33.2:
 eslint-plugin-react@^7.33.2:
   version "7.33.2"
   version "7.33.2"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608"
@@ -3311,7 +3316,7 @@ libsodium-wrappers@0.7.9:
   dependencies:
   dependencies:
     libsodium "^0.7.0"
     libsodium "^0.7.0"
 
 
-libsodium@0.7.9, libsodium@^0.7.0:
+libsodium@^0.7.0:
   version "0.7.9"
   version "0.7.9"
   resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b"
   resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b"
   integrity sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==
   integrity sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==