From 5b18db3a77e4fc0075a08b1180572925739f1922 Mon Sep 17 00:00:00 2001 From: soubarna Date: Mon, 5 Oct 2026 17:29:03 +0530 Subject: [PATCH] fix: eslint findings Remove dead code and unused imports, drop useless assignments, and tune rules in eslint.config.js (underscore-prefixed unused names, intentional BOM/NBSP, React Compiler rules from eslint-plugin-react-hooks 7 as warnings). --- eslint.config.js | 29 ++++++++++++++++++++++++ scripts/decor/script-word.mjs | 1 - src/lib/selfTestE2e.ts | 3 +-- src/pdf/model/text.ts | 1 + src/pdf/quirks.test.tsx | 2 +- src/pdf/templates/band/plan.ts | 2 -- src/pdf/templates/harness.test.tsx | 12 ++-------- src/pdf/templates/highlighter/Layout.tsx | 2 +- src/pdf/templates/marble/Layout.tsx | 2 +- src/pdf/templates/marble/plan.ts | 2 +- src/pdf/templates/neutral/plan.ts | 2 +- src/selftestBoot.ts | 2 +- src/views/AppSettings.tsx | 1 - src/views/NewInvoice.tsx | 8 +------ src/views/SeriesSettings.tsx | 2 +- 15 files changed, 41 insertions(+), 30 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 9d5000a..0f7fa9d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -10,6 +10,35 @@ export default tseslint.config( js.configs.recommended, tseslint.configs.recommended, reactHooks.configs.flat.recommended, + { + rules: { + // Leading underscore marks an intentionally unused binding (omitting a key via rest, unused + // override parameters). + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_", ignoreRestSiblings: true }, + ], + // The BOM and NBSP are written literally on purpose in the CSV exporters and the text sanitiser. + "no-irregular-whitespace": ["error", { skipStrings: true, skipTemplates: true, skipRegExps: true }], + }, + }, + { + // The React Compiler rules shipped in eslint-plugin-react-hooks 7 flag long-standing patterns + // here (load data on mount, "latest value" refs written during render). They are legitimate + // design feedback but not bugs, and rewriting them blind risks behaviour changes in an + // effect-heavy app, so they stay visible as warnings until each site is reworked. + files: ["src/**/*.{ts,tsx}"], + rules: { + "react-hooks/set-state-in-effect": "warn", + "react-hooks/refs": "warn", + "react-hooks/immutability": "warn", + }, + }, + { + // Test helper that reads react-pdf's untyped internal layout data. + files: ["src/pdf/testing/**/*.ts"], + rules: { "@typescript-eslint/no-explicit-any": "warn" }, + }, { files: ["src/**/*.{ts,tsx}"], languageOptions: { globals: globals.browser }, diff --git a/scripts/decor/script-word.mjs b/scripts/decor/script-word.mjs index 783a127..92d8a78 100644 --- a/scripts/decor/script-word.mjs +++ b/scripts/decor/script-word.mjs @@ -52,7 +52,6 @@ function wordCommands(font) { function rotate(cmds) { const pts = cmds.flatMap((c) => c.args); const xs = pts.filter((_, i) => i % 2 === 0); - const ys = pts.filter((_, i) => i % 2 === 1); const inkLen = Math.max(...xs) - Math.min(...xs); const s = INK_LENGTH / inkLen; const map = (x, y) => [-y * s, -x * s]; diff --git a/src/lib/selfTestE2e.ts b/src/lib/selfTestE2e.ts index f9954a8..5631c93 100644 --- a/src/lib/selfTestE2e.ts +++ b/src/lib/selfTestE2e.ts @@ -102,7 +102,6 @@ export async function runE2eSteps(log: StepLog, reportPath: string): Promise { const outcome = await issueAndArchive(input, prefs, deps); invoice = outcome.invoice; @@ -112,7 +111,7 @@ export async function runE2eSteps(log: StepLog, reportPath: string): Promise { const st = await api.archiveStatus(inv.id); diff --git a/src/pdf/model/text.ts b/src/pdf/model/text.ts index 7200fa4..42cd66c 100644 --- a/src/pdf/model/text.ts +++ b/src/pdf/model/text.ts @@ -22,6 +22,7 @@ export function sanitize(value: string | null | undefined): string { .replace(/\r\n?/g, "\n") .replace(/[\t\n\v\f]/g, " ") .replace(/ /g, " ") + // eslint-disable-next-line no-control-regex -- stripping control characters is the point .replace(/[\u0000-\u001F\u007F-\u009F​]/g, "") .replace(/ {2,}/g, " ") .trim(); diff --git a/src/pdf/quirks.test.tsx b/src/pdf/quirks.test.tsx index 64f8377..03ec85b 100644 --- a/src/pdf/quirks.test.tsx +++ b/src/pdf/quirks.test.tsx @@ -21,7 +21,7 @@ import { RenderTxt } from "./blocks/RenderTxt"; import { TableGuard, guardPresence } from "./blocks/TableGuard"; import { renderCore } from "./render/core"; import { loadCanvas, inkPixels } from "./testing/rasterize"; -import { byId, doc, FAMILY, find, frameFor, PAD, PAGE_W, rawLayout, txt } from "./testing/docs"; +import { byId, doc, FAMILY, find, frameFor, PAD, rawLayout, txt } from "./testing/docs"; import { FONTS_DIR, setupPdfTest } from "./testing/setup"; import { stack } from "./fonts/register"; diff --git a/src/pdf/templates/band/plan.ts b/src/pdf/templates/band/plan.ts index 6354a06..60f3bbf 100644 --- a/src/pdf/templates/band/plan.ts +++ b/src/pdf/templates/band/plan.ts @@ -15,8 +15,6 @@ export const REVERSE_GAP = 4; export const HEADING_AFTER = 4; /** Space between the closing block's two columns. */ export const CLOSING_COLUMN_GAP = 16; -/** Slate: space between a mark and the business name in the dark band. */ -const NAME_AFTER_LOGO = 7.8; /** Space above and below the thin rule that separates the stack from TOTAL. */ export const STACK_RULE = { before: 2, after: 8, thickness: 0.75 }; const MIN_NAME_W = 40; diff --git a/src/pdf/templates/harness.test.tsx b/src/pdf/templates/harness.test.tsx index 39a0135..b718823 100644 --- a/src/pdf/templates/harness.test.tsx +++ b/src/pdf/templates/harness.test.tsx @@ -1,17 +1,9 @@ import { mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import type { AuditIssue } from "../engine/audit"; -import { PAGE_SIZES } from "../engine/geometry"; -import { PAGE_LABEL_ID, TABLE_HEAD_ID, TABLE_ROW_PREFIX, CLOSING_ID, TOTALS_ID } from "../engine/ids"; -import { walk, type LayoutNode, type LayoutPage, type Rect } from "../engine/layoutTree"; -import { forbiddenProbe, SLOT_RULES, slotContent, type SlotName } from "../model/slots"; -import { frameForPrefs, renderInvoicePdf } from "../render/invoice"; import { loadCanvas, pagePng } from "../testing/rasterize"; -import { SLOT_ID } from "../blocks/slots"; -import { META_ID, SUPPLIER_ID, TITLE_ID } from "./classic/ids"; -import { HEADER_GAP, headerCols } from "./classic/plan"; -import { FIXTURE_GSTIN, FIXTURES, buildFixture, ensureSetup, makeItems, PAGE_SIZE_IDS, prefsFor, renderFixture, type FixtureSpec, type Rendered } from "./harness"; +import { renderInvoicePdf } from "../render/invoice"; +import { FIXTURES, buildFixture, ensureSetup, PAGE_SIZE_IDS, prefsFor, renderFixture, type FixtureSpec } from "./harness"; import { TEMPLATES } from "./registry"; /** diff --git a/src/pdf/templates/highlighter/Layout.tsx b/src/pdf/templates/highlighter/Layout.tsx index 32e1245..79ec845 100644 --- a/src/pdf/templates/highlighter/Layout.tsx +++ b/src/pdf/templates/highlighter/Layout.tsx @@ -1,6 +1,6 @@ import { Image, View } from "@react-pdf/renderer"; import type { ReactNode } from "react"; -import { colsDetailed, pinned } from "../../engine/columns"; +import { pinned } from "../../engine/columns"; import { CLOSING_ID, TABLE_HEAD_ID, TABLE_ROW_PREFIX, TOTALS_ID } from "../../engine/ids"; import { AvailableWidthProvider } from "../../blocks/AvailableWidth"; import { BottomSpacer } from "../../blocks/BottomSpacer"; diff --git a/src/pdf/templates/marble/Layout.tsx b/src/pdf/templates/marble/Layout.tsx index 67e0b72..f3663c3 100644 --- a/src/pdf/templates/marble/Layout.tsx +++ b/src/pdf/templates/marble/Layout.tsx @@ -202,7 +202,7 @@ function Parties({ ctx }: { ctx: Ctx }) { const blocks: ReactNode[] = []; let k = 1; if (plan.info.hasBank) blocks.push(); - if (plan.info.hasIds) blocks.push(); + if (plan.info.hasIds) blocks.push(); const specs: ColSpec[] = cols.map((w, i) => (i === cols.length - 1 ? { fill: true } : { fixed: w })); return ( diff --git a/src/pdf/templates/marble/plan.ts b/src/pdf/templates/marble/plan.ts index 631023c..aadeafa 100644 --- a/src/pdf/templates/marble/plan.ts +++ b/src/pdf/templates/marble/plan.ts @@ -151,7 +151,7 @@ export function planMarble(model: RenderModel, frame: PageFrame, v: MarbleVarian let k = 0; const clientW = textW(k++); const bankW = hasBank ? textW(k++) : 0; - const idsW = hasIds ? textW(k++) : 0; + const idsW = hasIds ? textW(k) : 0; const head = leading(r.heading) + v.parties.headingGap; let clientH = head + textHeight(r.body, c.name, clientW); for (const l of c.addressLines) clientH += textHeight(r.body, l, clientW); diff --git a/src/pdf/templates/neutral/plan.ts b/src/pdf/templates/neutral/plan.ts index bcd7c26..5f8870f 100644 --- a/src/pdf/templates/neutral/plan.ts +++ b/src/pdf/templates/neutral/plan.ts @@ -296,7 +296,7 @@ export function planNeutral(model: RenderModel, frame: PageFrame, v: NeutralVari for (const l of lines) payH += textHeight(r.address, l, payW); } if (v.id === "serenity") signH += leading(r.label) + 4; - let closingH = 0; + let closingH: number; if (v.payTo.where === "closing") closingH = Math.max(payH, signH); else closingH = signH; if (model.cancelled) closingH += 10 + textHeight(r.small, CANCELLED_SENTENCE, W); diff --git a/src/selftestBoot.ts b/src/selftestBoot.ts index f9cb5c6..dfa7aec 100644 --- a/src/selftestBoot.ts +++ b/src/selftestBoot.ts @@ -11,7 +11,7 @@ import type { StepResult } from "./lib/selfTestSteps"; const WATCHDOG_MS = 120_000; export async function maybeRunSelfTest(): Promise { - let reportPath: string | null = null; + let reportPath: string | null; try { reportPath = await invoke("selftest_config"); } catch { diff --git a/src/views/AppSettings.tsx b/src/views/AppSettings.tsx index 372170d..53a0a72 100644 --- a/src/views/AppSettings.tsx +++ b/src/views/AppSettings.tsx @@ -24,7 +24,6 @@ import { TabPanels, Tabs, Tile, - Toggle, } from "@carbon/react"; import { Add, Save, TrashCan } from "@carbon/icons-react"; import { confirm } from "@tauri-apps/plugin-dialog"; diff --git a/src/views/NewInvoice.tsx b/src/views/NewInvoice.tsx index 9469253..2936d79 100644 --- a/src/views/NewInvoice.tsx +++ b/src/views/NewInvoice.tsx @@ -48,8 +48,7 @@ import type { Settings, TaxType, } from "../lib/types"; -import { computeLineAmount, computeTotals } from "../lib/invoice"; -import { amountInWords } from "../lib/numberToWords"; +import { computeLineAmount } from "../lib/invoice"; import { gstinFullError } from "../lib/validators"; import { addDays, formatAmount, todayIso } from "../lib/format"; import { modelFromAssets, prefsForInvoice } from "../lib/pdf"; @@ -256,11 +255,6 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv ? "cgst_sgst" : "igst"; const clientGstinError = gstinFullError(clientGstin); - const totals = useMemo( - () => computeTotals(items, discount, effectiveTaxType, taxRate), - [items, discount, effectiveTaxType, taxRate], - ); - const words = useMemo(() => amountInWords(totals.total), [totals.total]); const bank = useMemo(() => banks.find((b) => b.id === bankId) ?? null, [banks, bankId]); const payload = useMemo( diff --git a/src/views/SeriesSettings.tsx b/src/views/SeriesSettings.tsx index b25bd98..2b9b7d0 100644 --- a/src/views/SeriesSettings.tsx +++ b/src/views/SeriesSettings.tsx @@ -29,7 +29,7 @@ export default function SeriesSettings({ embedded?: boolean; }) { const toast = useToast(); - const [active, setActive] = useState(null); + const [, setActive] = useState(null); const [all, setAll] = useState([]); const [loading, setLoading] = useState(true);