mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
perf(invoices): lazy-load pdf-lib + swissqrbill, -516 KB on route
/(app)/invoices/[id] route bundle drops from **534 KB → 18.6 KB** by
moving PDF rendering behind dynamic imports.
Changes:
- views/DetailView.svelte: `await import('../pdf/renderer')` inside
renderPdf() + downloadPdf(), cached in a module-local ref.
- components/SendModal.svelte: same for openAndDownload().
- pdf/scor.ts (new): generateSCORReference extracted so the
invoices store can derive a reference string without pulling
swissqrbill/svg + pdf-lib into the list-view bundle.
- pdf/qr-bill.ts: re-exports generateSCORReference from scor.ts
for backward compatibility.
- stores/invoices.svelte.ts: imports from ../pdf/scor (light) instead
of ../pdf/qr-bill (heavy).
- index.ts: drop re-export of the PDF renderer from the module
barrel so `import ... from '$lib/modules/invoices'` never drags
pdf-lib in.
The heavy chunk (pdf-lib + swissqrbill, ~576 KB) now only loads when
a user actually opens an invoice detail — list views, create flow, and
all other routes stay lean.
20/20 qr-bill tests pass; svelte-check clean.
Bonus: scripts/audit-icon-usage.mjs (+ pnpm run audit:icon-usage)
audits @mana/shared-icons imports. Reveals 204 distinct icons across
the codebase, 199 of them at default weight but paying for all 6
Phosphor weights. Biggest offender: app-registry/apps.ts with 69
static icon imports accounting for ~290 KB of the shared 466 KB icon
chunk. Migration path for that is documented in
docs/optimizable/bundle-analysis.md §2 — next session's work.
docs/optimizable/bundle-analysis.md also updated with the root (app)
layout (260 KB) investigation notes (start/stop lifecycle hooks to
defer via idleCallback).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
596e5a7424
commit
4d5a96e21b
9 changed files with 250 additions and 46 deletions
|
|
@ -45,23 +45,57 @@ Routes loaded per navigation (not eagerly):
|
|||
|
||||
## Priority improvements
|
||||
|
||||
1. **`/invoices/[id]` code-split** — 534 KB is large. `swissqrbill`
|
||||
and `pdf-lib` are probably both eagerly imported. Wrap the PDF/QR
|
||||
generation path with `await import('swissqrbill')` so the route
|
||||
bundle drops to ~150 KB for the list/display case. Real win for
|
||||
Swiss-bill users on 3G / slow laptops.
|
||||
### 1. `/invoices/[id]` code-split — ✅ **SHIPPED 2026-04-22**
|
||||
|
||||
2. **`@mana/shared-icons` chunking** — 317 + 149 KB of Phosphor SVG
|
||||
paths across two chunks. Phosphor doesn't tree-shake well because
|
||||
its icons are named exports of a single module. Either:
|
||||
- migrate to tree-shakable per-icon imports, OR
|
||||
- lazy-load rarely-used icons in the module that imports them
|
||||
instead of the shared chunk.
|
||||
**Before:** 534 KB route bundle. **After:** 18.6 KB.
|
||||
|
||||
3. **Root `(app)` layout (260 KB)** — on the high side for "just the
|
||||
shell". Investigate whether any module-specific stores / AI pipeline
|
||||
bits are leaking into the shared layout when they could be
|
||||
route-scoped.
|
||||
`DetailView.svelte` + `SendModal.svelte` now import `./pdf/renderer`
|
||||
dynamically (`await import(...)`) so pdf-lib + swissqrbill/svg (~576 KB
|
||||
combined) move into a separate chunk that only loads when the user
|
||||
actually opens an invoice detail. Also split `generateSCORReference`
|
||||
into its own `./pdf/scor.ts` so the invoices store can compute a
|
||||
reference on create without pulling the heavy renderer graph.
|
||||
|
||||
### 2. `@mana/shared-icons` — **OPEN**
|
||||
|
||||
466 KB of Phosphor SVG paths across 2 chunks. Root cause from
|
||||
`audit:icon-usage` report (2026-04-22):
|
||||
|
||||
- **204 distinct icons** imported across the codebase.
|
||||
- **199 use the default "regular" weight** — but Phosphor ships all
|
||||
6 weights per icon regardless.
|
||||
- Single worst offender: `app-registry/apps.ts` imports **69 icons**
|
||||
in one file (the module-name → icon-component map), pulled into the
|
||||
shared layout chunk → 69 × 6 weights × ~0.7 KB ≈ 290 KB on every
|
||||
cold load.
|
||||
|
||||
**Migration paths** (pick one, sized to follow-up sessions):
|
||||
|
||||
1. Rewrite `app-registry/apps.ts` so each module's icon is a string
|
||||
name, with a lazy `getIconComponent(name)` helper backed by
|
||||
per-path dynamic imports (`() => import('phosphor-svelte/House')`).
|
||||
Saves ~290 KB from the initial layout chunk. Biggest single win.
|
||||
2. Drop `export * from 'phosphor-svelte'` in
|
||||
`packages/shared-icons/src/index.ts` and re-export only the 204
|
||||
icons actually used. Defends against future barrel-broadening.
|
||||
3. Longest-term: build a custom icon set that only ships the weights
|
||||
actually used (most icons only need "regular").
|
||||
|
||||
Run `pnpm run audit:icon-usage --top 30` for the current inventory.
|
||||
|
||||
### 3. Root `(app)` layout (260 KB) — **OPEN**
|
||||
|
||||
`routes/(app)/+layout.svelte` statically imports ~15 start/stop
|
||||
lifecycle hooks (mission tick, server-iteration executor, event store,
|
||||
event bridge, streak tracker, goal tracker, byok init, tools init,
|
||||
articles-from-news migration, reminder scheduler, llm queue). Each
|
||||
pulls its own dependency graph into the shared layout chunk.
|
||||
|
||||
**Recommended approach:** wrap the non-critical ones in `queueMicrotask`
|
||||
or `requestIdleCallback`-deferred dynamic imports — the layout finishes
|
||||
hydrating, then the heavy lifecycle code streams in. The one-shot
|
||||
`runArticlesFromNewsMigration` in particular is a prime candidate since
|
||||
it's executed only once per user per session.
|
||||
|
||||
## What's already good
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue