From 13a36e6c4147478a7ab812c1dc6f41e27ac42923 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Sat, 21 Mar 2026 10:51:21 -0700 Subject: [PATCH] Fix prefer-add-event-listener, no-unassigned-import, require-module-specifiers Resolves all 9 remaining non-console lint warnings and promotes all three rules to errors in .oxlintrc.json. - prefer-add-event-listener: converted onchange/onclick/onload assignments to addEventListener in useDraftNotifications.ts and admin.data-sync.tsx; stored changeHandler ref for proper cleanup with removeEventListener - no-unassigned-import: configured rule with allow list for legitimate side-effect imports (*.css, @testing-library/jest-dom, @testing-library/cypress/add-commands) - require-module-specifiers: removed redundant `export {}` from cypress/support/e2e.ts (file already has an import) Co-Authored-By: Claude Sonnet 4.6 --- .oxlintrc.json | 7 ++++++- app/hooks/useDraftNotifications.ts | 10 ++++++---- app/routes/admin.data-sync.tsx | 4 ++-- cypress/support/e2e.ts | 1 - 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index aae4b03..4417162 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -37,9 +37,14 @@ "react/self-closing-comp": "warn", "import/no-duplicates": "error", + "import/no-unassigned-import": ["error", { + "allow": ["**/*.css", "@testing-library/jest-dom", "@testing-library/cypress/add-commands"] + }], "no-shadow": "error", - "unicorn/consistent-function-scoping": "error" + "unicorn/consistent-function-scoping": "error", + "unicorn/prefer-add-event-listener": "error", + "unicorn/require-module-specifiers": "error" }, "overrides": [ { diff --git a/app/hooks/useDraftNotifications.ts b/app/hooks/useDraftNotifications.ts index 7952c81..036eea0 100644 --- a/app/hooks/useDraftNotifications.ts +++ b/app/hooks/useDraftNotifications.ts @@ -44,18 +44,20 @@ export function useDraftNotifications(seasonId: string, userId: string) { // would still be null at that point without the flag). let aborted = false; let permissionStatus: PermissionStatus | null = null; + let changeHandler: (() => void) | null = null; navigator.permissions .query({ name: "notifications" }) .then((status) => { if (aborted) return; permissionStatus = status; - status.onchange = () => { + changeHandler = () => { setPermissionState(status.state as NotificationPermission); // If permission was revoked, disable notifications if (status.state !== "granted") { setEnabledState(false); } }; + status.addEventListener("change", changeHandler); }) .catch(() => { // Permissions API not available in all environments; silently ignore @@ -63,8 +65,8 @@ export function useDraftNotifications(seasonId: string, userId: string) { return () => { aborted = true; - if (permissionStatus) { - permissionStatus.onchange = null; + if (permissionStatus && changeHandler) { + permissionStatus.removeEventListener("change", changeHandler); } }; }, [seasonId, userId]); @@ -120,7 +122,7 @@ export function useDraftNotifications(seasonId: string, userId: string) { body, tag: `draft-${seasonId}`, }); - n.onclick = () => window.focus(); + n.addEventListener("click", () => window.focus()); }, [enabled, seasonId] ); diff --git a/app/routes/admin.data-sync.tsx b/app/routes/admin.data-sync.tsx index 5cd41b7..69557e8 100644 --- a/app/routes/admin.data-sync.tsx +++ b/app/routes/admin.data-sync.tsx @@ -139,14 +139,14 @@ export default function DataSync({ loaderData }: Route.ComponentProps) { } const reader = new FileReader(); - reader.onload = (event) => { + reader.addEventListener("load", (event) => { const fileData = event.target?.result as string; const formData = new FormData(); formData.append("intent", "import"); formData.append("mode", importMode); formData.append("fileData", fileData); fetcher.submit(formData, { method: "POST" }); - }; + }); reader.readAsText(selectedFile); }; diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index e8d4e2d..beb10f7 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -19,4 +19,3 @@ declare global { } } -export {};