Fantasy-Map-Generator/_bmad-output/implementation-artifacts/3-2-bundle-size-audit.md
Azgaar a285d450c8 feat: refactor draw-relief-icons renderer to utilize WebGL2LayerFramework
- Removed global renderer, camera, and scene management in favor of layer framework integration.
- Implemented terrain layer registration with setup, render, and dispose methods.
- Enhanced texture loading and caching mechanisms.
- Updated geometry building to return Mesh objects directly.
- Added performance benchmarking story for render performance validation.
- Created bundle size audit story to ensure effective tree-shaking and size constraints.
2026-03-12 15:04:37 +01:00

11 KiB

Story 3.2: Bundle Size Audit

Status: backlog Epic: 3 — Quality & Bundle Integrity Story Key: 3-2-bundle-size-audit Created: 2026-03-12 Developer: unassigned


Story

As a developer, I want the Vite production bundle analyzed to confirm Three.js tree-shaking is effective and the total bundle size increase is within budget, So that the feature does not negatively impact page load performance.


Acceptance Criteria

AC1: Three.js named imports only (NFR-B1) Given webgl-layer-framework.ts and draw-relief-icons.ts source is inspected When Three.js import statements are reviewed Then no import * as THREE from 'three' exists in any src/**/*.ts file — all imports are named

AC2: Bundle size increase ≤50KB gzipped (NFR-B2) Given the bundle size before and after the feature is compared When gzip sizes are measured from npm run build output Then the total bundle size increase from this feature's new code is ≤50KB gzipped

AC3: Tree-shaking verification Given vite build is run with the complete implementation When the bundle is analyzed with rollup-plugin-visualizer or npx vite-bundle-visualizer Then only the required Three.js classes are included in the bundle (no full THREE namespace)

AC4: Named imports enumerated and verified Given the final implementation When all Three.js named imports in the project are listed Then the set matches the declared architecture list: WebGLRenderer, Scene, OrthographicCamera, Group, BufferGeometry, BufferAttribute, Mesh, MeshBasicMaterial, TextureLoader, SRGBColorSpace, LinearMipmapLinearFilter, LinearFilter, DoubleSide

AC5: Results documented Given the bundle audit completes When results are captured Then actual gzip delta is recorded in this story's Dev Agent Record and compared to the 50KB budget


Context

What This Story Is

This is a build analysis and documentation story. Run npm run build, inspect the output, verify tree-shaking, calculate the gzip size delta vs. the baseline (pre-feature), and document findings.

Key architectural note: Three.js is already a project dependency for the globe view (public/libs/three.min.js — pre-existing). The new WebGL relief feature adds TypeScript-side consumption of Three.js via import {...} from 'three' (Vite/Rollup tree-shaking). The budget is the delta of new classes uniquely added by this feature.

