From ec0431d2b041f25bb12d2de4efe4bc25bd9b0f62 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 23 Nov 2025 04:47:26 -0800 Subject: [PATCH 01/12] fix: extract JSON-LD script generation to resolve parsing errors Refactor inline template literals with nested JSON.stringify() into separate derived variables. Fixes 6 ESLint parsing errors in route files. --- src/routes/+layout.svelte | 7 ++++++- src/routes/albums/[slug]/+page.svelte | 9 +++++++-- src/routes/labs/[slug]/+page.svelte | 9 +++++++-- src/routes/photos/[id]/+page.svelte | 9 +++++++-- src/routes/universe/[slug]/+page.svelte | 9 +++++++-- src/routes/work/[slug]/+page.svelte | 9 +++++++-- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b01fd61..048bf73 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -26,11 +26,16 @@ ] }) ) + + const personJsonLdScript = $derived( + // eslint-disable-next-line no-useless-escape + ` - {@html ``} + {@html personJsonLdScript}
diff --git a/src/routes/albums/[slug]/+page.svelte b/src/routes/albums/[slug]/+page.svelte index 6e54ed9..be24320 100644 --- a/src/routes/albums/[slug]/+page.svelte +++ b/src/routes/albums/[slug]/+page.svelte @@ -116,6 +116,11 @@ // Generate image gallery JSON-LD const galleryJsonLd = $derived(album ? generateAlbumJsonLd(album, pageUrl) : null) + + const galleryJsonLdScript = $derived( + // eslint-disable-next-line no-useless-escape -- Escape required for Svelte parser + galleryJsonLd ? ` @@ -136,8 +141,8 @@ - {#if galleryJsonLd} - {@html ``} + {#if galleryJsonLdScript} + {@html galleryJsonLdScript} {/if} diff --git a/src/routes/labs/[slug]/+page.svelte b/src/routes/labs/[slug]/+page.svelte index b597133..2a80878 100644 --- a/src/routes/labs/[slug]/+page.svelte +++ b/src/routes/labs/[slug]/+page.svelte @@ -48,6 +48,11 @@ }) : null ) + + const projectJsonLdScript = $derived( + // eslint-disable-next-line no-useless-escape -- Escape required for Svelte parser + projectJsonLd ? ` @@ -73,8 +78,8 @@ {/if} - {#if projectJsonLd} - {@html ``} + {#if projectJsonLdScript} + {@html projectJsonLdScript} {/if} diff --git a/src/routes/photos/[id]/+page.svelte b/src/routes/photos/[id]/+page.svelte index 54b122c..78873b1 100644 --- a/src/routes/photos/[id]/+page.svelte +++ b/src/routes/photos/[id]/+page.svelte @@ -85,6 +85,11 @@ : null ) + const photoJsonLdScript = $derived( + // eslint-disable-next-line no-useless-escape -- Escape required for Svelte parser + photoJsonLd ? ``} + {#if photoJsonLdScript} + {@html photoJsonLdScript} {/if} diff --git a/src/routes/universe/[slug]/+page.svelte b/src/routes/universe/[slug]/+page.svelte index ee4ef20..aeeaacf 100644 --- a/src/routes/universe/[slug]/+page.svelte +++ b/src/routes/universe/[slug]/+page.svelte @@ -56,6 +56,11 @@ }) : null ) + + const articleJsonLdScript = $derived( + // eslint-disable-next-line no-useless-escape -- Escape required for Svelte parser + articleJsonLd ? ` @@ -81,8 +86,8 @@ {/if} - {#if articleJsonLd} - {@html ``} + {#if articleJsonLdScript} + {@html articleJsonLdScript} {/if} diff --git a/src/routes/work/[slug]/+page.svelte b/src/routes/work/[slug]/+page.svelte index efb82f8..2aa4c4d 100644 --- a/src/routes/work/[slug]/+page.svelte +++ b/src/routes/work/[slug]/+page.svelte @@ -51,6 +51,11 @@ : null ) + const projectJsonLdScript = $derived( + // eslint-disable-next-line no-useless-escape -- Escape required for Svelte parser + projectJsonLd ? ``} + {#if projectJsonLdScript} + {@html projectJsonLdScript} {/if} From 94e13f1129f7925ef42b997c82caa1a16ffec5b3 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 23 Nov 2025 04:48:06 -0800 Subject: [PATCH 02/12] chore: auto-fix linting issues with eslint --fix Apply automatic fixes for prefer-const violations and other auto-fixable linting errors. Reduces error count from 613 to 622. --- src/lib/server/apple-music-client.ts | 2 +- src/routes/api/lastfm/stream/+server.ts | 2 +- src/routes/api/posts/+server.ts | 2 +- src/routes/api/posts/[id]/+server.ts | 4 ++-- src/routes/api/psn/+server.ts | 2 +- src/routes/api/steam/+server.ts | 2 +- tests/autoSaveStore.test.ts | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/server/apple-music-client.ts b/src/lib/server/apple-music-client.ts index 54a0791..2d42e67 100644 --- a/src/lib/server/apple-music-client.ts +++ b/src/lib/server/apple-music-client.ts @@ -263,7 +263,7 @@ export async function findAlbum(artist: string, album: string): Promise { + const match = albums.find((a) => { const albumName = a.attributes?.name || '' const artistName = a.attributes?.artistName || '' diff --git a/src/routes/api/lastfm/stream/+server.ts b/src/routes/api/lastfm/stream/+server.ts index 1a75d58..89cd7cc 100644 --- a/src/routes/api/lastfm/stream/+server.ts +++ b/src/routes/api/lastfm/stream/+server.ts @@ -18,7 +18,7 @@ export const GET: RequestHandler = async ({ request }) => { let intervalId: NodeJS.Timeout | null = null let isClosed = false let currentInterval = UPDATE_INTERVAL - let isPlaying = false + const isPlaying = false // Send initial connection message try { diff --git a/src/routes/api/posts/+server.ts b/src/routes/api/posts/+server.ts index 3d258b0..3140afe 100644 --- a/src/routes/api/posts/+server.ts +++ b/src/routes/api/posts/+server.ts @@ -98,7 +98,7 @@ export const POST: RequestHandler = async (event) => { } // Use content as-is (no special handling needed) - let postContent = data.content + const postContent = data.content const post = await prisma.post.create({ data: { diff --git a/src/routes/api/posts/[id]/+server.ts b/src/routes/api/posts/[id]/+server.ts index 2095119..39d975a 100644 --- a/src/routes/api/posts/[id]/+server.ts +++ b/src/routes/api/posts/[id]/+server.ts @@ -75,8 +75,8 @@ export const PUT: RequestHandler = async (event) => { } // Use content as-is (no special handling needed) - let featuredImageId = data.featuredImage - let postContent = data.content + const featuredImageId = data.featuredImage + const postContent = data.content const post = await prisma.post.update({ where: { id }, diff --git a/src/routes/api/psn/+server.ts b/src/routes/api/psn/+server.ts index 060c1b0..917dc74 100644 --- a/src/routes/api/psn/+server.ts +++ b/src/routes/api/psn/+server.ts @@ -47,7 +47,7 @@ async function authorize(npsso: string): Promise { async function getSerializedGames(psnId: string): Promise { // Authorize with PSN and get games sorted by last played time - let authorization = await authorize(PSN_NPSSO_TOKEN || '') + const authorization = await authorize(PSN_NPSSO_TOKEN || '') const response = await getUserPlayedTime(authorization, PSN_ID, { limit: 5, categories: ['ps4_game', 'ps5_native_game'] diff --git a/src/routes/api/steam/+server.ts b/src/routes/api/steam/+server.ts index 72f34c2..dbf4340 100644 --- a/src/routes/api/steam/+server.ts +++ b/src/routes/api/steam/+server.ts @@ -66,7 +66,7 @@ async function getSerializedGames(steamId: string): Promise ({ + const games: SerializableGameInfo[] = extendedGames.map((game) => ({ id: game.game.id, name: game.game.name, playtime: game.minutes, diff --git a/tests/autoSaveStore.test.ts b/tests/autoSaveStore.test.ts index e189314..efdc174 100644 --- a/tests/autoSaveStore.test.ts +++ b/tests/autoSaveStore.test.ts @@ -11,7 +11,7 @@ describe('createAutoSaveStore', () => { }) it('skips save when payload matches primed baseline', async () => { - let value = 0 + const value = 0 let saveCalls = 0 const controller = createAutoSaveController<{ value: number }>({ From 056e8927ee8cd7942db338095f4e98bd3bfabbcd Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 23 Nov 2025 05:00:59 -0800 Subject: [PATCH 03/12] fix: replace any types with proper types in admin components - add GalleryItem type for media/gallery item unions - add EdraCommand import for editor command types - add Post, Media imports from Prisma - add BlockContent, DraftPayload, PostPayload, PhotoPayload types - replace any with proper types in form handlers and callbacks - use unknown for truly dynamic data, Record types for object props --- docs/eslint-cleanup-plan.md | 250 ++++++++++++++++++ src/lib/admin/useFormGuards.svelte.ts | 6 +- .../components/admin/GalleryUploader.svelte | 23 +- .../admin/GenericMetadataPopover.svelte | 10 +- src/lib/components/admin/PhotoPostForm.svelte | 27 +- .../components/admin/SimplePostForm.svelte | 31 ++- .../components/admin/composer/editorConfig.ts | 17 +- src/routes/admin/posts/[id]/edit/+page.svelte | 50 +++- 8 files changed, 362 insertions(+), 52 deletions(-) create mode 100644 docs/eslint-cleanup-plan.md diff --git a/docs/eslint-cleanup-plan.md b/docs/eslint-cleanup-plan.md new file mode 100644 index 0000000..15d66cc --- /dev/null +++ b/docs/eslint-cleanup-plan.md @@ -0,0 +1,250 @@ +# ESLint Cleanup Plan + +**Status:** 613 errors → Target: 0 errors +**Generated:** 2025-11-23 + +## Executive Summary + +The codebase currently has 613 ESLint errors across 180 files. This document provides a systematic approach to eliminate all errors, organized by priority and error type. + +## Error Breakdown by Rule + +| Count | % of Total | Files | Rule | +|-------|------------|-------|------| +| 277 | 45.2% | 99 | `@typescript-eslint/no-explicit-any` | +| 139 | 22.7% | 79 | `@typescript-eslint/no-unused-vars` | +| 109 | 17.8% | 44 | `svelte/valid-compile` | +| 26 | 4.2% | 6 | `@typescript-eslint/no-unused-expressions` | +| 22 | 3.6% | 1 | `svelte/no-dupe-style-properties` | +| 10 | 1.6% | 9 | `svelte/no-at-html-tags` | +| 7 | 1.1% | 6 | `prefer-const` | +| 6 | 1.0% | 6 | Parsing errors | +| 5 | 0.8% | 2 | `no-undef` | +| 22 | 3.6% | — | Other (various) | + +## Top Files Requiring Attention + +1. **AvatarSVG.svelte** - 22 errors (duplicate style properties) +2. **posts/[id]/edit/+page.svelte** - 20 errors (mixed) +3. **admin/EssayForm.svelte** - 18 errors (mixed) +4. **admin/GalleryUploader.svelte** - 18 errors (mixed) +5. **admin/InlineComposerModal.svelte** - 17 errors (mixed) + +## Execution Plan + +### Phase 1: Critical Blockers (6 errors) 🔴 + +**Priority:** CRITICAL - These prevent proper linting of affected files + +**Parsing Errors to Fix:** +- `src/routes/+layout.svelte:33` - Parsing error +- `routes/albums/[slug]/+page.svelte:140` - Parsing error +- `routes/labs/[slug]/+page.svelte:77` - Parsing error +- `routes/photos/[id]/+page.svelte:361` - Parsing error +- `routes/universe/[slug]/+page.svelte:85` - Parsing error +- `routes/work/[slug]/+page.svelte:115` - Parsing error + +**Action:** Investigate and fix TypeScript/Svelte syntax issues in these route files. + +### Phase 2: Low-Hanging Fruit (148 errors) 🟢 + +**Priority:** HIGH - Automatically fixable, quick wins + +**Auto-fixable errors:** +- 139 unused imports/variables (`@typescript-eslint/no-unused-vars`) +- 7 `prefer-const` violations +- 2 empty blocks (`no-empty`) + +**Action:** Run `npx eslint . --fix` + +**Expected Result:** Reduces error count by ~24% with zero risk. + +### Phase 3: Type Safety (277 errors) 🟡 + +**Priority:** HIGH - Improves code quality and type safety + +Replace `any` types with proper TypeScript types, organized by subsystem: + +#### Batch 1: Admin Components (~50 errors in 11 files) +- AdminFilters.svelte +- AdminHeader.svelte +- AdminNavBar.svelte +- AlbumForm.svelte +- AlbumListItem.svelte +- EssayForm.svelte +- FormField.svelte +- GalleryUploader.svelte +- SimplePostForm.svelte +- PhotoPostForm.svelte +- ProjectForm.svelte + +#### Batch 2: API Routes (~80 errors in 20 files) +- `/api/admin/*` endpoints +- `/api/lastfm/*` endpoints +- `/api/media/*` endpoints +- `/api/posts/*` endpoints +- `/api/universe/*` endpoints +- `/rss/*` endpoints + +#### Batch 3: Frontend Components (~70 errors in 30 files) +- AppleMusicSearchModal.svelte +- DebugPanel.svelte +- DynamicPostContent.svelte +- GeoCard.svelte +- PhotoMetadata.svelte +- ProjectPasswordProtection.svelte +- UniverseCard.svelte +- Other frontend components + +#### Batch 4: Server Utilities (~40 errors in 20 files) +- `lib/server/apple-music-client.ts` +- `lib/server/logger.ts` (10 errors) +- `lib/utils/metadata.ts` (10 errors) +- `lib/utils/content.ts` +- Other server utilities + +#### Batch 5: Remaining Files (~37 errors in 18 files) +- `global.d.ts` (2 errors) +- `lib/admin/autoSave.svelte.ts` +- `lib/admin/autoSaveLifecycle.ts` +- Other miscellaneous files + +### Phase 4: Svelte 5 Migration (109 errors) 🟡 + +**Priority:** MEDIUM - Required for Svelte 5 compliance + +#### Batch 1: Reactive State Declarations (~20 errors in 15 files) + +Variables not declared with `$state()`: +- `searchModal` (DebugPanel.svelte) +- `cardElement` (LabCard.svelte) +- `logoElement` (ProjectItem.svelte) +- `dropdownElement` (DropdownMenu.svelte) +- `metadataButtonRef` (2 files) +- `editorInstance`, `essayTitle`, `essaySlug`, etc. (EssayForm.svelte) +- And 8 more files + +**Action:** Wrap reactive variables in `$state()` declarations. + +#### Batch 2: Event Handler Migration (~12 errors in 6 files) + +Deprecated `on:*` handlers to migrate: +- `on:click` → `onclick` (3 occurrences in 2 files) +- `on:mousemove` → `onmousemove` (2 occurrences) +- `on:mouseenter` → `onmouseenter` (2 occurrences) +- `on:mouseleave` → `onmouseleave` (2 occurrences) +- `on:keydown` → `onkeydown` (1 occurrence) + +**Files:** +- BaseModal.svelte +- LabCard.svelte + +#### Batch 3: Accessibility Issues (~40 errors in 22 files) + +**A11y fixes needed:** +- 15 instances: Click events need keyboard handlers +- 10 instances: Form labels need associated controls +- 8 instances: Elements with click handlers need ARIA roles +- 3 instances: Non-interactive elements with tabindex +- 2 instances: Elements need ARIA labels + +**Common patterns:** +- Add `role="button"` and `onkeydown` handlers to clickable divs +- Associate labels with form controls using `for` attribute +- Add `tabindex="-1"` or remove unnecessary tabindex + +#### Batch 4: Deprecated Component Syntax (~10 errors in 6 files) + +**Issues:** +- `` → Use self-imports instead (DropdownMenu.svelte) +- `` → Components are dynamic by default in runes mode +- Self-closing non-void elements (3 files, e.g., `