diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index debf4ba648..5551030f1e 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -117,6 +117,11 @@ jobs: # eslint-report: web/eslint_report.json # github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Web tsslint + if: steps.changed-files.outputs.any_changed == 'true' + working-directory: ./web + run: pnpm run lint:tss + - name: Web type check if: steps.changed-files.outputs.any_changed == 'true' working-directory: ./web diff --git a/web/.vscode/extensions.json b/web/.vscode/extensions.json index 68f5c7bf0e..01235650ab 100644 --- a/web/.vscode/extensions.json +++ b/web/.vscode/extensions.json @@ -1,6 +1,8 @@ { "recommendations": [ "bradlc.vscode-tailwindcss", - "kisstkondoros.vscode-codemetrics" + "kisstkondoros.vscode-codemetrics", + "johnsoncodehk.vscode-tsslint", + "dbaeumer.vscode-eslint" ] } diff --git a/web/package.json b/web/package.json index 5dfacece73..12e7ac3b17 100644 --- a/web/package.json +++ b/web/package.json @@ -33,6 +33,7 @@ "lint:quiet": "pnpm lint --quiet", "lint:complexity": "pnpm lint --rule 'complexity: [error, {max: 15}]' --quiet", "lint:report": "pnpm lint --output-file eslint_report.json --format json", + "lint:tss": "tsslint --project tsconfig.json", "type-check": "tsc --noEmit", "type-check:tsgo": "tsgo --noEmit", "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", @@ -178,6 +179,9 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", + "@tsslint/cli": "^3.0.1", + "@tsslint/compat-eslint": "^3.0.1", + "@tsslint/config": "^3.0.1", "@types/js-cookie": "^3.0.6", "@types/js-yaml": "^4.0.9", "@types/negotiator": "^0.6.4", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a6c31f2f62..0a549f8a61 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -430,6 +430,15 @@ importers: '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) + '@tsslint/cli': + specifier: ^3.0.1 + version: 3.0.1(@tsslint/compat-eslint@3.0.1(jiti@1.21.7)(typescript@5.9.3))(typescript@5.9.3) + '@tsslint/compat-eslint': + specifier: ^3.0.1 + version: 3.0.1(jiti@1.21.7)(typescript@5.9.3) + '@tsslint/config': + specifier: ^3.0.1 + version: 3.0.1(@tsslint/compat-eslint@3.0.1(jiti@1.21.7)(typescript@5.9.3))(typescript@5.9.3) '@types/js-cookie': specifier: ^3.0.6 version: 3.0.6 @@ -1644,14 +1653,30 @@ packages: eslint: optional: true + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.21.1': resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.4.2': resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.17.0': resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1664,6 +1689,10 @@ packages: resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.27.0': + resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.39.2': resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1676,6 +1705,10 @@ packages: resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.4.1': resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1990,6 +2023,14 @@ packages: cpu: [x64] os: [win32] + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -3462,6 +3503,36 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' + '@tsslint/cli@3.0.1': + resolution: {integrity: sha512-y5yzMFl6sKQNsomuGInmFzMiKW37xxDcJauHnPqYoCWL8LldNLnaUOBqx0illfNZ0FDAiSuV/oshC/NG8/F2Tw==} + engines: {node: '>=22.6.0'} + hasBin: true + peerDependencies: + typescript: '*' + + '@tsslint/compat-eslint@3.0.1': + resolution: {integrity: sha512-cojBaB1C9RxWjDfCvLBhbffshyizb+Cf1Os9NXHuzyQOPvU1IwYPW5Sxo1RU19pCOE9/TvQcuxgnGfwbkk/Dig==} + + '@tsslint/config@3.0.1': + resolution: {integrity: sha512-1S8YYLrZE22xfH3GtDXRO7YzkeQj9+FjoxaWhYQsjWDU82HHeSRWq5d2UzPSN/ac6WFmFq8yApXIGylfvrG6MA==} + engines: {node: '>=22.6.0'} + hasBin: true + peerDependencies: + '@tsslint/compat-eslint': 3.0.0-alpha.0 + tsl: ^1.0.28 + peerDependenciesMeta: + '@tsslint/compat-eslint': + optional: true + tsl: + optional: true + + '@tsslint/core@3.0.1': + resolution: {integrity: sha512-8FEczJ20hdpmEH5vm272hS3QAycsk5574yZT6VMS8TUK8kNY4qoRKY/gdOY0nYNYWZrRPs+6dr1TmEVPBZjlvw==} + engines: {node: '>=22.6.0'} + + '@tsslint/types@3.0.1': + resolution: {integrity: sha512-JPK/+tSJ2hPTwgN173fkenPEnAI2CD0r0FDJ23PfftTc0NM449ZiAFHvs1KuPUOjAvBFIo5BsLr7Kxc1Ekdgtw==} + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -3881,6 +3952,18 @@ packages: '@vitest/utils@4.0.17': resolution: {integrity: sha512-RG6iy+IzQpa9SB8HAFHJ9Y+pTzI+h8553MrciN9eC6TFBErqrQaTas4vG+MVj8S4uKk8uTT2p0vgZPnTdxd96w==} + '@volar/language-core@2.4.27': + resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==} + + '@volar/language-hub@0.0.1': + resolution: {integrity: sha512-2eOUnlMKTyjtlXIVd+6pfAtcuVugxCOgpNgcLWmlPuncQTG5C1E5mTDL/PUMw7aEnLySUOtMTIp8lT3vk/7w6Q==} + + '@volar/source-map@2.4.27': + resolution: {integrity: sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==} + + '@volar/typescript@2.4.27': + resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==} + '@vue/compiler-core@3.5.25': resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==} @@ -5227,6 +5310,16 @@ packages: resolution: {integrity: sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@9.27.0: + resolution: {integrity: sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + eslint@9.39.2: resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6459,6 +6552,10 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -9760,6 +9857,11 @@ snapshots: eslint: 9.39.2(jiti@1.21.7) ignore: 7.0.5 + '@eslint-community/eslint-utils@4.9.1(eslint@9.27.0(jiti@1.21.7))': + dependencies: + eslint: 9.27.0(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@1.21.7))': dependencies: eslint: 9.39.2(jiti@1.21.7) @@ -9848,6 +9950,14 @@ snapshots: optionalDependencies: eslint: 9.39.2(jiti@1.21.7) + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 @@ -9856,10 +9966,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/config-helpers@0.2.3': {} + '@eslint/config-helpers@0.4.2': dependencies: '@eslint/core': 0.17.0 + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 @@ -9882,6 +10002,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/js@9.27.0': {} + '@eslint/js@9.39.2': {} '@eslint/markdown@7.5.1': @@ -9900,6 +10022,11 @@ snapshots: '@eslint/object-schema@2.1.7': {} + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + '@eslint/plugin-kit@0.4.1': dependencies: '@eslint/core': 0.17.0 @@ -10151,6 +10278,12 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 4.2.3 @@ -11757,6 +11890,47 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 + '@tsslint/cli@3.0.1(@tsslint/compat-eslint@3.0.1(jiti@1.21.7)(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@clack/prompts': 0.8.2 + '@tsslint/config': 3.0.1(@tsslint/compat-eslint@3.0.1(jiti@1.21.7)(typescript@5.9.3))(typescript@5.9.3) + '@tsslint/core': 3.0.1 + '@volar/language-core': 2.4.27 + '@volar/language-hub': 0.0.1 + '@volar/typescript': 2.4.27 + minimatch: 10.1.1 + typescript: 5.9.3 + transitivePeerDependencies: + - '@tsslint/compat-eslint' + - tsl + + '@tsslint/compat-eslint@3.0.1(jiti@1.21.7)(typescript@5.9.3)': + dependencies: + '@tsslint/types': 3.0.1 + '@typescript-eslint/parser': 8.53.0(eslint@9.27.0(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.27.0(jiti@1.21.7) + transitivePeerDependencies: + - jiti + - supports-color + - typescript + + '@tsslint/config@3.0.1(@tsslint/compat-eslint@3.0.1(jiti@1.21.7)(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@tsslint/types': 3.0.1 + minimatch: 10.1.1 + ts-api-utils: 2.4.0(typescript@5.9.3) + optionalDependencies: + '@tsslint/compat-eslint': 3.0.1(jiti@1.21.7)(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@tsslint/core@3.0.1': + dependencies: + '@tsslint/types': 3.0.1 + minimatch: 10.1.1 + + '@tsslint/types@3.0.1': {} + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -12036,6 +12210,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.53.0(eslint@9.27.0(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + eslint: 9.27.0(jiti@1.21.7) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.53.0 @@ -12250,6 +12436,20 @@ snapshots: '@vitest/pretty-format': 4.0.17 tinyrainbow: 3.0.3 + '@volar/language-core@2.4.27': + dependencies: + '@volar/source-map': 2.4.27 + + '@volar/language-hub@0.0.1': {} + + '@volar/source-map@2.4.27': {} + + '@volar/typescript@2.4.27': + dependencies: + '@volar/language-core': 2.4.27 + path-browserify: 1.0.1 + vscode-uri: 3.0.8 + '@vue/compiler-core@3.5.25': dependencies: '@babel/parser': 7.28.5 @@ -13823,6 +14023,48 @@ snapshots: eslint-visitor-keys@5.0.0: {} + eslint@9.27.0(jiti@1.21.7): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.27.0(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.27.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.7 + transitivePeerDependencies: + - supports-color + eslint@9.39.2(jiti@1.21.7): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7)) @@ -15454,6 +15696,10 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 2.0.2 diff --git a/web/tsslint.config.ts b/web/tsslint.config.ts new file mode 100644 index 0000000000..10c427638b --- /dev/null +++ b/web/tsslint.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, importESLintRules } from '@tsslint/config' + +// Run `npx tsslint-docgen` to generate documentation for the configured rules. + +export default defineConfig({ + rules: { + ...await importESLintRules({ + 'react-x/no-leaked-conditional-rendering': 'warn', + }), + }, +})