API

Module: Bundle Size Tooling

Package paths: scripts/bundle-scenes-core.ts, scripts/build-bundle-scenes.ts, scripts/build-master-bundle-info.ts, scripts/report-bundle-size-deltas.ts, lab/index.html

Purpose

The bundle-size tooling builds production Vite bundles for each lab scene, measures the JavaScript actually fetched at runtime, and exposes local-vs-master size comparisons in the lab Bundle tab.

The tooling has two separate data layers:

  1. Runtime size manifests — per-scene raw/gzip totals and runtime-loaded chunk names.
  2. File/module breakdowns — per-scene Rollup chunk and minified source-module attribution used by the Bundle tab Files popup.

All generated local and master bundle data is gitignored; the master baseline is published by CI rather than tracked in the repository.

Manifest Semantics

The per-scene bundle data lives in one JSON file per scene under lab/public/bundle/manifest/, aggregated into a single manifest.json that runtime consumers (lab UI, bundle-size test, report script, static lab site) read. None of it is tracked — the whole lab/public/bundle/ tree is generated build output.

FileGenerated byTracked?Meaning
lab/public/bundle/manifest/<scene>.json
pnpm build:bundle-scenes
No
This build's per-scene runtime measurements.
lab/public/bundle/manifest.json
pnpm build:bundle-scenes (aggregated from the per-scene files)
No
Aggregate of all per-scene files, consumed at runtime. This is the file master publishes as the baseline.
lab/public/bundle/master-manifest.json
pnpm build:bundle-scenes and pnpm build:bundle-master-info
No
The master baseline, fetched from the published URL (or a git ref when one is named explicitly). Used for the delta report.

local-manifest.json is not part of the architecture and must not be used.

Where the Baseline Comes From

It is measured once per master build and published to a stable public URL:

https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/lite/bundle-baseline/manifest.json

pnpm build:bundle-scenes fetches it and writes master-manifest.json.

This replaced a baseline of ~227 JSON files tracked in git and refreshed by PR authors. Because nearly every change to a shared module moves bytes in most of the ~230 scenes, that arrangement broke the repo in two separate ways at once:

  • Red CI — the moment any shared-code PR merged, every other open PR's committed manifest was stale and its Bundle Size job went red for reasons unrelated to that PR, curable only by a rebase plus a very slow full local rebuild, then again on the next merge.
  • Merge conflicts — two branches that both regenerated the manifest rewrote the same ~200 tracked files and collided in git. This was the repo's dominant source of conflicts: in one representative PR, 168 of the 169 conflicting paths were manifest files and zero were source files.

Both symptoms have one cause: a generated file living in git, on branches that have no reason to carry it. Publishing it instead of committing it removes the cause — there is no tracked file to go stale, and nothing for two branches to collide on.

Publishing rather than pushing to master also keeps CI out of the repository's write path. master is protected (one required approval, plus a ruleset with required status checks) and the CI identity deliberately holds no bypass, so a design in which a bot commits the baseline to master could not work here without weakening that protection.

This is safe because the baseline gates nothing:

  • the size gate reads scene-config.json (maxRawKB), enforced byte-exactly by pnpm build:bundle-scenes;
  • the baseline is consulted only for the advisory "increased vs master" delta.

A missing or unreachable baseline therefore degrades to "no delta report" and never fails a build. bundle-size.spec.ts skips its advisory warning, and report-bundle-size-deltas.ts sets POST_BUNDLE_COMMENT=false and exits 0.

BuildMeasuresEnforces ceilingsPublishes the baseline
PR validation
yes
yes
no
Master (merged)
yes
yes
yes

The PR-validation Bundle Size job therefore:

  1. measures every scene (pnpm build:bundle-scenes), which fails the job when a scene exceeds its scene-config.json ceiling — the deterministic, never-stale gate;
  2. posts the size deltas against the published baseline as a PR comment (report-bundle-size-deltas.ts), which is how reviewers see the size impact.

azure-pipelines-bundle-manifest.yml re-measures on each push to master and uploads manifest.json to the deployment server, then purges the CDN so the new baseline is visible promptly. It refuses to publish an empty or missing manifest, so a broken build cannot blank the baseline, and the ceiling check runs first so a breach can never be published as the new normal.

To override the baseline source locally, set BUNDLE_MASTER_MANIFEST_FILE to a downloaded manifest, or blank BUNDLE_MASTER_MANIFEST_URL to skip the fetch.

Scene bundles are built against the package's compiled build/lib output (the module-granular tree a real consumer of @babylonjs/lite resolves), not the TypeScript source — so the measured size is exactly what a downstream bundler produces. pnpm build:bundle-scenes runs build:lib first; it fails fast if build/lib is missing. (The lab dev app and the master-comparison bundle-info still resolve babylon-lite to source for a fast dev-iteration loop; their sizes may differ slightly, but these scene bundle-size tests are the authoritative guard against size drift.)

Each scene's runtime measurement subtracts its own ignored bytes, computed from that build's own bundle-info. The ignored set covers (a) local *-nme.ts NME data payloads and (b) bundled third-party WASM/shaping runtimes — text-shaper (default-layout text), manifold-3d (CSG2), and @recast-navigation (navmesh) — each loaded only by the feature that needs it, so engine-size ceilings track our own runtime code. The ceiling check always uses the current build's own accounting; master-manifest.json is consulted only for the advisory "increased vs master" delta, never to compute the gated rawKB.

