diff --git a/eslint.config.mjs b/eslint.config.mjs index 0d95fab8a..9a1b06ed3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,19 +4,9 @@ import typescriptEslint from '@typescript-eslint/eslint-plugin'; import globals from 'globals'; import tsParser from '@typescript-eslint/parser'; -import parser from 'svelte-eslint-parser'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import svelteParser from 'svelte-eslint-parser'; +import sveltePlugin from 'eslint-plugin-svelte'; import js from '@eslint/js'; -import { FlatCompat } from '@eslint/eslintrc'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all, -}); export default [ { @@ -40,12 +30,11 @@ export default [ 'prisma/.fabbrica/index.ts', ], }, - ...compat.extends( - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:svelte/recommended', - 'prettier', - ), + // Base JS rules first + js.configs.recommended, + // Svelte rules override JS rules where appropriate (intentional) + // This allows Svelte-specific handling of rules like no-undef, no-unused-vars + ...sveltePlugin.configs['flat/recommended'], { plugins: { '@typescript-eslint': typescriptEslint, @@ -55,6 +44,13 @@ export default [ globals: { ...globals.browser, ...globals.node, + // Add Svelte 5 runes as global variables + $state: 'readonly', + $derived: 'readonly', + $effect: 'readonly', + $props: 'readonly', + $bindable: 'readonly', + $inspect: 'readonly', }, parser: tsParser, @@ -74,15 +70,25 @@ export default [ 'ts-ignore': false, }, ], + // Add TypeScript ESLint rules manually + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-explicit-any': 'warn', + // Disable some strict Svelte rules that are too aggressive + 'svelte/require-each-key': 'warn', + 'svelte/no-useless-mustaches': 'warn', + 'svelte/prefer-writable-derived': 'warn', // New in 3.6.0 - prefer $derived over $state+$effect + 'svelte/valid-prop-names-in-kit-pages': 'warn', // Allow props other than data/errors in pages + 'no-unused-vars': 'off', // Use TypeScript version instead + 'no-undef': 'off', // TypeScript handles this }, }, { files: ['**/*.svelte'], languageOptions: { - parser: parser, - ecmaVersion: 5, - sourceType: 'script', + parser: svelteParser, + ecmaVersion: 'latest', // ES2023+ features support + sourceType: 'module', // ESM import/export support parserOptions: { parser: '@typescript-eslint/parser', diff --git a/package.json b/package.json index 1a7c9d5ba..9dc700a69 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@vitest/ui": "3.2.4", "eslint": "9.29.0", "eslint-config-prettier": "10.1.5", - "eslint-plugin-svelte": "2.46.1", + "eslint-plugin-svelte": "3.9.2", "globals": "16.2.0", "husky": "9.1.7", "jsdom": "26.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c049dae43..feecb8598 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,8 +127,8 @@ importers: specifier: 10.1.5 version: 10.1.5(eslint@9.29.0(jiti@1.21.6)) eslint-plugin-svelte: - specifier: 2.46.1 - version: 2.46.1(eslint@9.29.0(jiti@1.21.6))(svelte@5.34.5)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)) + specifier: 3.9.2 + version: 3.9.2(eslint@9.29.0(jiti@1.21.6))(svelte@5.34.5)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)) globals: specifier: 16.2.0 version: 16.2.0 @@ -774,12 +774,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.7.0': resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2671,32 +2665,22 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-compat-utils@0.5.1: - resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - eslint-config-prettier@10.1.5: resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-svelte@2.46.1: - resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} - engines: {node: ^14.17.0 || >=16.0.0} + eslint-plugin-svelte@3.9.2: + resolution: {integrity: sha512-aqzfHtG9RPaFhCUFm5QFC6eFY/yHFQIT8VYYFe7/mT2A9mbgVR3XV2keCqU19LN8iVD9mdvRvqHU+4+CzJImvg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 + eslint: ^8.57.1 || ^9.0.0 svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.3.0: resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2741,10 +2725,6 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -3267,8 +3247,8 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - known-css-properties@0.35.0: - resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + known-css-properties@0.36.0: + resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} @@ -3489,11 +3469,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@5.0.7: resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==} engines: {node: ^18 || >=20} @@ -3784,11 +3759,11 @@ packages: peerDependencies: postcss: ^8.2.14 - postcss-safe-parser@6.0.0: - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} peerDependencies: - postcss: ^8.3.3 + postcss: ^8.4.31 postcss-scss@4.0.9: resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} @@ -3815,6 +3790,10 @@ packages: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4117,11 +4096,6 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -4312,15 +4286,6 @@ packages: svelte: ^4.0.0 || ^5.0.0-next.0 typescript: '>=5.0.0' - svelte-eslint-parser@0.43.0: - resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - svelte: - optional: true - svelte-eslint-parser@1.2.0: resolution: {integrity: sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5284,11 +5249,6 @@ snapshots: '@esbuild/win32-x64@0.25.5': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.29.0(jiti@1.21.6))': - dependencies: - eslint: 9.29.0(jiti@1.21.6) - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@1.21.6))': dependencies: eslint: 9.29.0(jiti@1.21.6) @@ -7474,39 +7434,28 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.29.0(jiti@1.21.6)): - dependencies: - eslint: 9.29.0(jiti@1.21.6) - semver: 7.6.2 - eslint-config-prettier@10.1.5(eslint@9.29.0(jiti@1.21.6)): dependencies: eslint: 9.29.0(jiti@1.21.6) - eslint-plugin-svelte@2.46.1(eslint@9.29.0(jiti@1.21.6))(svelte@5.34.5)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)): + eslint-plugin-svelte@3.9.2(eslint@9.29.0(jiti@1.21.6))(svelte@5.34.5)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.29.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@1.21.6)) '@jridgewell/sourcemap-codec': 1.5.0 eslint: 9.29.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.29.0(jiti@1.21.6)) esutils: 2.0.3 - known-css-properties: 0.35.0 - postcss: 8.4.49 - postcss-load-config: 3.1.4(postcss@8.4.49)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)) - postcss-safe-parser: 6.0.0(postcss@8.4.49) - postcss-selector-parser: 6.1.2 - semver: 7.6.2 - svelte-eslint-parser: 0.43.0(svelte@5.34.5) + globals: 16.2.0 + known-css-properties: 0.36.0 + postcss: 8.5.6 + postcss-load-config: 3.1.4(postcss@8.5.6)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)) + postcss-safe-parser: 7.0.1(postcss@8.5.6) + semver: 7.7.2 + svelte-eslint-parser: 1.2.0(svelte@5.34.5) optionalDependencies: svelte: 5.34.5 transitivePeerDependencies: - ts-node - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.3.0: dependencies: esrecurse: 4.3.0 @@ -7581,12 +7530,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - espree@9.6.1: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -8108,7 +8051,7 @@ snapshots: kleur@4.1.5: {} - known-css-properties@0.35.0: {} + known-css-properties@0.36.0: {} kolorist@1.8.0: {} @@ -8332,8 +8275,6 @@ snapshots: nanoid@3.3.11: {} - nanoid@3.3.8: {} - nanoid@5.0.7: {} natural-compare@1.4.0: {} @@ -8552,12 +8493,12 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.49 - postcss-load-config@3.1.4(postcss@8.4.49)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)): + postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.49 + postcss: 8.5.6 ts-node: 10.9.1(@types/node@24.0.3)(typescript@5.6.3) postcss-load-config@4.0.2(postcss@8.4.49)(ts-node@10.9.1(@types/node@24.0.3)(typescript@5.6.3)): @@ -8573,13 +8514,9 @@ snapshots: postcss: 8.4.49 postcss-selector-parser: 6.1.2 - postcss-safe-parser@6.0.0(postcss@8.4.49): + postcss-safe-parser@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.49 - - postcss-scss@4.0.9(postcss@8.4.49): - dependencies: - postcss: 8.4.49 + postcss: 8.5.6 postcss-scss@4.0.9(postcss@8.5.3): dependencies: @@ -8599,7 +8536,7 @@ snapshots: postcss@8.4.49: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -8609,6 +8546,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.34.5): @@ -8879,8 +8822,6 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.6.2: {} - semver@7.7.2: {} set-cookie-parser@2.7.1: {} @@ -9079,16 +9020,6 @@ snapshots: transitivePeerDependencies: - picomatch - svelte-eslint-parser@0.43.0(svelte@5.34.5): - dependencies: - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - postcss: 8.4.49 - postcss-scss: 4.0.9(postcss@8.4.49) - optionalDependencies: - svelte: 5.34.5 - svelte-eslint-parser@1.2.0(svelte@5.34.5): dependencies: eslint-scope: 8.3.0 diff --git a/src/lib/components/GradeLabel.svelte b/src/lib/components/GradeLabel.svelte index 8c3412224..dfe9f47c1 100644 --- a/src/lib/components/GradeLabel.svelte +++ b/src/lib/components/GradeLabel.svelte @@ -46,7 +46,7 @@ {#if taskGrade !== TaskGrade.PENDING} {grade} {:else} - {'−'} + − {/if} diff --git a/src/lib/components/SubmissionStatus/IconForUpdating.svelte b/src/lib/components/SubmissionStatus/IconForUpdating.svelte index 80cf31870..b01a85029 100644 --- a/src/lib/components/SubmissionStatus/IconForUpdating.svelte +++ b/src/lib/components/SubmissionStatus/IconForUpdating.svelte @@ -12,9 +12,7 @@ {#if isLoggedIn}