From ee3357c3a744e422e26c33e8e75fc117301f4ccd Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 15:47:57 +0200 Subject: [PATCH 1/9] update eslint -> v10 with new config.mjs --- .eslintignore | 6 - eslint.config.mjs | 47 ++++ package-lock.json | 507 ++++++++++++++++++++++++++---------- package.json | 6 +- tools/patch-eslint-react.js | 31 +++ 5 files changed, 452 insertions(+), 145 deletions(-) delete mode 100644 .eslintignore create mode 100644 eslint.config.mjs create mode 100644 tools/patch-eslint-react.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index c5beec6..0000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -# Generated files -pkg/ -coverage/ - -# Config files -next.config.js diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..860f165 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,47 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import tsParser from "@typescript-eslint/parser"; +import nextConfig from "eslint-config-next/core-web-vitals"; + +export default defineConfig([ + globalIgnores(["**/pkg/", "**/coverage/", "**/next.config.js"]), + ...nextConfig, + // Override the Next.js Babel parser with @typescript-eslint/parser, which + // implements the scopeManager.addGlobals API required by ESLint 10. + { + files: ["**/*.{js,jsx,mjs,ts,tsx,mts,cts}"], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaFeatures: { jsx: true }, + }, + }, + }, + { + files: ["**/*.ts", "**/*.tsx"], + rules: { + "@typescript-eslint/explicit-function-return-type": "warn", + "@typescript-eslint/no-unused-vars": ["error", { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }], + }, + }, + { + files: ["**/*.{js,jsx,mjs,ts,tsx,mts,cts}"], + rules: { + "no-console": "warn", + "no-debugger": "warn", + "jsx-a11y/no-autofocus": "warn", + "react/jsx-no-target-blank": "warn", + }, + }, + { + files: ["**/*.test.ts", "**/*.test.tsx"], + rules: { + "no-unused-vars": ["error", { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }], + }, + }, +]); diff --git a/package-lock.json b/package-lock.json index 1a53c70..c721781 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "UrbanAnalyst", "version": "0.2.2", + "hasInstallScript": true, "license": "MIT", "dependencies": { "@emotion/cache": "^11.14.0", @@ -31,6 +32,8 @@ "wasm": "file:pkg" }, "devDependencies": { + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^10.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^14.6.1", @@ -38,6 +41,7 @@ "@types/node": "^25.5.2", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", + "@typescript-eslint/parser": "^8.61.0", "@vitejs/plugin-react": "^5.1.2", "@vitest/browser": "^4.1.8", "@vitest/coverage-v8": "^4.0.17", @@ -186,12 +190,12 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -277,9 +281,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -341,12 +345,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1578,6 +1582,119 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, "node_modules/@eslint/object-schema": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", @@ -4348,17 +4465,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", - "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz", + "integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", "debug": "^4.4.3" }, "engines": { @@ -4373,38 +4489,55 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz", + "integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@typescript-eslint/tsconfig-utils": "^8.61.0", + "@typescript-eslint/types": "^8.61.0", + "debug": "^4.4.3" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", - "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz", + "integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.3", - "@typescript-eslint/types": "^8.59.3", - "debug": "^4.4.3" + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz", + "integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" @@ -4413,22 +4546,99 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/project-service/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz", + "integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz", + "integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@typescript-eslint/project-service": "8.61.0", + "@typescript-eslint/tsconfig-utils": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz", + "integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", + "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.3", + "@typescript-eslint/types": "^8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { @@ -4491,24 +4701,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/types": { "version": "8.59.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", @@ -4551,24 +4743,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/utils": { "version": "8.59.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", @@ -5253,6 +5427,26 @@ "node": ">=8" } }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -5575,22 +5769,6 @@ "npm": ">=6" } }, - "node_modules/babel-plugin-styled-components": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", - "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "lodash": "^4.17.21", - "picomatch": "^2.3.1" - }, - "peerDependencies": { - "styled-components": ">= 2" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -6570,9 +6748,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -7149,24 +7327,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint-config-next/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", @@ -8836,6 +8996,29 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsdom": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz", @@ -9203,12 +9386,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -10102,6 +10279,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -10197,19 +10375,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/pretty-format/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -11383,6 +11548,19 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", @@ -11425,6 +11603,22 @@ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", "license": "MIT" }, + "node_modules/styled-components/node_modules/babel-plugin-styled-components": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.3.0.tgz", + "integrity": "sha512-nP/y6PbBqS/qtKROnJCgpGo8hYUzlBAVXN1QAjSBANL6vZiQXPQN7FYW/nUwoxY7nZhBEGm9T5tjL9gbzwulDw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "styled-components": ">= 2" + } + }, "node_modules/styled-components/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -11434,6 +11628,18 @@ "node": ">=4" } }, + "node_modules/styled-components/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/styled-components/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -11888,6 +12094,31 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", + "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/typewise": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", diff --git a/package.json b/package.json index bdd2a16..e067df5 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "test": "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", - "prepare": "husky" + "prepare": "husky", + "postinstall": "node tools/patch-eslint-react.js" }, "dependencies": { "@emotion/cache": "^11.14.0", @@ -44,6 +45,8 @@ "wasm": "file:pkg" }, "devDependencies": { + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^10.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^14.6.1", @@ -51,6 +54,7 @@ "@types/node": "^25.5.2", "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", + "@typescript-eslint/parser": "^8.61.0", "@vitejs/plugin-react": "^5.1.2", "@vitest/browser": "^4.1.8", "@vitest/coverage-v8": "^4.0.17", diff --git a/tools/patch-eslint-react.js b/tools/patch-eslint-react.js new file mode 100644 index 0000000..8613104 --- /dev/null +++ b/tools/patch-eslint-react.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node +// Patches eslint-plugin-react inside eslint-config-next for ESLint 10 compatibility. +// ESLint 10 removed context.getFilename() in favour of context.filename. +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const base = path.join(__dirname, '..', 'node_modules', 'eslint-config-next', 'node_modules', 'eslint-plugin-react', 'lib'); + +function patch(file, from, to) { + const full = path.join(base, file); + if (!fs.existsSync(full)) return; + const src = fs.readFileSync(full, 'utf8'); + if (src.includes(to)) return; // already patched + if (!src.includes(from)) return; // nothing to patch + fs.writeFileSync(full, src.replace(from, to)); + console.log(`patched ${file}`); +} + +patch( + 'util/version.js', + 'contextOrFilename.getFilename()', + 'contextOrFilename.filename ?? contextOrFilename.getFilename?.()' +); + +patch( + 'rules/jsx-filename-extension.js', + 'const filename = context.getFilename();', + 'const filename = context.filename ?? context.getFilename?.();' +); From 7ab96d5388f21398d983c267139660c31b9dc1e0 Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:02:24 +0200 Subject: [PATCH 2/9] update eslint.config.mjs config --- eslint.config.mjs | 2 ++ tools/patch-eslint-react.js | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 860f165..9c57a89 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -42,6 +42,8 @@ export default defineConfig([ argsIgnorePattern: "^_", varsIgnorePattern: "^_", }], + "@typescript-eslint/explicit-function-return-type": "off", + "@next/next/no-img-element": "off", }, }, ]); diff --git a/tools/patch-eslint-react.js b/tools/patch-eslint-react.js index 8613104..a47e953 100644 --- a/tools/patch-eslint-react.js +++ b/tools/patch-eslint-react.js @@ -15,7 +15,6 @@ function patch(file, from, to) { if (src.includes(to)) return; // already patched if (!src.includes(from)) return; // nothing to patch fs.writeFileSync(full, src.replace(from, to)); - console.log(`patched ${file}`); } patch( From dd729a1ffba58346293cc6eb1a650fc025cb5207 Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:08:45 +0200 Subject: [PATCH 3/9] disable lints on 2 lines --- src/components/appBar.tsx | 8 +++----- src/components/windowSize.tsx | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/appBar.tsx b/src/components/appBar.tsx index d8b99e7..22f25e0 100644 --- a/src/components/appBar.tsx +++ b/src/components/appBar.tsx @@ -74,13 +74,11 @@ const MIN_MENU_WIDTH = 600; export default function ButtonAppBar(props: ButtonAppProps): JSX.Element { - const [theme, setTheme] = useState(lightTheme); + const [theme] = useState(() => getPreferredTheme()); const [mounted, setMounted] = useState(false); - useEffect(() => { - setMounted(true); - setTheme(getPreferredTheme()); - }, []); + // eslint-disable-next-line react-hooks/set-state-in-effect + useEffect(() => { setMounted(true); }, []); const buttonInputs = props.text.map((i) => ({ key: capitaliseFirst(i), diff --git a/src/components/windowSize.tsx b/src/components/windowSize.tsx index f79256e..ff9daa0 100644 --- a/src/components/windowSize.tsx +++ b/src/components/windowSize.tsx @@ -7,12 +7,14 @@ export default function useWindowSize(): { width: number | null; height: number height: null }); + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }, []); + /* eslint-enable react-hooks/set-state-in-effect */ useEffect(() => { function handleResize(): void { From 7185c49650f4f0110b1856c07b7bc933af6d2913 Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:10:35 +0200 Subject: [PATCH 4/9] lint utils/*.tsx --- src/components/utils/mapTourUtils.tsx | 38 +++++---------------- src/components/utils/tourUtils.tsx | 33 +++++------------- src/components/utils/transformTourUtils.tsx | 38 +++++---------------- 3 files changed, 27 insertions(+), 82 deletions(-) diff --git a/src/components/utils/mapTourUtils.tsx b/src/components/utils/mapTourUtils.tsx index 8845e72..60ba366 100644 --- a/src/components/utils/mapTourUtils.tsx +++ b/src/components/utils/mapTourUtils.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import useWindowSize from '@/components/windowSize'; import getPreferredTourClass from '@/components/tourClass'; import { getTourConfig } from '@/components/map/tour/tourConfig'; @@ -19,49 +19,29 @@ export function useMapTourLogic(): { }; handleTourOpen: () => void; } { - const [tourClass, setTourClass] = useState(tourStyles.tourhelperLight); - const [width, setWidth] = useState(0); - const [height, setHeight] = useState(0); + const [tourClass] = useState(() => + typeof window !== 'undefined' ? getPreferredTourClass() : tourStyles.tourhelperLight + ); const size = useWindowSize(); - const [isTourOpen, setTourOpen] = useState(false); + const width = size?.width || 0; + const height = size?.height || 0; + const [isTourOpen, setTourOpen] = useState(() => + typeof window !== 'undefined' && !sessionStorageHelpers.getItem('uamaptour') + ); const accentColor = "#5cb7b7"; - // Initialize tour class - useEffect(() => { - if (typeof window !== 'undefined') { - setTourClass(getPreferredTourClass()); - } - }, []); - - // Update window size - useEffect(() => { - const w = size?.width || 0; - setWidth(w); - const h = size?.height || 0; - setHeight(h); - }, [size]); - - // Tour configuration const tourConfig = useMemo(() => getTourConfig(width, height), [width, height]); const handleTourOpen = (): void => { setTourOpen(true); }; - // Use sessionStorage to only show tour once per session. const closeTour = (): void => { setTourOpen(false); sessionStorageHelpers.setItem("uamaptour", "done"); }; - // Auto-show tour for new sessions - useEffect(() => { - if(!sessionStorageHelpers.getItem('uamaptour')) { - setTourOpen(true) - } - }, []); - return { tourProps: { onRequestClose: closeTour, diff --git a/src/components/utils/tourUtils.tsx b/src/components/utils/tourUtils.tsx index b185fcd..efb7735 100644 --- a/src/components/utils/tourUtils.tsx +++ b/src/components/utils/tourUtils.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import useWindowSize from '@/components/windowSize'; import getPreferredTourClass from '@/components/tourClass'; import { getTourConfig } from '@/components/compare/tour/tourConfig'; @@ -18,27 +18,18 @@ export function useTourLogic(): { }; handleTourOpen: () => void; } { - const [tourClass, setTourClass] = useState(tourStyles.tourhelperLight); - const [width, setWidth] = useState(0); - const [height, setHeight] = useState(0); + const [tourClass] = useState(() => + typeof window !== 'undefined' ? getPreferredTourClass() : tourStyles.tourhelperLight + ); const size = useWindowSize(); - const [isTourOpen, setTourOpen] = useState(false); + const width = size?.width || 0; + const height = size?.height || 0; + const [isTourOpen, setTourOpen] = useState(() => + typeof window !== 'undefined' && !sessionStorageHelpers.getItem('uacomparetour') + ); const accentColor = "#5cb7b7"; - useEffect(() => { - if (typeof window !== 'undefined') { - setTourClass(getPreferredTourClass()); - } - }, []); - - useEffect(() => { - const w = size?.width || 0; - setWidth(w); - const h = size?.height || 0; - setHeight(h); - }, [size]); - const tourConfig = useMemo(() => getTourConfig(width, height), [width, height]); const handleTourOpen = (): void => { @@ -50,12 +41,6 @@ export function useTourLogic(): { sessionStorageHelpers.setItem("uacomparetour", "done"); }; - useEffect(() => { - if(!sessionStorageHelpers.getItem('uacomparetour')) { - setTourOpen(true) - } - }, []); - return { tourProps: { onRequestClose: closeTour, diff --git a/src/components/utils/transformTourUtils.tsx b/src/components/utils/transformTourUtils.tsx index e499408..7d3c623 100644 --- a/src/components/utils/transformTourUtils.tsx +++ b/src/components/utils/transformTourUtils.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import useWindowSize from '@/components/windowSize'; import getPreferredTourClass from '@/components/tourClass'; import { getTourConfig } from '@/components/transform/tour/tourConfig'; @@ -19,49 +19,29 @@ export function useTransformTourLogic(): { }; handleTourOpen: () => void; } { - const [tourClass, setTourClass] = useState(tourStyles.tourhelperLight); - const [width, setWidth] = useState(0); - const [height, setHeight] = useState(0); + const [tourClass] = useState(() => + typeof window !== 'undefined' ? getPreferredTourClass() : tourStyles.tourhelperLight + ); const size = useWindowSize(); - const [isTourOpen, setTourOpen] = useState(false); + const width = size?.width || 0; + const height = size?.height || 0; + const [isTourOpen, setTourOpen] = useState(() => + typeof window !== 'undefined' && !sessionStorageHelpers.getItem('uatransformtour') + ); const accentColor = "#5cb7b7"; - // Initialize tour class - useEffect(() => { - if (typeof window !== 'undefined') { - setTourClass(getPreferredTourClass()); - } - }, []); - - // Update window size - useEffect(() => { - const w = size?.width || 0; - setWidth(w); - const h = size?.height || 0; - setHeight(h); - }, [size]); - - // Tour configuration const tourConfig = useMemo(() => getTourConfig(width, height), [width, height]); const handleTourOpen = (): void => { setTourOpen(true); }; - // Use sessionStorage to only show tour once per session. const closeTour = (): void => { setTourOpen(false); sessionStorageHelpers.setItem("uatransformtour", "done"); }; - // Auto-show tour for new sessions - useEffect(() => { - if(!sessionStorageHelpers.getItem('uatransformtour')) { - setTourOpen(true) - } - }, []); - return { tourProps: { onRequestClose: closeTour, From 72fff87f25c8966193562270f8f918429630cc8e Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:11:15 +0200 Subject: [PATCH 5/9] lint summarise/*.tsx --- src/components/summarise/cityList.tsx | 15 ++------------- src/components/summarise/control.tsx | 19 ++++--------------- src/components/summarise/summarisePage.tsx | 21 +++++++-------------- 3 files changed, 13 insertions(+), 42 deletions(-) diff --git a/src/components/summarise/cityList.tsx b/src/components/summarise/cityList.tsx index 8f2ad27..a6e3557 100644 --- a/src/components/summarise/cityList.tsx +++ b/src/components/summarise/cityList.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback } from 'react'; // import Select from 'react-select'; import Box from '@mui/material/Box'; @@ -33,22 +33,11 @@ export default function CityList(props: CityListProps): JSX.Element { } return op; }, [options, props.idx]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - - // This is necessary to ensure localStorage values are correctly set on initial load: - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [findMatchingOption, options]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { const val = event.target.value as string; if (val) { - setSelectedOption(val); const matchingIdx: number = options.find(option => option.value === val)?.index ?? 0; props.handleIdxChange(matchingIdx); } diff --git a/src/components/summarise/control.tsx b/src/components/summarise/control.tsx index b6e54c8..6bd8ea3 100644 --- a/src/components/summarise/control.tsx +++ b/src/components/summarise/control.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import Image from "next/image" import localFont from 'next/font/local' @@ -22,20 +22,9 @@ export default function Control (props: SummariseControlProps): JSX.Element { const handleControlsVisibility = (pHideControls: boolean): void => { setHideControls(pHideControls); } - const [controlStyle, setControStyle] = useState(styles.light); - useEffect(() => { - if (typeof window !== 'undefined') { - const dark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setControStyle(dark ? styles.dark : styles.light); - } - }, []); - const [uaLogo, setUaLogo] = useState('/ua.svg'); - useEffect(() => { - if (typeof window !== 'undefined') { - const dark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setUaLogo(dark ? '/ua-dark.svg' : 'ua.svg'); - } - }, []); + const isDark = typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches; + const [controlStyle] = useState(isDark ? styles.dark : styles.light); + const [uaLogo] = useState(isDark ? '/ua-dark.svg' : 'ua.svg'); return ( <> diff --git a/src/components/summarise/summarisePage.tsx b/src/components/summarise/summarisePage.tsx index 7e16d04..5c8c189 100644 --- a/src/components/summarise/summarisePage.tsx +++ b/src/components/summarise/summarisePage.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react"; +import { useState } from "react"; import Control from '@/components/summarise/control'; import styles from '@/styles/summarise.module.css'; @@ -9,19 +9,12 @@ import { localStorageHelpers } from '@/components/utils/localStorageUtils'; export default function SummarisePage(): JSX.Element { - const [idx, setIdx] = useState(0); - - useEffect(() => { - var idxLocal = 0; - const storedIdx = localStorageHelpers.getItem('uaCityIdx'); - if(storedIdx) { - idxLocal = parseInt(storedIdx, 10); - if (isNaN(idxLocal)) { - idxLocal = 0; - } - } - setIdx(idxLocal); - }, []) + const [idx, setIdx] = useState(() => { + const stored = localStorageHelpers.getItem('uaCityIdx'); + if (!stored) return 0; + const parsed = parseInt(stored, 10); + return isNaN(parsed) ? 0 : parsed; + }); const contentArray = Content(); From 76853690b81e2feb392765adfb5294bdf0c9ce55 Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:11:52 +0200 Subject: [PATCH 6/9] lint compare/*.tsx --- src/components/compare/control.tsx | 19 ++++--------------- src/components/compare/layerlist.tsx | 14 ++------------ src/components/compare/meanVar.tsx | 11 ++--------- src/components/compare/sortOrderList.tsx | 14 ++------------ 4 files changed, 10 insertions(+), 48 deletions(-) diff --git a/src/components/compare/control.tsx b/src/components/compare/control.tsx index 8a4050e..3bbca12 100644 --- a/src/components/compare/control.tsx +++ b/src/components/compare/control.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import Image from "next/image" import localFont from 'next/font/local' @@ -57,20 +57,9 @@ export default function Control(props: CompareControlProps): JSX.Element { const handleControlsVisibility = (pHideControls: boolean): void => { setHideControls(pHideControls); } - const [controlStyle, setControStyle] = useState(styles.light); - useEffect(() => { - if (typeof window !== 'undefined') { - const dark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setControStyle(dark ? styles.dark : styles.light); - } - }, []); - const [uaLogo, setUaLogo] = useState('/ua.svg'); - useEffect(() => { - if (typeof window !== 'undefined') { - const dark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setUaLogo(dark ? '/ua-dark.svg' : 'ua.svg'); - } - }, []); + const isDark = typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches; + const [controlStyle] = useState(isDark ? styles.dark : styles.light); + const [uaLogo] = useState(isDark ? '/ua-dark.svg' : 'ua.svg'); const IdxWashington = getIdxWashington(props.citiesArray); diff --git a/src/components/compare/layerlist.tsx b/src/components/compare/layerlist.tsx index 7c28f88..5b79633 100644 --- a/src/components/compare/layerlist.tsx +++ b/src/components/compare/layerlist.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -52,20 +52,10 @@ export default function LayerList(props: LayerListProps): JSX.Element { return op; }, [reducedOptions, props.layer]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption("transport"); - } - }, [props.layer, findMatchingOption, reducedOptions]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { props.handleLayerChange(event.target.value as DataRangeKeys); - setSelectedOption(event.target.value as DataRangeKeys); }; const width = useWindowSize().width; diff --git a/src/components/compare/meanVar.tsx b/src/components/compare/meanVar.tsx index 332a2e5..24a07c1 100644 --- a/src/components/compare/meanVar.tsx +++ b/src/components/compare/meanVar.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useEffect, useState } from 'react'; +import { ChangeEvent } from 'react'; import Radio from '@mui/material/Radio'; import RadioGroup from '@mui/material/RadioGroup'; @@ -13,15 +13,8 @@ interface meanVarProps { export default function MeanVarButtons(props: meanVarProps): JSX.Element { - const [mean, setMean] = useState(true); - useEffect(() => { - const meanState = props.meanVals || !props.singleLayer; - setMean(meanState); - }, [props.meanVals, props.singleLayer]); - const handleChange = (event: ChangeEvent): void => { - setMean((event.target as HTMLInputElement).value === "average"); - props.handleMeanChange(mean); + props.handleMeanChange((event.target as HTMLInputElement).value === "average"); } return ( diff --git a/src/components/compare/sortOrderList.tsx b/src/components/compare/sortOrderList.tsx index a3e08bd..8011185 100644 --- a/src/components/compare/sortOrderList.tsx +++ b/src/components/compare/sortOrderList.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -33,22 +33,12 @@ export default function SortOrderList(props: SortOrderListProps): JSX.Element { return op; }, [options, props.sortOpt]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { - setSelectedOption(event.target.value as string); props.handleSortChange(event.target.value as string); }; - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [props.sortOpt, findMatchingOption, options]); - const width = useWindowSize().width; const sizeString = width == null ? "medium" : (width < 700 ? "small" : "medium"); From d4c6cdd230f904d57507ec66a41e11215259b5e8 Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:12:17 +0200 Subject: [PATCH 7/9] lint map/*.tsx --- src/components/map/cityList.tsx | 15 ++------------- src/components/map/control.tsx | 16 ++++++---------- src/components/map/layerList.tsx | 13 ++----------- src/components/map/layerList2.tsx | 13 ++----------- src/components/map/map.tsx | 8 ++------ src/components/map/mapPage.tsx | 4 ++++ 6 files changed, 18 insertions(+), 51 deletions(-) diff --git a/src/components/map/cityList.tsx b/src/components/map/cityList.tsx index 58e971a..67efc8f 100644 --- a/src/components/map/cityList.tsx +++ b/src/components/map/cityList.tsx @@ -1,5 +1,5 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -35,22 +35,11 @@ export default function CityList(props: CityListProps): JSX.Element { } return op; }, [options, props.idx]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - - // This is necessary to ensure localStorage values are correctly set on initial load: - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [findMatchingOption, options]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { const val = event.target.value as string; if (val) { - setSelectedOption(val); const opInt = parseInt(val); props.handleIdxChange(opInt); props.handleViewStateChange({...props.citiesArray[opInt].initialViewState, diff --git a/src/components/map/control.tsx b/src/components/map/control.tsx index 10461d6..72bbd6a 100644 --- a/src/components/map/control.tsx +++ b/src/components/map/control.tsx @@ -58,18 +58,14 @@ export default function Control (props: MapControlProps): JSX.Element { const handleControlsVisibility = (pHideControls: boolean): void => { setHideControls(pHideControls); } - const [uaLogo, setUaLogo] = useState('/ua.svg'); - useEffect(() => { - if (typeof window !== 'undefined') { - const dark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setUaLogo(dark ? '/ua-dark.svg' : 'ua.svg'); - } - }, []); + const [uaLogo] = useState( + typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches + ? '/ua-dark.svg' : 'ua.svg' + ); const [sliderValues, setSliderValues] = useState(props.layerRange); - useEffect(() => { - setSliderValues(props.layerRange); - }, [props.layerRange]); + // eslint-disable-next-line react-hooks/set-state-in-effect + useEffect(() => { setSliderValues(props.layerRange); }, [props.layerRange]); var step = Math.floor(props.layerStartStop[1] - props.layerStartStop[0]) / 20; var multiplier = 10; diff --git a/src/components/map/layerList.tsx b/src/components/map/layerList.tsx index 7c60362..f139615 100644 --- a/src/components/map/layerList.tsx +++ b/src/components/map/layerList.tsx @@ -1,5 +1,5 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -55,19 +55,10 @@ export default function LayerList(props: LayerListProps): JSX.Element { return op; }, [options, props.layer]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [props.layer, findMatchingOption, options]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { props.handleLayerChange(event.target.value as DataRangeKeys); - setSelectedOption(event.target.value as DataRangeKeys); }; const width = useWindowSize().width; diff --git a/src/components/map/layerList2.tsx b/src/components/map/layerList2.tsx index b1d582a..730e959 100644 --- a/src/components/map/layerList2.tsx +++ b/src/components/map/layerList2.tsx @@ -1,5 +1,5 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -57,19 +57,10 @@ export default function LayerList2(props: LayerListProps): JSX.Element { return op; }, [options, props.layer2]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [props.layer2, findMatchingOption, options]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { props.handleLayerChange(event.target.value as DataRangeKeys); - setSelectedOption(event.target.value as DataRangeKeys); }; const width = useWindowSize().width; diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index e6f57b8..8165635 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, Suspense } from "react"; +import { Suspense } from "react"; import { DeckGL } from "@deck.gl/react/typed"; import { Map } from "react-map-gl"; @@ -21,11 +21,7 @@ const MAP_STYLE = "mapbox://styles/mapbox/light-v10" */ export default function UTAMap(props: MapProps): JSX.Element { - const this_layer = mapLayer(props); - const [layer, setLayer] = useState(this_layer); - useEffect(() => { - setLayer(mapLayer(props)); - }, [props]); + const layer = mapLayer(props); return ( diff --git a/src/components/map/mapPage.tsx b/src/components/map/mapPage.tsx index 96ef260..950fd61 100644 --- a/src/components/map/mapPage.tsx +++ b/src/components/map/mapPage.tsx @@ -190,6 +190,7 @@ export default function MapPage(): JSX.Element { const [layerStartStop, setLayerStartStop] = useState<[number, number]>(LAYER_CONSTANTS.DEFAULT_LAYER_START_STOP); const [layerRange, setLayerRange] = useState<[number, number]>(LAYER_CONSTANTS.DEFAULT_LAYER_RANGE); + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { const initialState = loadInitialState(); const { @@ -233,7 +234,9 @@ export default function MapPage(): JSX.Element { setLayerRange([rangeData.layer_min, rangeData.layer_max]); setLayerStartStop([rangeData.layer_start, rangeData.layer_stop]); }, []) + /* eslint-enable react-hooks/set-state-in-effect */ + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { if (cityLayers.length > 0) { const rangeData = calculateLayerRanges( @@ -248,6 +251,7 @@ export default function MapPage(): JSX.Element { setLayerStartStop([rangeData.layer_start, rangeData.layer_stop]); } }, [idx, layer, layer2, numLayers, cityLayers.length]) + /* eslint-enable react-hooks/set-state-in-effect */ const createViewStateForCity = useCallback((cityIdx: number) => ({ ...CITY_DATA.citiesArray[cityIdx].initialViewState, From d134de418c8e2f2bf7759b86c2e6d9795e95ecff Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:12:51 +0200 Subject: [PATCH 8/9] lint transform/*.tsx --- src/components/transform/cityList.tsx | 15 ++------------- src/components/transform/control.tsx | 16 ++++++---------- src/components/transform/layerList.tsx | 11 +++-------- src/components/transform/targetCityList.tsx | 15 ++------------- src/components/transform/transformComponent.tsx | 4 ++++ src/components/transform/transformPage.tsx | 4 ++++ 6 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/components/transform/cityList.tsx b/src/components/transform/cityList.tsx index 480e58e..da32785 100644 --- a/src/components/transform/cityList.tsx +++ b/src/components/transform/cityList.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -34,22 +34,11 @@ export default function CityList(props: CityListProps): JSX.Element { } return op; }, [options, props.idx]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - - // This is necessary to ensure localStorage values are correctly set on initial load: - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [findMatchingOption, options]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { const val = event.target.value as string; if (val) { - setSelectedOption(val); const opInt = parseInt(val); props.handleIdxChange(opInt); props.handleViewStateChange({...props.citiesArray[opInt].initialViewState, diff --git a/src/components/transform/control.tsx b/src/components/transform/control.tsx index d016c78..1ddd57a 100644 --- a/src/components/transform/control.tsx +++ b/src/components/transform/control.tsx @@ -117,13 +117,10 @@ export default function Control (props: TransformControlProps): JSX.Element { const handleControlsVisibility = (pHideControls: boolean): void => { setHideControls(pHideControls); } - const [uaLogo, setUaLogo] = useState('/ua.svg'); - useEffect(() => { - if (typeof window !== 'undefined') { - const dark = window.matchMedia('(prefers-color-scheme: dark)').matches; - setUaLogo(dark ? '/ua-dark.svg' : 'ua.svg'); - } - }, []); + const [uaLogo] = useState( + typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches + ? '/ua-dark.svg' : 'ua.svg' + ); const [_defaultVarnames, setDefaultVarnames] = useState(props.varnames); @@ -151,9 +148,8 @@ export default function Control (props: TransformControlProps): JSX.Element { // Layer range sliders: const [sliderValues, setSliderValues] = useState(props.layerRange); - useEffect(() => { - setSliderValues(props.layerRange); - }, [props.layerRange]); + // eslint-disable-next-line react-hooks/set-state-in-effect + useEffect(() => { setSliderValues(props.layerRange); }, [props.layerRange]); var layerRangeStep = Math.floor(props.layerStartStop[1] - props.layerStartStop[0]) / 20; var multiplier = 10; diff --git a/src/components/transform/layerList.tsx b/src/components/transform/layerList.tsx index abea288..82a1928 100644 --- a/src/components/transform/layerList.tsx +++ b/src/components/transform/layerList.tsx @@ -1,5 +1,5 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -60,22 +60,17 @@ export default function LayerList(props: LayerListProps): JSX.Element { return op; }, [options, props.layer]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); + const selectedOption = findMatchingOption(); const { idx, idx2, citiesArray, setVarnames } = props; useEffect(() => { const this_option = findMatchingOption(); if (this_option) { - setSelectedOption(this_option); - const _layer = this_option; - setVarnames(DefaultExtraLayers({ idx, idx2, layer: _layer, citiesArray })); - } else { - setSelectedOption(options[0].value); + setVarnames(DefaultExtraLayers({ idx, idx2, layer: this_option, citiesArray })); } }, [idx, idx2, citiesArray, setVarnames, findMatchingOption, options]); const handleChange = (event: SelectChangeEvent): void => { props.handleLayerChange(event.target.value as DataRangeKeys); - setSelectedOption(event.target.value as DataRangeKeys); }; const width = useWindowSize().width; diff --git a/src/components/transform/targetCityList.tsx b/src/components/transform/targetCityList.tsx index 4c3e13a..2548f40 100644 --- a/src/components/transform/targetCityList.tsx +++ b/src/components/transform/targetCityList.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback } from 'react'; import Box from '@mui/material/Box'; import InputLabel from '@mui/material/InputLabel'; @@ -33,22 +33,11 @@ export default function TargetCityList(props: CityListProps): JSX.Element { } return op; }, [options, props.idx2]); - const [selectedOption, setSelectedOption] = useState(findMatchingOption()); - - // This is necessary to ensure localStorage values are correctly set on initial load: - useEffect(() => { - const this_option = findMatchingOption(); - if (this_option) { - setSelectedOption(this_option); - } else { - setSelectedOption(options[0].value); - } - }, [findMatchingOption, options]); + const selectedOption = findMatchingOption(); const handleChange = (event: SelectChangeEvent): void => { const val = event.target.value as string; if (val) { - setSelectedOption(val); const opInt = parseInt(val); props.handleIdx2Change(opInt); } diff --git a/src/components/transform/transformComponent.tsx b/src/components/transform/transformComponent.tsx index f883e72..18f1751 100644 --- a/src/components/transform/transformComponent.tsx +++ b/src/components/transform/transformComponent.tsx @@ -111,11 +111,13 @@ const TransformComponent = (props: TransformProps): JSX.Element => { /** * ------ Effect #1 ------ */ + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { const mapPathSource = "/api/ghAggregateData?city=" + props.city + "&type=data"; setMapPathSource(mapPathSource); setData1(null); }, [props.city, setData1]); + /* eslint-enable react-hooks/set-state-in-effect */ /** * Effect to load 'dataraw' point-based data for source and target cities. @@ -244,12 +246,14 @@ const TransformComponent = (props: TransformProps): JSX.Element => { /** * ------ Effect #9 ------ */ + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { if (geoJSONcontent !== null) { const this_layer = getGeoJsonLayer(geoJSONcontent, props.layerRange, layer, props.alpha); setGeoJsonLayer(this_layer); } }, [props.layerRange, layer, props.alpha, geoJSONcontent]); + /* eslint-enable react-hooks/set-state-in-effect */ return ( <> diff --git a/src/components/transform/transformPage.tsx b/src/components/transform/transformPage.tsx index d06353e..4d4d2ce 100644 --- a/src/components/transform/transformPage.tsx +++ b/src/components/transform/transformPage.tsx @@ -168,6 +168,7 @@ export default function TransformPage(): JSX.Element { const [outputLayer, setOutputLayer] = useState(OUTPUT_LAYER_TYPES.RELATIVE); const [cityLayers, setCityLayers] = useState([]); + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { var idxLocal: number = LAYER_CONSTANTS.DEFAULT_CITY_INDEX; var idx2Local: number = LAYER_CONSTANTS.DEFAULT_TARGET_CITY_INDEX; @@ -220,7 +221,9 @@ export default function TransformPage(): JSX.Element { localStorageHelpers.removeItem('uaLayer'); } }, []) + /* eslint-enable react-hooks/set-state-in-effect */ + /* eslint-disable react-hooks/set-state-in-effect */ useEffect(() => { const layer_start = CITY_DATA.citiesArray[idx].dataRanges[layer as DataRangeKeys][0]; const layer_min = CITY_DATA.citiesArray[idx].dataRanges[layer as DataRangeKeys][1]; @@ -230,6 +233,7 @@ export default function TransformPage(): JSX.Element { setLayerRange([layer_min, layer_max]); setLayerStartStop([layer_start, layer_stop]); }, [idx, layer]); + /* eslint-enable react-hooks/set-state-in-effect */ const createViewStateForCity = useCallback((cityIdx: number) => ({ ...CITY_DATA.citiesArray[cityIdx].initialViewState, From 0d7569e4eb55eca028efbb30f8ae59e0a81d27fc Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 15 Jun 2026 16:15:30 +0200 Subject: [PATCH 9/9] update eslint -> latest 10.5.0 --- package-lock.json | 27 +++++++++++++++------------ package.json | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index c721781..0de8178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "@vitest/coverage-v8": "^4.0.17", "@vitest/ui": "^4.0.17", "autoprefixer": "^10.4.27", - "eslint": "^10.3.0", + "eslint": "^10.5.0", "eslint-config-next": "^16.2.6", "husky": "^9.1.7", "jsdom": "^29.0.2", @@ -1557,9 +1557,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1706,9 +1706,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7233,19 +7233,22 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", + "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, "license": "MIT", "peer": true, + "workspaces": [ + "packages/*" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", + "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", diff --git a/package.json b/package.json index e067df5..75eca73 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@vitest/coverage-v8": "^4.0.17", "@vitest/ui": "^4.0.17", "autoprefixer": "^10.4.27", - "eslint": "^10.3.0", + "eslint": "^10.5.0", "eslint-config-next": "^16.2.6", "husky": "^9.1.7", "jsdom": "^29.0.2",