Skip to content

Commit 174267e

Browse files
thunpisitclaude
andauthored
fix: 7 ESLint errors blocking CI on fresh install (closes #47) (#49)
The CI gate (.github/workflows/deploy.yml) runs `pnpm run lint`, which fails on every push because of legitimately-flagged code patterns. None of these are real bugs — they're cases where the linter rule is correct in general but doesn't fit our specific intent. Fixed each at the smallest scope: - CookieBanner.svelte: privacyHref is operator-supplied (built from layout's `localePath()` → /[locale]/privacy-policy), not a build-time route — eslint-disable for svelte/no-navigation-without-resolve. - Seo.svelte: emit JSON-LD via concatenated string + escaped </script> sequence so untrusted JSON values can't break out; eslint-disable svelte/no-at-html-tags + drop the unnecessary `\/` escape. - webhooks/index.ts: drop the dead initializer on `responseExcerpt` (every code path reassigns before reading). - dashboard/+page.server.ts: drop unused `and` from drizzle-orm import. - media/+page.svelte: lookup map is local to a $derived block, rebuilt every invocation, never reactive — plain Map is correct; eslint-disable svelte/prefer-svelte-reactivity with rationale. - (www)/[locale]/[...slug]/+page.svelte: trusted server-rendered markdown — same eslint-disable pattern blog/[slug] already uses. After: `pnpm run lint` reports 0 errors (407 paraglide-generated prettier warnings remain — those are auto-generated and gitignored in spirit; separate cleanup). Build still green; svelte-check unchanged. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 228807b commit 174267e

6 files changed

Lines changed: 19 additions & 5 deletions

File tree

src/lib/components/consent/CookieBanner.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<p class="font-medium text-foreground mb-1">{m.cookie_banner_title()}</p>
4747
<p>
4848
{m.cookie_banner_body()}
49+
<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -- privacy href is operator-supplied (always /[locale]/privacy-policy from layout); not a build-time route -->
4950
<a href={privacyHref} class="underline hover:text-foreground">{m.cookie_banner_learn_more()}</a>
5051
</p>
5152
{#if detailsOpen}

src/lib/components/seo/Seo.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,16 @@
8888
<meta name="twitter:site" content={defaults.twitter} />
8989
{/if}
9090

91-
<!-- JSON-LD: one <script> per entry for maximum tooling compatibility -->
91+
<!-- JSON-LD: one <script> per entry for maximum tooling compatibility.
92+
Using {@html} is required to emit a literal <script> tag (Svelte
93+
strips them in normal markup). The payload is server-built JSON
94+
from our own typed builders ($lib/seo) — never user input — and
95+
we escape any "</script>" sequence before injection so a JSON
96+
value can't break out of the tag. -->
9297
{#each jsonLd as ld, i (i)}
93-
{@html `<script type="application/ld+json">${JSON.stringify(ld)}<\/script>`}
98+
{@const safeJson = JSON.stringify(ld).replace(/<\/script>/gi, '<\\/script>')}
99+
<!-- eslint-disable-next-line svelte/no-at-html-tags -- trusted: server-built JSON-LD with </script> escaped -->
100+
{@html '<script type="application/ld+json">' + safeJson + '<' + '/script>'}
94101
{/each}
95102

96103
<!-- RSS auto-discovery -->

src/lib/server/webhooks/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ async function deliverOne(
7979
for (let attempt = 1; attempt <= MAX_INLINE_ATTEMPTS; attempt++) {
8080
const t0 = Date.now();
8181
let responseStatus: number | null = null;
82-
let responseExcerpt: string | null = null;
82+
// Assigned in every code path below; no initializer to satisfy
83+
// no-useless-assignment.
84+
let responseExcerpt: string | null;
8385
let ok = false;
8486
try {
8587
const ctrl = new AbortController();

src/routes/(cms)/cms/dashboard/+page.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { redirect, error } from "@sveltejs/kit";
22
import { drizzle } from "drizzle-orm/d1";
3-
import { desc, eq, and, gte } from "drizzle-orm";
3+
import { desc, eq, gte } from "drizzle-orm";
44
import * as schema from "$lib/server/content/schema";
55
import { canManageUsers } from "$lib/server/auth/permissions";
66
import { AnalyticsService } from "$lib/server/analytics";

src/routes/(cms)/cms/media/+page.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@
3434
Boolean(data.user && ['super_admin', 'admin', 'editor'].includes(data.user.role)),
3535
);
3636
37-
// Build a parent → children map for the folder tree.
37+
// Build a parent → children map for the folder tree. Local to the
38+
// derived; never read reactively (rebuilt every invocation), so a
39+
// plain Map is correct — no need for SvelteMap's reactivity overhead.
3840
const childrenByParent = $derived.by(() => {
41+
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- local lookup, not reactive state
3942
const map = new Map<string | null, MediaFolderRecord[]>();
4043
for (const f of data.folders) {
4144
const parent = f.parentId ?? null;

src/routes/(www)/[locale]/[...slug]/+page.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<h1 class="text-4xl font-bold mb-2">{data.title}</h1>
2323
</header>
2424
<div class="prose prose-neutral dark:prose-invert max-w-none">
25+
<!-- eslint-disable-next-line svelte/no-at-html-tags -- trusted: server-rendered markdown from CMS -->
2526
{@html data.htmlContent}
2627
</div>
2728
</article>

0 commit comments

Comments
 (0)