import { readFile } from "node:fs/promises"; import { evaluateBundleBudget } from "../src/application/policies/performance-budgets.ts"; import { bundleOutputInventoryArtifactSchema, bundlePerformanceArtifactSchema, } from "./contracts/release-artifacts.ts"; import { classifyViteJavascript } from "./lib/classify-vite-bundle.ts"; import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts"; type ViteManifest = Record< string, { file: string; isEntry?: boolean; imports?: string[] } >; type BundleBudgets = { initialJsGzipBytes: number; lazyChunkGzipBytes: number; }; const report = bundleOutputInventoryArtifactSchema.parse( JSON.parse(await readFile("artifacts/performance/bundle.json", "utf8")) as unknown, ); const viteManifest = JSON.parse( await readFile("dist/.vite/manifest.json", "utf8"), ) as ViteManifest; const budgets = JSON.parse( await readFile("config/performance/budgets.json", "utf8"), ).bundle as BundleBudgets; const outputByPath = new Map( report.outputs.map((output) => [output.path.replace(/^dist\//, ""), output]), ); const classification = classifyViteJavascript(viteManifest); const initialJsGzipBytes = classification.initialFiles.reduce( (total, file) => total + (outputByPath.get(file)?.gzipBytes ?? 0), 0, ); const lazyChunks = classification.lazyFiles.map((file) => ({ path: file, gzipBytes: outputByPath.get(file)?.gzipBytes ?? 0, })); const missingOutputs = [ ...classification.initialFiles, ...classification.lazyFiles, ].filter((file) => !outputByPath.has(file)); const measurements = { initialJsGzipBytes, lazyChunks }; const result = evaluateBundleBudget(measurements, budgets); const fixtures = [ { name: "initial-js-over-budget", passed: !evaluateBundleBudget( { initialJsGzipBytes: budgets.initialJsGzipBytes + 1, lazyChunks: [], }, budgets, ).passed, }, { name: "lazy-chunk-over-budget", passed: !evaluateBundleBudget( { initialJsGzipBytes: 0, lazyChunks: [ { path: "fixture.js", gzipBytes: budgets.lazyChunkGzipBytes + 1, }, ], }, budgets, ).passed, }, ]; const passed = result.passed && fixtures.every((fixture) => fixture.passed) && classification.missingImports.length === 0 && missingOutputs.length === 0; const completedReport = { ...report, measurements, classification, missingOutputs, thresholds: budgets, results: result, fixtures, passed, }; await writeValidatedJsonArtifact({ path: "artifacts/performance/bundle.json", schema: bundlePerformanceArtifactSchema, value: completedReport, }); if (!passed) { process.stderr.write( `Bundle budget or manifest integrity failed: ${[ ...classification.missingImports, ...missingOutputs, ].join(", ")}\n`, ); process.exit(1); } process.stdout.write( `Bundle budget: PASS (initial JS ${initialJsGzipBytes} / ${budgets.initialJsGzipBytes} gzip bytes)\n`, );