Files
marketplaces/docs/PERFORMANCE_REPORT.md
sdarbinyan 3253b297eb docs: add PERFORMANCE_REPORT.md for RC PERF-01
Consolidates the 3 perf-audit commits (reactivity/change-detection,
bundles/lazy-loading/tree-shaking, assets) into one report: headline
1.47MB->1.12MB initial bundle (-24%), plus per-area findings and a
remaining-work list (primeng/primeicons still in package.json pending
a blocked npm uninstall, large lazy chunks, combineLatest sites).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 08:28:00 +04:00

7.7 KiB
Raw Blame History

Performance Report — RC PERF-01

Production-readiness performance audit + fixes, application-wide. No redesign, no UI/visual changes, no route/API changes — pure performance/correctness work. Three sequential commits on branch B2B (not pushed):

Commit Scope
61a5714 Angular reactivity, change detection, rendering, memory
4bf0666 Bundles, lazy-loading, tree-shaking, large chunks
2e931bb Assets: images, fonts, SVG, CSS

npm run build verified green after every commit. npx tsc --noEmit clean throughout.

Headline result

Initial bundle: 1.47 MB → 1.12 MB raw (350 KB / 24%), transfer estimate 263.59 kB → 221.51 kB (16%). Budget overage shrank from 769 KB over to 417 KB over (still a warning, not an error — the 700 KB budget itself is unchanged and still exceeded, see Remaining below). Global CSS bundle: 9.60 kB → 8.41 kB raw (12%).

Findings and fixes by area

Angular reactivity / change detection / memory (61a5714)

  • .subscribe() audit: 149 call sites across 44 files. Spot-checked every long-lived stream in the highest-risk components (route params, router events, polling/intervals) — all already use takeUntilDestroyed(this.destroyRef) or an explicit Subscription + ngOnDestroy unsubscribe. One-shot HTTP-backed subscribes self-complete, no leak. No leaks found — the codebase's existing discipline here was already solid.
  • ChangeDetectionStrategy.OnPush: 191 components, 190 already had it. Added to the 1 remaining (app.ts, the root component) — verified safe (signal-driven template, no direct DOM mutation).
  • @for/track: fully compliant app-wide — the codebase is entirely on the new control-flow syntax, which the compiler requires track on, so there was nothing to fix. Legacy *ngFor without trackBy: zero occurrences.
  • Duplicate HTTP calls: CategoryFacade and ConfigService already cache via shareReplay({bufferSize:1, refCount:true}), applied consistently — no duplicate-request pattern found.
  • Flagged, not fixed: 2 combineLatest sites (catalog-container.component.ts, subcategories.component.ts) are narrow, already-minimal RxJS composition — converting to computed() would touch adjoining facade state shape, judged out of this pass's risk budget. Per-item template method calls in @for loops (getDiscountedPrice, itemName, categoryName, stockLabelKey) are cheap pure lookups on an already-OnPush host — flagged as pipe-conversion candidates for a future pass, not urgent.