File/Module Breakdown Semantics

FileGenerated byTracked?Meaning
lab/public/bundle/bundle-info/<scene>.json
pnpm build:bundle-scenes
No
Current/local Rollup chunk list with minified module-byte attribution and surviving exported symbols.
lab/public/bundle/master-bundle-info/<scene>.json
pnpm build:bundle-master-info
No
Same breakdown generated from an archive of the selected master ref.

build-master-bundle-info.ts resolves the master ref from MASTER_BUNDLE_REF, upstream/master, origin/master, then master. It writes master-manifest.json from the same ref before building master-bundle-info, so master file comparisons and runtime chunk filtering use a consistent baseline.

Build Commands

pnpm build:bundle-scenes

Builds the selected local scenes (BUNDLE_SCENES=scene1,scene75 for a subset), writes local scene bundles, writes bundle-info/<scene>.json, writes/updates the generated per-scene manifest/<scene>.json files and aggregate manifest.json, and refreshes master-manifest.json from master. All of these outputs are gitignored.

pnpm build:bundle-master-info

Builds master file/module breakdowns for selected scenes (BUNDLE_SCENES=...) from the selected master ref (MASTER_BUNDLE_REF=... override), writes master-bundle-info/<scene>.json, and refreshes master-manifest.json from the same ref.

PR Scene Impact Selection

The full master bundle build publishes impact-manifest.json beside the bundle-size baseline. It is generated from every Rollup chunk statically reachable from each scene and maps repository source files to the scenes that can consume them. This deliberately includes lazy chunks that a single baseline measurement did not fetch, so later interactions and nondeterministic runtime branches cannot create false-negative scene selections. Local source dependencies such as raw WGSL imports are included in the map. The file is a generated deployment artifact, not tracked source.

PR jobs run pnpm select:affected-scenes -- --azure against the exact merge-base commit's immutable impact manifest. The selector combines that map with directly named scene files and changed scene-config.json entries, then sets separate Azure variables for bundle-size, parity, and performance scenes. New files inherit the scenes of changed mapped modules that import them.

Selection fails closed:

  • an unavailable or mismatched impact manifest makes runtime changes run all scenes;
  • an unmapped or unclassified runtime file runs all scenes;
  • documentation, thumbnails, most unit tests, lite-gl, compat, and playground-only changes skip WebGPU scene jobs;
  • bundle-content unit tests explicitly select the scene fixtures required by their bundle-backed assertions;
  • cloud scene tests are selected by the presence of a Playwright scene spec, independently of whether skipParity disables Babylon/golden comparison for that scene;
  • scenes opting out of bundle-size or performance checks are removed only from that specific job.

Every master build still measures all scenes and republishes both the size and impact manifests. This refreshes the dependency map after each merge and preserves repository-wide coverage without making generated metadata a merge-conflict source.

Minified Attribution

bundle-scenes-core.ts builds with hidden sourcemaps and runs the final minified output through module attribution:

  • chunk bytes are final emitted UTF-8 JavaScript bytes
  • module bytes are minified source-map-attributed bytes
  • modules with zero attributed minified bytes are omitted
  • the lab Files popup adds a (chunk overhead) row for bytes that cannot be attributed to a source module

Small per-module deltas can be source-map attribution drift even when standalone source or emitted module code is unchanged. The chunk total remains the authoritative emitted size.

Lab Bundle Tab

The Bundle tab loads:

  • /bundle/manifest.json for current/local scene totals
  • /bundle/master-manifest.json for master scene totals
  • /bundle/bundle-info/<scene>.json for local file/module details
  • /bundle/master-bundle-info/<scene>.json for master file/module details

The scene cards and summary compare local manifest.json values against master-manifest.json.

The Files popup compares local and master chunk/module bytes when both breakdowns are available:

  • Rollup hash suffixes are ignored when matching chunk names.
  • Local-only chunks/modules show positive deltas.
  • Master-only chunks/modules are shown as 0 B locally with negative deltas.
  • Zero deltas are hidden.
  • Sorting preserves comparison groups (common, local-only, master-only) while applying Size/Path and Asc/Desc within each group.
  • Runtime chunk filtering uses each source's own manifest (manifest.json for local, master-manifest.json for master).

CI Comment Script

scripts/report-bundle-size-deltas.ts compares:

  • current: lab/public/bundle/manifest.json or BUNDLE_SIZE_CURRENT_MANIFEST
  • master: lab/public/bundle/master-manifest.json or BUNDLE_SIZE_MASTER_MANIFEST

It emits a rounded KB Markdown summary and Azure variables for conditional PR comments.

File Manifest

FilePurpose
scripts/bundle-scenes-core.ts
Shared bundle build, runtime measurement, manifest generation, minified attribution, and bundle-info writing.
scripts/build-bundle-scenes.ts
Local/current bundle entry point.
scripts/build-master-bundle-info.ts
Master-ref archive extraction and master file/module breakdown generation.
scripts/report-bundle-size-deltas.ts
PR-comment delta report from current and master manifests.
azure-pipelines-bundle-manifest.yml
Master-triggered pipeline that re-measures every scene and publishes the baseline manifest.
lab/index.html
Bundle tab summary, scene cards, and Files popup comparison UI.
lab/vite.config.ts
Lab dev-server reload signature for current/master bundle manifests.