Frontend: bundle vendor-split (221 KB gz, ~29 KB headroom) + fill unit-test gaps #67

Closed
opened 2026-06-08 13:42:27 +00:00 by logaritmisk · 1 comment
Owner

Severity: Medium. From a frontend deep audit, 2026-06-08. Performance headroom + a few targeted test gaps. The suite is otherwise healthy (175 tests, 0 skipped, strong selectors, parity test).

Problems

  • [Med] Single ~221 KB gz main chunk, only ~29 KB under the 250 KB budget, and vite build already prints the >500 KB raw warning. vite.config.ts has no build.rollupOptions.output.manualChunks. The chunk is framework code (react-dom + react-router 7 + @tanstack/react-query + @base-ui/react + i18next + both locales) that rarely changes but invalidates on every app edit.
  • [Med] components/delete-confirm-dialog.tsx — the delete-in-use flow is untested at the UI layer. InUseError → t("actions.inUse", {count}) (:40) is only covered at the query layer (api/queries.test.ts); the dialog's rendering of the in-use message + blocked-delete branch has no .test.tsx.
  • [Med] objects/prune-fields.ts — non-trivial pure transform tested only indirectly via object-form.test.tsx (localized single-lang filtering, empty/null pruning, nested maps). Ideal direct-unit-test candidate.
  • [Low] lib/labels.ts + lib/format-date.ts — widely-used pure functions (label-fallback resolution, locale-aware date formatting) with no direct test.
  • [Low] Story gapscombobox (composed interactive primitive) lacks a visual/a11y story; also alert-dialog, drawer, label, skeleton, card.

Suggested fix

  • Add a manualChunks vendor split (e.g. react/react-dom/router together, @base-ui separately) — buys budget headroom + long-term cache stability for the cheapest effort.
  • Add delete-confirm-dialog.test.tsx (InUseError path), prune-fields.test.ts, and direct tests for labels/format-date.
  • Add a combobox story.

Source: frontend deep audit (perf/testing dimension), 2026-06-08. Verified-good (no action): tree-shakeable lucide-react, heavy object-form already code-split, native Intl (no heavy date lib).

**Severity: Medium.** _From a frontend deep audit, 2026-06-08. Performance headroom + a few targeted test gaps. The suite is otherwise healthy (175 tests, 0 skipped, strong selectors, parity test)._ ## Problems - **[Med] Single ~221 KB gz main chunk, only ~29 KB under the 250 KB budget**, and `vite build` already prints the >500 KB raw warning. `vite.config.ts` has no `build.rollupOptions.output.manualChunks`. The chunk is framework code (react-dom + react-router 7 + @tanstack/react-query + @base-ui/react + i18next + both locales) that rarely changes but invalidates on every app edit. - **[Med] `components/delete-confirm-dialog.tsx` — the delete-in-use flow is untested at the UI layer.** `InUseError → t("actions.inUse", {count})` (`:40`) is only covered at the query layer (`api/queries.test.ts`); the dialog's rendering of the in-use message + blocked-delete branch has no `.test.tsx`. - **[Med] `objects/prune-fields.ts` — non-trivial pure transform tested only indirectly** via `object-form.test.tsx` (localized single-lang filtering, empty/null pruning, nested maps). Ideal direct-unit-test candidate. - **[Low] `lib/labels.ts` + `lib/format-date.ts`** — widely-used pure functions (label-fallback resolution, locale-aware date formatting) with no direct test. - **[Low] Story gaps** — `combobox` (composed interactive primitive) lacks a visual/a11y story; also `alert-dialog`, `drawer`, `label`, `skeleton`, `card`. ## Suggested fix - Add a `manualChunks` vendor split (e.g. react/react-dom/router together, @base-ui separately) — buys budget headroom + long-term cache stability for the cheapest effort. - Add `delete-confirm-dialog.test.tsx` (InUseError path), `prune-fields.test.ts`, and direct tests for `labels`/`format-date`. - Add a `combobox` story. _Source: frontend deep audit (perf/testing dimension), 2026-06-08. Verified-good (no action): tree-shakeable lucide-react, heavy object-form already code-split, native Intl (no heavy date lib)._
Author
Owner

Done in merge 390f689.

Vendor split (vite.config.tsbuild.rollupOptions.output.manualChunks): framework deps now split into cache-stable chunks — react (react + react-dom + react-router + react-router-dom, one shared instance), base-ui, query, i18n — and the app entry chunk carries only app code. Largest chunk dropped 216.5 → 90.2 KB gz (budget 250). Note: used the function form of manualChunks (matches pnpm's .pnpm/<pkg>@… layout); the object form is unbuildable here because bare specifiers like react-router aren't root-resolvable under pnpm's strict store.

Test-gap fills: prune-fields.test.ts (localized single-lang + empty-pruning branches), lib/labels.test.ts (fallback chain), lib/format-date.test.ts (locale format + no tz day-shift + edges), components/delete-confirm-dialog.test.tsx (the delete-in-use flow: InUseError(3)actions.inUse count, dialog stays open; clean confirm closes). Plus a combobox Storybook story.

283 tests pass (existing unchanged); typecheck/lint/build clean; check:size 90.2 KB gz; check:colors clean; no new dependency; no new i18n keys; no codename.

Out of scope → follow-up: the buildkit/Dockerfile CI migration (the robust fix for the resource-starved native runner; overlaps #25).

Done in merge `390f689`. **Vendor split** (`vite.config.ts` → `build.rollupOptions.output.manualChunks`): framework deps now split into cache-stable chunks — `react` (react + react-dom + react-router + react-router-dom, one shared instance), `base-ui`, `query`, `i18n` — and the app entry chunk carries only app code. **Largest chunk dropped 216.5 → 90.2 KB gz** (budget 250). Note: used the *function form* of `manualChunks` (matches pnpm's `.pnpm/<pkg>@…` layout); the object form is unbuildable here because bare specifiers like `react-router` aren't root-resolvable under pnpm's strict store. **Test-gap fills:** `prune-fields.test.ts` (localized single-lang + empty-pruning branches), `lib/labels.test.ts` (fallback chain), `lib/format-date.test.ts` (locale format + no tz day-shift + edges), `components/delete-confirm-dialog.test.tsx` (the delete-in-use flow: `InUseError(3)` → `actions.inUse` count, dialog stays open; clean confirm closes). Plus a `combobox` Storybook story. 283 tests pass (existing unchanged); typecheck/lint/build clean; check:size 90.2 KB gz; check:colors clean; no new dependency; no new i18n keys; no codename. **Out of scope → follow-up:** the buildkit/Dockerfile CI migration (the robust fix for the resource-starved native runner; overlaps #25).
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logaritmisk/biggus-dickus#67