Files
EpicNext-Cms/docs/performance-budgets.md
Simo cc714b427a
CI / check (push) Failing after 54s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
ci: report per-route JavaScript budgets from Docker build
2026-09-11 08:41:31 +02:00

3.9 KiB
Raw Permalink Blame History

Informational route JavaScript budgets

Run after the existing production build; no second build or server is needed:

node scripts/performance-report.mjs --next-dir .next --config scripts/performance-budgets.json --output-dir build-reports

The command writes report.json and report.md and prints the Markdown report. An optional PERFORMANCE_COMMIT_SHA environment variable records the commit declared by the build caller; the script does not infer that an existing build matches the current checkout. JSON also records BUILD_ID, Node/zlib versions, manifest provenance, exact file paths, sizes and source entries.

What is measured

For each configured App Router route, resolve its exact app path using app-path-routes-manifest.json and server/app-paths-manifest.json. Read its generated page_client-reference-manifest.js as a JSON assignment without executing JavaScript. Use its sibling page/build-manifest.json, falling back to the root build manifest only if that sibling is absent.

The initial entry envelope is the union of route bootstrap rootMainFilesTree[appPath] (or rootMainFiles) and every entryJSFiles list in that route's client-reference manifest. This includes layout, page and boundary/loading entries. The definition follows the data exposed by the installed Next 16.3.4 Turbopack build and the getLinkAndScriptTags / getRequiredScripts renderer helpers; it is deliberately a build-artifact envelope, not a browser network trace. Conditional rendering, redirects, streaming and browser caches can change actual requests.

  • Raw bytes are filesystem byte lengths of unique JavaScript assets in that envelope.
  • Gzip bytes are the sum of independent gzip level 9 compressions of those files using the recorded Node/zlib runtime. They are not gzip of concatenated source, nor observed CDN transfer sizes.
  • Deployment query strings and /_next/ prefixes are normalized before deduplication. Shared files count once per route; each route is measured independently, with no misleading cross-route total.
  • Legacy nomodule polyfills are measured separately, outside the modern initial budget. CSS, source maps, images, external scripts, HTML/RSC payloads and async-only chunks absent from entryJSFiles are excluded.
  • This report makes no claims about execution cost, LCP, hydration time or real-user performance.

Initial limits

The first limits are baseline bytes × 1.15, rounded upward to the next 10 KiB (10,240 bytes) independently for raw and gzip. They are provisional size alerts, not validated speed targets. Baseline: existing local production build build-TfctsWXpff2fKS, Next 16.3.4; its source commit was not inferred.

Route Baseline raw bytes Baseline gzip bytes Raw limit Gzip limit
/me 767156 238571 890880 276480
/news 765367 237599 880640 276480
/events 765851 237964 890880 276480
/search 765851 237964 890880 276480
/admin/catalog 1654898 492619 1904640 573440
/admin/studio/furni 1241312 391541 1433600 450560

Configured limits are positive integer bytes; null explicitly means observe-only. scripts/performance-budgets.json remains mode: informational. Exceeding a limit produces over-budget and a warning, with exit code 0. Missing production BUILD_ID, unsupported manifests, missing routes or missing referenced assets produce unavailable with a reason and no partial/zero total, also exit code 0. Malformed budget configuration or an unwritable output directory fails the command. This keeps initial CI reporting non-blocking while preventing invalid configuration from quietly disabling limits.

Synthetic tests cover shared-chunk deduplication, exact byte/gzip calculations, route bootstrap selection, missing data, safe parsing and CLI exit behavior. Run pnpm exec vitest run --coverage.enabled=false scripts/performance-report.test.mjs.