mirror of
https://github.com/juherr/kill-the-news.git
synced 2026-06-20 22:03:48 +00:00
fix(lint): close type-check gaps in client scripts and tooling
Remove unused import flagged by CI lint, then harden the toolchain so such issues are caught before push: - lint-staged now also matches .tsx/.jsx (previously .tsx files skipped the pre-commit eslint pass, which is how the error reached CI) - eslint ignores generated client bundles (gitignored, not worth linting) - typecheck now also runs the client tsconfig; the hand-written browser source was excluded from the root config and never type-checked - consolidate the window global augmentations (showToast, parseJsonResponseOrThrow) into a single client globals.d.ts; the inline declare-global blocks failed (non-module files) and masked real errors Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ import prettier from "eslint-config-prettier";
|
|||||||
import tseslint from "typescript-eslint";
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
{ ignores: ["dist/", "coverage/"] },
|
{ ignores: ["dist/", "coverage/", "src/scripts/generated/"] },
|
||||||
...tseslint.configs.recommended,
|
...tseslint.configs.recommended,
|
||||||
prettier,
|
prettier,
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-2
@@ -16,11 +16,12 @@
|
|||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"test:coverage": "vitest run --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit && npm run typecheck:client",
|
||||||
|
"typecheck:client": "tsc -p src/scripts/client/tsconfig.json --noEmit",
|
||||||
"prepare": "husky && npm run build:client"
|
"prepare": "husky && npm run build:client"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{ts,js}": [
|
"*.{ts,tsx,js,jsx}": [
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
"prettier --write"
|
"prettier --write"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
updateFeedInList,
|
updateFeedInList,
|
||||||
removeFeedFromList,
|
removeFeedFromList,
|
||||||
removeFeedsFromListBulk,
|
removeFeedsFromListBulk,
|
||||||
deleteKeysWithConcurrency,
|
|
||||||
purgeFeedKeysStep,
|
purgeFeedKeysStep,
|
||||||
} from "./helpers";
|
} from "./helpers";
|
||||||
|
|
||||||
@@ -45,7 +44,12 @@ const updateFeedSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const senderFilterSchema = z.object({
|
const senderFilterSchema = z.object({
|
||||||
action: z.enum(["allow_sender", "allow_domain", "block_sender", "block_domain"]),
|
action: z.enum([
|
||||||
|
"allow_sender",
|
||||||
|
"allow_domain",
|
||||||
|
"block_sender",
|
||||||
|
"block_domain",
|
||||||
|
]),
|
||||||
value: z.string().min(1),
|
value: z.string().min(1),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -668,7 +672,9 @@ feedsRouter.post("/bulk-delete", async (c) => {
|
|||||||
|
|
||||||
const deletedFeedIds = await removeFeedsFromListBulk(emailStorage, okIds);
|
const deletedFeedIds = await removeFeedsFromListBulk(emailStorage, okIds);
|
||||||
if (deletedFeedIds.length > 0) {
|
if (deletedFeedIds.length > 0) {
|
||||||
await bumpCounters(emailStorage, { feeds_deleted: deletedFeedIds.length });
|
await bumpCounters(emailStorage, {
|
||||||
|
feeds_deleted: deletedFeedIds.length,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const removed = new Set(deletedFeedIds);
|
const removed = new Set(deletedFeedIds);
|
||||||
@@ -720,7 +726,9 @@ feedsRouter.post("/bulk-delete", async (c) => {
|
|||||||
|
|
||||||
const deletedFeedIds = await removeFeedsFromListBulk(emailStorage, okIds);
|
const deletedFeedIds = await removeFeedsFromListBulk(emailStorage, okIds);
|
||||||
if (deletedFeedIds.length > 0) {
|
if (deletedFeedIds.length > 0) {
|
||||||
await bumpCounters(emailStorage, { feeds_deleted: deletedFeedIds.length });
|
await bumpCounters(emailStorage, {
|
||||||
|
feeds_deleted: deletedFeedIds.length,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.redirect(
|
return c.redirect(
|
||||||
|
|||||||
@@ -341,21 +341,6 @@ function refreshFeedRowCache(): void {
|
|||||||
updateFeedSelectionState();
|
updateFeedSelectionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToastHandle {
|
|
||||||
update?: (msg: string, opts?: Record<string, unknown>) => void;
|
|
||||||
dismiss?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
showToast?: (msg: string, opts?: Record<string, unknown>) => ToastHandle;
|
|
||||||
parseJsonResponseOrThrow?: (
|
|
||||||
res: Response,
|
|
||||||
opts?: Record<string, unknown>,
|
|
||||||
) => Promise<Record<string, unknown>>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupFeedDeleteButtons(): void {
|
function setupFeedDeleteButtons(): void {
|
||||||
const buttons = Array.from(
|
const buttons = Array.from(
|
||||||
document.querySelectorAll<HTMLButtonElement>(
|
document.querySelectorAll<HTMLButtonElement>(
|
||||||
|
|||||||
@@ -321,21 +321,6 @@ function refreshEmailRowCache(): void {
|
|||||||
updateEmailSelectionState();
|
updateEmailSelectionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToastHandle {
|
|
||||||
update?: (msg: string, opts?: Record<string, unknown>) => void;
|
|
||||||
dismiss?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
showToast?: (msg: string, opts?: Record<string, unknown>) => ToastHandle;
|
|
||||||
parseJsonResponseOrThrow?: (
|
|
||||||
res: Response,
|
|
||||||
opts?: Record<string, unknown>,
|
|
||||||
) => Promise<Record<string, unknown>>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupEmailDeleteButtons(): void {
|
function setupEmailDeleteButtons(): void {
|
||||||
Array.from(
|
Array.from(
|
||||||
document.querySelectorAll<HTMLButtonElement>(
|
document.querySelectorAll<HTMLButtonElement>(
|
||||||
|
|||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
// Ambient declarations for browser globals injected at runtime via inline
|
||||||
|
// <script> bundles (see src/scripts/toast.ts and src/scripts/httpErrors.ts).
|
||||||
|
// Those helpers attach themselves to window rather than being importable, so the
|
||||||
|
// client TypeScript needs them declared here to type-check.
|
||||||
|
|
||||||
|
interface ToastOptions {
|
||||||
|
type?: "info" | "success" | "warning" | "error" | (string & {});
|
||||||
|
loading?: boolean;
|
||||||
|
duration?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToastHandle {
|
||||||
|
dismiss: () => void;
|
||||||
|
update: (message: string, opts?: ToastOptions) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParseJsonOptions {
|
||||||
|
prefix?: string;
|
||||||
|
allowText?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
showToast: (message: string, opts?: ToastOptions) => ToastHandle;
|
||||||
|
parseJsonResponseOrThrow: (
|
||||||
|
res: Response,
|
||||||
|
opts?: ParseJsonOptions,
|
||||||
|
) => Promise<Record<string, unknown>>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
Reference in New Issue
Block a user