Prerequisites

  • Story 3.1 debe be done (or both can be done in parallel — they're independent)
  • npm run build must produce a clean output (TypeScript errors would block this)
  • npm run lint must be clean

Build Command

npm run build
# = tsc && vite build
# output: dist/ (built from src/, publicDir from public/)

Bundle Analysis Tools

Two options (no new prod dependencies required):

Option A — rollup-plugin-visualizer (recommended):

npx rollup-plugin-visualizer --help  # check availability
# OR temporarily add to vite.config.ts:
import {visualizer} from "rollup-plugin-visualizer";
export default {
  root: "./src",
  plugins: [visualizer({open: true, filename: "dist/stats.html"})]
  // ... rest of config
};

Then npm run build — opens dist/stats.html in browser showing tree map.

Option B — vite-bundle-visualizer:

npx vite-bundle-visualizer

Option C — manual bundle inspection (simplest, no extra tools):

npm run build 2>&1
ls -la dist/
# Check the JS chunk sizes in dist/
du -sh dist/*.js
# For gzip sizes:
for f in dist/*.js; do echo "$f: $(gzip -c "$f" | wc -c) bytes gzip"; done

Baseline Measurement Strategy

Since Three.js was already included as a CDN/pre-bundled lib (via public/libs/three.min.js), the new feature adds TypeScript module consumption of Three.js via npm (named imports in src/). Vite will tree-shake these.

Two-point comparison for NFR-B2 delta:

  1. Before delta — the git state BEFORE Epic 1 (git stash or checkout to a clean state):

    git stash
    npm run build
    # Record gzip sizes
    git stash pop
    

    If the git stash is impractical (too much state), use the main branch or initial commit as baseline.

  2. After delta — current state:

    npm run build
    # Record gzip sizes
    

    Delta = (after) - (before) gzip size

  3. Alternative if git stash is messy — estimate based on class sizes:

    • webgl-layer-framework.ts source: ~280 lines of TS ≈ ~5KB minified + gzip
    • draw-relief-icons.ts source: ~260 lines (substantially refactored) — net delta is small
    • Three.js named imports for NEW classes only: review which classes were NOT already imported by any pre-existing code

Three.js Import Audit

Classes used by webgl-layer-framework.ts:

import {Group, OrthographicCamera, Scene, WebGLRenderer} from "three";

Classes used by draw-relief-icons.ts:

import {
  type Group, // ← already in webgl-layer-framework.ts (shared, no extra bundle cost)
  type Texture,
  BufferAttribute,
  BufferGeometry,
  DoubleSide,
  LinearFilter,
  LinearMipmapLinearFilter,
  Mesh,
  MeshBasicMaterial,
  SRGBColorSpace,
  TextureLoader
} from "three";

Check for any import * as THREE — should find ZERO:

grep -r "import \* as THREE" src/
# Expected output: (nothing)

Pre-existing Three.js Usage in Project

The project already has public/libs/three.min.js (CDN/pre-built). However, this is a different bundle path — it's a global script, not a module import. The Vite build for src/ will bundle Three.js module imports separately via npm (node_modules/three).

Check if Three.js was already imported via npm in any pre-existing src/ files:

grep -r "from 'three'\|from \"three\"" src/ --include="*.ts"

If the globe view uses the pre-built three.min.js (global THREE) rather than ESM imports, then Three.js ESM bundle cost is 100% new from this feature. If there are pre-existing ESM imports, the delta is only the newly added classes.

NFR Reference

NFR Threshold Verification
NFR-B1 No import * as THREE grep -r "import \* as THREE" src/ returns nothing
NFR-B2 ≤50KB gzipped increase Measure actual gzip delta before/after

Key Architecture Facts

  • Architecture Decision confirmed: "Three.js is already present; adds no bundle cost" — [Source: _bmad-output/planning-artifacts/architecture.md#Decision 1]
  • This refers to Three.js being already a dependency; the NAMED import tree-shaking still matters
  • Framework code size estimate: ~5KB minified, ~2KB gzip [Source: architecture.md#NFR-B2]
  • Vite version: ^7.3.1 — full ESM + tree-shaking support

Previous Story Intelligence

From Story 2.2 (draw-relief-icons.ts refactor)

  • Final named Three.js imports in draw-relief-icons.ts: BufferAttribute, BufferGeometry, DoubleSide, Group (type), LinearFilter, LinearMipmapLinearFilter, Mesh, MeshBasicMaterial, SRGBColorSpace, Texture (type), TextureLoader
  • The Biome import organizer (organizeImports: on) auto-orders imports alphabetically and moves type imports first. Confirmed lint-clean.
  • No import * as THREE from "three" remains anywhere in the project src/ tree.

From Story 3.1 (performance benchmarking)

  • src/renderers/draw-relief-icons.bench.ts may have been created in Story 3.1 — if so, verify its Three.js imports also follow named-import pattern (NFR-B1 applies to all src/ TypeScript)
  • Confirm bench file passes lint before running build

From Epic 1 (webgl-layer-framework.ts)

  • webgl-layer-framework.ts imports: Group, OrthographicCamera, Scene, WebGLRenderer — 4 named classes
  • draw-relief-icons.ts imports: 9 additional named classes (bufffers, mesh, material, texture, consts)
  • Total unique Three.js classes pulled: 13 (some overlap between the two files — Rollup deduplicates)

Tasks

  • T1: Verify NFR-B1 — no import * as THREE anywhere in src/

    • T1a: Run grep -r "import \* as THREE" src/ — expect zero matches
    • T1b: Run grep -r "import \* as THREE" src/ on bench file if created in Story 3.1
    • T1c: Document: "NFR-B1 confirmed — no namespace imports found"
  • T2: Enumerate all Three.js named imports actually used

    • T2a: grep -r "from \"three\"" src/ --include="*.ts" — list all import statements
    • T2b: Verify the list matches the architecture declaration (AC4)
    • T2c: Document the full import inventory
  • T3: Run production build

    • T3a: npm run build → confirm exit code 0 (no TypeScript errors, no Vite errors)
    • T3b: List dist/ output files and sizes: ls -la dist/
    • T3c: Calculate gzip sizes for all JS chunks: for f in dist/*.js; do echo "$f: $(gzip -c "$f" | wc -c) bytes"; done
  • T4: Establish baseline (before-feature bundle size)

    • T4a: git stash (stash current work if clean) OR use git show HEAD~N:dist/ if build artifacts were committed
    • T4b: If git stash feasible: git stashnpm run build → record gzip sizes → git stash pop
    • T4c: If stash impractical: use the main branch in a separate terminal, build separately, record sizes
    • T4d: Record baseline sizes
  • T5: Calculate and verify NFR-B2 delta

    • T5a: Compute: after_gzip_total - before_gzip_total
    • T5b: Verify delta ≤ 51,200 bytes (50KB)
    • T5c: If delta > 50KB: investigate which chunk grew unexpectedly (bundle visualizer)
  • T6: (Optional) Run bundle visualizer for tree-shaking confirmation (AC3)

    • T6a: Add rollup-plugin-visualizer temporarily to vite.config.ts
    • T6b: Run npm run build → open dist/stats.html
    • T6c: Verify Three.js tree nodes show only the expected named classes
    • T6d: Remove the visualizer from vite.config.ts afterward (do not commit it in production config — or move to a separate vite.analyze.ts config)
  • T7: npm run lint — zero errors (T6 vite.config.ts change must not be committed if produces lint issues)

  • T8: Document all results in Dev Agent Record:

    • T8a: NFR-B1 verdict (pass/fail + grep output)
    • T8b: Named import list (matches architecture spec?)
    • T8c: Baseline gzip sizes
    • T8d: Post-feature gzip sizes
    • T8e: Delta in bytes and KB — pass/fail vs 50KB budget
    • T8f: Bundle visualizer screenshot path or description (if T6 executed)

Dev Agent Record

Agent Model Used

to be filled by dev agent

Debug Log References

Completion Notes List

Record actual bundle measurements here:

NFR-B1:

  • grep -r "import * as THREE" src/ result: tbd
  • Verdict: tbd

NFR-B2:

  • Baseline bundle gzip total: tbd bytes
  • Post-feature bundle gzip total: tbd bytes
  • Delta: tbd bytes (tbd KB)
  • Budget: 51,200 bytes (50KB)
  • Verdict: tbd

Named Three.js imports (AC4):

_tbd — paste grep output here_

File List

Files created/modified (to be filled by dev agent):

  • vite.config.ts — TEMPORARY: add/remove visualizer plugin for T6 (do not commit)