Bundles / lazy-loading / tree-shaking (4bf0666)

  • Lazy-loading boundaries: already correct — every storefront/builder/backoffice route in app.routes.ts uses loadComponent, nothing eagerly imported.
  • i18n eager-loading — the biggest single win: TranslateService statically imported all three locale packs (ru/en/hy, 346 KB raw combined) into the initial bundle regardless of visitor language. Fixed: ru (default) stays eager, en/hy are now import()ed on demand via a new preloadLanguage(), awaited inside languageGuard before route activation — router blocks navigation until the pack resolves, so there's no untranslated-flash risk.
  • Dead code: knip found src/app/components/items-carousel/* (3 files) completely unreferenced anywhere, and the sole consumer of the primeng dependency. Deleted the component and its now-dead primeicons.css import from src/styles.scss.
  • Tree-shaking: fixed one accidental barrel import in widget-host.service.ts (now imports UnknownWidgetComponent directly instead of through the widgets/ui barrel) — negligible measured impact, but correct going forward. @lucide/angular icons already use named tree-shakeable imports.
  • Duplicate deps: npm ls confirms a single deduped rxjs@7.8.2 — no duplicate module instances.
  • Flagged, not fixed: primeng/primeicons remain in package.json/lockfile — npm install/uninstall fails in this environment (ETARGET) on a pre-existing, unrelated broken barry-cache devDependency, so the lockfile couldn't be safely regenerated by hand. Follow-up needed: fix the barry-cache devDependency resolution, then npm uninstall primeng primeicons to drop the dependency entirely (the code reference is already gone). Large chunks (project-editor 320 kB, catalog-container 126 kB, product-details-container 88 kB, cart 61 kB) unchanged — no mechanical split found inside their scope; a real reduction here would mean lazy-loading a sub-feature (e.g. an embedded editor), which is a larger, dedicated task.

Assets: images, fonts, SVG, CSS (2e931bb)

  • Images: storefront <img> tags already have loading="lazy"/decoding="async" on grids/galleries and CLS is already handled via CSS aspect-ratio (from prior RC-Visual-02/RC-Premium-01 passes) — nothing to fix. Flagged: a handful of single-image admin/editor previews lack loading="lazy" (page-editor, brand-overview, asset-details-drawer) — negligible traffic, left alone.
  • Fonts: index.html already preconnects to Google Fonts, loads DM Sans 400/500/600/700 with display=swap; all 4 loaded weights are actually used. Flagged: weights 800/900 are referenced in some component SCSS but never loaded (pre-existing faux-bold rendering) — changing loaded weights risks a visible text difference, left out of scope for a perf-only pass.
  • SVG: icon system is fully centralized through @lucide/angular via icon-registry.ts — no inline path duplication. public/ SVGs checked for editor cruft (metadata/inkscape artifacts) — already clean. Flagged: 5 branding SVGs (mastercard-logo.min.svg, dexar-logo*.svg, dexar-favicon.svg, novo-logo.svg, novo-favicon.svg) appear unreferenced in src, left in place — may back tenant branding resolved from the backend at runtime, deletion needs a human decision, not this pass's call.
  • CSS: manual grep-based cross-check (literal + hyphen-safe, checked for dynamic [class.x]/string-built classes too) of src/styles.scss utility classes against all templates. Confirmed-dead and removed: .btn-primary/.btn-secondary+hover, .catalog-product-card, .item-badges-overlay, .item-simple-desc, .text-center, .mt-1..4, .mb-1..4, .p-1..4.

Remaining recommendations (not applied — flagged for follow-up)

  1. primeng/primeicons still in package.json despite the only consumer being deleted — needs the pre-existing barry-cache devDependency resolution issue fixed first, then a clean npm uninstall. Highest-value remaining action: would likely close most of the remaining 417 KB budget overage in one move once confirmed.
  2. Large lazy chunks (project-editor 320 kB, catalog-container 126 kB) — no mechanical fix found; needs a dedicated task to identify a genuinely lazy-loadable sub-feature inside each.
  3. 2 combineLatest sites — safe to leave, candidates for a computed() conversion in a future facade-focused pass.
  4. Per-item template method calls in @for loops — pipe-conversion candidates, not urgent (already bounded by OnPush).
  5. Font weights 800/900 used in SCSS but not loaded — either load them explicitly or normalize the SCSS to a loaded weight; a design-system call, not a perf call.
  6. 5 unreferenced branding SVGs — confirm with backend/tenant-branding owner before deleting.
  7. Admin/editor preview images missing loading="lazy" — low priority, low traffic.

Verification

tsc --noEmit and npm run build run and green after each of the three commits. No live browser session available this session — real-world Core Web Vitals (LCP/CLS/INP) were not measured; recommend a Lighthouse/WebPageTest pass against a deployed build before the client demo referenced in docs/FRONTEND-ROADMAP.md.