From 76ef9940673742da02c73c684da1bcc26088eeca Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Wed, 15 Jul 2026 18:31:13 +0000 Subject: [PATCH] fix: Implement unicorn eslint rules and fix them --- eslint.config.js | 24 +- package-lock.json | 431 +++++++++++++++++- package.json | 1 + .../scrobblers/AbstractScrobbleClient.ts | 22 +- src/backend/utils/FSUtils.ts | 22 +- src/client/components/ActivityDetail.tsx | 5 +- src/client/components/ActivityTimeline.tsx | 65 ++- .../msComponent/MSComponentDetailed.tsx | 12 +- .../components/playActivity/ActivityList.tsx | 4 +- 9 files changed, 497 insertions(+), 89 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 082d2619..a4c0ca7c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -13,6 +13,7 @@ import tsEslint from 'typescript-eslint'; import arrow from 'eslint-plugin-prefer-arrow-functions'; import hooks from 'eslint-plugin-react-hooks'; import mochaPlugin from 'eslint-plugin-mocha'; +import unicorn from 'eslint-plugin-unicorn'; const defaultRules = { 'no-useless-catch': 'off', @@ -70,7 +71,8 @@ export default defineConfig([ plugins: { "prefer-arrow-functions": arrow, js, - 'unused-imports': unusedImports + 'unused-imports': unusedImports, + unicorn, }, rules: { ...defaultRules, @@ -104,6 +106,26 @@ export default defineConfig([ // "import/consistent-type-specifier-style": [ // "error", "prefer-top-level-if-only-type-imports" // ] + "unicorn/prefer-then-catch": "error", + "unicorn/consistent-destructuring": "warn", + "unicorn/consistent-function-scoping": "warn", + "unicorn/consistent-optional-chaining": "error", + "unicorn/no-array-callback-reference": "warn", + "unicorn/no-accidental-bitwise-operator": "warn", + "no-obj-calls": "error", + "unicorn/new-for-builtins": "error", + "unicorn/no-impossible-length-comparison": "error", + "unicorn/no-duplicate-loops": "warn", + "unicorn/no-duplicate-logical-operands": "warn", + "unicorn/no-declarations-before-early-exit": "warn", + "unicorn/prefer-negative-index": "warn", + "unicorn/prefer-import-meta-properties": "error", + "unicorn/prefer-array-from-async": "warn", + "unicorn/prefer-array-flat-map": "warn", + "unicorn/no-useless-else": "warn", + "unicorn/no-unused-array-method-return": "error", + //"unicorn/no-unreadable-object-destructuring": "warn", + "unicorn/prefer-object-destructuring-defaults": "warn" }, extends: [ tsEslint.configs.recommended, diff --git a/package-lock.json b/package-lock.json index efab95d0..77c5f682 100644 --- a/package-lock.json +++ b/package-lock.json @@ -175,6 +175,7 @@ "eslint-plugin-prefer-arrow-functions": "^3.2.4", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-storybook": "^10.3.6", + "eslint-plugin-unicorn": "^72.0.0", "eslint-plugin-unused-imports": "^4.4.1", "git-cliff": "^2.12.0", "globals": "^17.7.0", @@ -1508,6 +1509,20 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@eslint/css-tree": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-4.0.4.tgz", + "integrity": "sha512-nxMparyhqVWQvadx9x8dIfubfIPOE+X2b2waua8fzdnM9vdp9rgVtwEZlG0TmCwEUz/d/f40fzvO/eqBwdxz0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.28.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, "node_modules/@eslint/js": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", @@ -8134,9 +8149,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.33", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", - "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8240,9 +8255,9 @@ "license": "ISC" }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", "dev": true, "funding": [ { @@ -8260,10 +8275,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -8309,6 +8324,19 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/builtin-modules": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.3.0.tgz", + "integrity": "sha512-hMQUl2bUFG339QygPM97E+mc8OY1IAchORZxm4a/frcYwKzozMzRVDBwHW0NjOqGElLm2O37AVQE8ikxlZHrMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -8494,9 +8522,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "version": "1.0.30001805", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", + "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==", "dev": true, "funding": [ { @@ -8601,6 +8629,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/character-entities-html4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", @@ -8921,6 +8956,19 @@ "node": ">= 0.6" } }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -8952,6 +9000,20 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "license": "MIT" }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", @@ -9272,6 +9334,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -10147,9 +10222,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.366", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.366.tgz", - "integrity": "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg==", + "version": "1.5.392", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.392.tgz", + "integrity": "sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==", "dev": true, "license": "ISC" }, @@ -10202,6 +10277,19 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -10897,6 +10985,73 @@ "typescript": ">=4.8.4" } }, + "node_modules/eslint-plugin-unicorn": { + "version": "72.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-72.0.0.tgz", + "integrity": "sha512-hqO6ksoOHO+ZhdseTuKRVQbx9U7PRO/cv8qAR1mctwzdVO2hYud8uS9luAhp43RJgziYgHAph8eHyipT8GL0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@eslint/css-tree": "^4.0.4", + "browserslist": "^4.28.4", + "change-case": "^5.4.4", + "ci-info": "^4.4.0", + "core-js-compat": "^3.49.0", + "detect-indent": "^7.0.2", + "entities": "^4.5.0", + "find-up-simple": "^1.0.1", + "globals": "^17.7.0", + "indent-string": "^5.0.0", + "is-builtin-module": "^5.0.0", + "is-identifier": "^1.1.0", + "pluralize": "^8.0.0", + "quote-js-string": "^0.1.0", + "regjsparser": "^0.13.2", + "reserved-identifiers": "^1.2.0", + "semver": "^7.8.5", + "strip-indent": "^4.1.1", + "yaml": "^2.9.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=10.4" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-unused-imports": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", @@ -11531,6 +11686,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/find-yarn-workspace-root": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", @@ -11743,6 +11911,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gaxios": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", @@ -12558,6 +12739,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/identifier-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/identifier-regex/-/identifier-regex-1.1.0.tgz", + "integrity": "sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==", + "dev": true, + "license": "MIT", + "dependencies": { + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -12708,6 +12905,22 @@ "node": ">=8" } }, + "node_modules/is-builtin-module": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", + "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^5.0.0" + }, + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-bun-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", @@ -12791,6 +13004,23 @@ "node": ">=0.10.0" } }, + "node_modules/is-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-identifier/-/is-identifier-1.1.0.tgz", + "integrity": "sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "identifier-regex": "^1.1.0", + "super-regex": "^1.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -13875,6 +14105,66 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/make-asynchronous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/make-asynchronous/-/make-asynchronous-1.1.0.tgz", + "integrity": "sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-event": "^6.0.0", + "type-fest": "^4.6.0", + "web-worker": "^1.5.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-asynchronous/node_modules/p-event": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz", + "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-asynchronous/node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-asynchronous/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -13905,6 +14195,13 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdn-data": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.28.1.tgz", + "integrity": "sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -14680,9 +14977,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.47", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", - "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, "license": "MIT", "engines": { @@ -15569,6 +15866,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", @@ -15882,6 +16189,19 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/quote-js-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/quote-js-string/-/quote-js-string-0.1.0.tgz", + "integrity": "sha512-Y3NoRtprEEZQD8RfxMCfS0ZTqc4e+i18OrXEXAvpM6TfC/3y+0L5rNbZiSnbBBEkDfFzbpd8o+cE8q3/anjMGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/quote-js-string?sponsor=1" + } + }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -16315,6 +16635,19 @@ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", "license": "MIT" }, + "node_modules/regjsparser": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -16341,6 +16674,19 @@ "dev": true, "license": "MIT" }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -16657,9 +17003,9 @@ "license": "BSD-3-Clause" }, "node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -17939,6 +18285,24 @@ "dev": true, "license": "MIT" }, + "node_modules/super-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.1.0.tgz", + "integrity": "sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-timeout": "^1.0.1", + "make-asynchronous": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/superagent": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", @@ -18058,6 +18422,22 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -19357,6 +19737,13 @@ "phin": "^3.6.1" } }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index dd763c19..2d04c74a 100644 --- a/package.json +++ b/package.json @@ -215,6 +215,7 @@ "eslint-plugin-prefer-arrow-functions": "^3.2.4", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-storybook": "^10.3.6", + "eslint-plugin-unicorn": "^72.0.0", "eslint-plugin-unused-imports": "^4.4.1", "git-cliff": "^2.12.0", "globals": "^17.7.0", diff --git a/src/backend/scrobblers/AbstractScrobbleClient.ts b/src/backend/scrobblers/AbstractScrobbleClient.ts index a70604e1..d53e9421 100644 --- a/src/backend/scrobblers/AbstractScrobbleClient.ts +++ b/src/backend/scrobblers/AbstractScrobbleClient.ts @@ -1011,14 +1011,14 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i } = {}, } = this.config; - // can't have negative retries! - const maxRetries = Math.max(0, maxRequestRetries); - if(this.scrobbling === true) { this.logger.warn(`Already scrobble processing! Processing needs to be stopped before it can be started`); return; } + // can't have negative retries! + const maxRetries = Math.max(0, maxRequestRetries); + while (this.scrobbleRetries <= maxRetries) { try { await this.doProcessing(signal); @@ -1030,11 +1030,13 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i this.logger.warn('Stopping scrobble processing due to client no longer usable.'); await this.notify({title: `Processing Error`, message: `Encountered error while scrobble processing and client is no longer usable, stopping processing!. | Error: ${e.message}`, priority: 'error'}); throw e; - } else if (this.authGated()) { + } + if (this.authGated()) { this.logger.warn('Stopping scrobble processing due to client no longer being authenticated.'); await this.notify({title: ` Processing Error`, message: `Encountered error while scrobble processing and client is no longer authenticated, stopping processing!. | Error: ${e.message}`, priority: 'error'}); throw e; - } else if (this.scrobbleRetries < maxRetries) { + } + if (this.scrobbleRetries < maxRetries) { const delayFor = pollingBackoff(this.scrobbleRetries + 1, retryMultiplier); this.logger.info(`Scrobble processing retries (${this.scrobbleRetries}) less than max processing retries (${maxRetries}), restarting processing after ${delayFor} second delay...`); await this.notify({title: `Processing Retry`, message: `Encountered error while polling but retries (${this.scrobbleRetries}) are less than max poll retries (${maxRetries}), restarting processing after ${delayFor} second delay. | Error: ${e.message}`, priority: 'warn'}); @@ -1294,9 +1296,8 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i if (processable === 0) { this.deadLogger.verbose(queueStatus); return; - } else { - this.setStatus(`Processing ${processable} Dead Plays`); } + this.setStatus(`Processing ${processable} Dead Plays`); this.logger.info(queueStatus); if(!this.upstreamRefresh.refreshEnabled) { this.deadLogger.verbose('Scrobble refresh is DISABLED. All dead scrobbles will likely always be scrobbled (nothing to check duplicates against).'); @@ -1456,10 +1457,10 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i // return; // } const deadQueueState = deadScrobble.queueStates.find(x => x.queueName === CLIENT_DEAD_QUEUE && x.queueStatus !== 'completed'); - const isQueued = deadQueueState.queueStatus === 'queued'; if(deadQueueState === undefined) { throw new Error(`Play ${deadScrobble.uid} is not currently queued in dead letter.`); } + const isQueued = deadQueueState.queueStatus === 'queued'; //this.deadLetterScrobbles.splice(index, 1); this.deadLetterGauge.labels(this.getPrometheusLabels()).dec(); const queueUpdate: Partial = { @@ -1723,9 +1724,8 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i if(npUpdateTopReason === 'previous update data matches current') { if(thresholds.maxMet) { return [true, `previous matches current update --AND-- ${thresholds.maxReason}`]; - } else { - return [false, `previous matches current update --BUT-- ${thresholds.maxReason}`]; } + return [false, `previous matches current update --BUT-- ${thresholds.maxReason}`]; } return [false, npUpdateTopReason]; @@ -1749,7 +1749,7 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i if (!thresholds.minMet) { return [false, `${npUpdateTopReason} and ${validStatusReason} --BUT-- ${thresholds.minReason}`]; } - else if ( + if ( // status hasn't changed this.nowPlayingLastPlay.status?.calculated === sourcePlayerData.status?.calculated // and both plays are defined and have not changed diff --git a/src/backend/utils/FSUtils.ts b/src/backend/utils/FSUtils.ts index 8ce631e4..d134a54a 100644 --- a/src/backend/utils/FSUtils.ts +++ b/src/backend/utils/FSUtils.ts @@ -51,16 +51,16 @@ export const fileOrDirectoryIsWriteable = (location: string) => { } catch (accessError: any) { if (accessError.code === 'EACCES') { // also can't access directory :( - throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application does not have permission to write to the parent directory`); - } else { - throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application is unable to access the parent directory due to a system error`, { cause: accessError }); + throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application does not have permission to write to the parent directory`,{cause: accessError}); } + + throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application is unable to access the parent directory due to a system error`, { cause: accessError }); } - } else if (code === 'EACCES') { - throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application does not have permission to write to it.`); - } else { - throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, { cause: err }); } + if (code === 'EACCES') { + throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application does not have permission to write to it.`,{cause: err}); + } + throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, { cause: err }); } }; @@ -74,10 +74,10 @@ export const fileExists = (location: string) => { const { code } = err; if (code === 'ENOENT') { return false; - } else if (code === 'EACCES') { - throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application does not have permission to write to it.`); - } else { - throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, { cause: err }); } + if (code === 'EACCES') { + throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application does not have permission to write to it.`, {cause: err}); + } + throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, { cause: err }); } }; \ No newline at end of file diff --git a/src/client/components/ActivityDetail.tsx b/src/client/components/ActivityDetail.tsx index bae5e0c7..e60a010b 100644 --- a/src/client/components/ActivityDetail.tsx +++ b/src/client/components/ActivityDetail.tsx @@ -150,13 +150,12 @@ export const ActivitySummaryFetchable = (props: MarkOptional { const { activity: { - queueStates = [], play: { lifecycle = [], scrobble, } = {}, error, - } + } = {} } = props; if(error !== undefined && error !== null) { return ; @@ -381,7 +380,7 @@ export const ActivityCollapsible = (props: ActivitySummaryProps & { key?: string - + { if(!a.dt.isSame(b.dt)) { return a.dt.isBefore(b.dt) ? -1 : 1; - } else { - // if they are the same timestamp then we need to determine the likely logical order + } + // if they are the same timestamp then we need to determine the likely logical order - // queue created always occurs before other actions as the play is queued first, then processed - if(b.id.includes('queue-created')) { - return 1; - } - if(a.id.includes('queue-created')) { - return -1; - } - - // transform steps always occur before scrobble actions - if(a.id.includes('scrobble') && b.id === 'transform-steps') { - return 1; - } - if(b.id.includes('scrobble') && a.id === 'transform-steps') { - return -1; - } + // queue created always occurs before other actions as the play is queued first, then processed + if(b.id.includes('queue-created')) { + return 1; + } + if(a.id.includes('queue-created')) { + return -1; + } + + // transform steps always occur before scrobble actions + if(a.id.includes('scrobble') && b.id === 'transform-steps') { + return 1; + } + if(b.id.includes('scrobble') && a.id === 'transform-steps') { + return -1; + } - // dupe matching always occurs before scrobbling - if(b.id === 'scrobble-match' && a.id === 'scrobble-response') { - return 1; - } - if(a.id === 'scrobble-match' && b.id === 'scrobble-response') { - return -1; - } + // dupe matching always occurs before scrobbling + if(b.id === 'scrobble-match' && a.id === 'scrobble-response') { + return 1; + } + if(a.id === 'scrobble-match' && b.id === 'scrobble-response') { + return -1; + } - // queue updated (finished) always occurs last - if(a.id.includes('queue-updated')) { - return 1; - } - if(b.id.includes('queue-updated')) { - return -1; - } + // queue updated (finished) always occurs last + if(a.id.includes('queue-updated')) { + return 1; + } + if(b.id.includes('queue-updated')) { + return -1; } // nothing else matched, keep order @@ -518,7 +517,7 @@ export const ActivityTimeline = (props: ActivityTimelineProps) => { console.log(f); - const timelineElements: React.JSX.Element[] = timelineItems.map((x) => { + const timelineElements: React.JSX.Element[] = timelineItems.flatMap((x) => { const timelineKey = `${x.id}-${x.dt.unix()}`; switch(x.id) { case 'new': @@ -547,7 +546,7 @@ export const ActivityTimeline = (props: ActivityTimelineProps) => { } } return undefined; - }).flat(); + }); return ( diff --git a/src/client/components/msComponent/MSComponentDetailed.tsx b/src/client/components/msComponent/MSComponentDetailed.tsx index 391df4ba..ee680eff 100644 --- a/src/client/components/msComponent/MSComponentDetailed.tsx +++ b/src/client/components/msComponent/MSComponentDetailed.tsx @@ -176,12 +176,12 @@ export const ComponentDetailedDesktop = (props: {data?: ComponentCommonApiJson, - - + + - - {sleepingRender}{props.data.status} + + {sleepingRender}{data.status} @@ -189,8 +189,8 @@ export const ComponentDetailedDesktop = (props: {data?: ComponentCommonApiJson, {error !== undefined && error !== null ? : undefined} {warning !== undefined && warning !== null ? : undefined} - {props.live ? : } - + {props.live ? : } + ) diff --git a/src/client/components/playActivity/ActivityList.tsx b/src/client/components/playActivity/ActivityList.tsx index bdf94832..859ebfdb 100644 --- a/src/client/components/playActivity/ActivityList.tsx +++ b/src/client/components/playActivity/ActivityList.tsx @@ -115,7 +115,7 @@ export const ListContainerFetchable = (props: { componentId: number, componentTy const queryClient = useQueryClient(); const client = useSSEContext(); useSSEAnyEvent(client, (payload) => { - if ('componentId' in (payload.data as object) && (payload.data as Record).componentId === props.componentId) { + if ('componentId' in (payload.data as object) && (payload.data as Record).componentId === componentId) { switch (payload.type) { case 'playInsert': { @@ -285,7 +285,7 @@ export const ListContainerFilterable = (props: { componentId: number, componentT return ( {componentType === 'source' ? 'Plays' : 'Scrobbles'} - + ) -- 2.51.2