From ceb93074143e43e2576ed24b3978c7eaffe70ecf Mon Sep 17 00:00:00 2001 From: Arno V Date: Sun, 18 Oct 2026 15:32:24 -0400 Subject: [PATCH] feat(bundlesize): export readSizes from the package entry Scripts that store size-limit output and read it back, such as a "vs main" baseline, can now validate it with the same reader `--type compare` uses instead of a hand-rolled copy: it rejects malformed entries and duplicate names and surfaces the error object size-limit prints when its run fails. Co-Authored-By: Claude Opus 5.5 --- packages/bundlesize/README.md | 10 +++ .../src/__tests__/readSizes.test.ts | 77 +++++++++++++++++++ packages/bundlesize/src/config.ts | 1 + 3 files changed, 88 insertions(+) create mode 100644 packages/bundlesize/src/__tests__/readSizes.test.ts diff --git a/packages/bundlesize/README.md b/packages/bundlesize/README.md index d928a6f..ffa94eb 100644 --- a/packages/bundlesize/README.md +++ b/packages/bundlesize/README.md @@ -444,6 +444,16 @@ import { compareSizes } from "@node-cli/bundlesize"; const { data, passed } = compareSizes({ current, baseline, baseSha }); ``` +So is the reader `--type compare` uses for both files, to load a stored baseline with the same checks: + +```js +import { readSizes } from "@node-cli/bundlesize"; + +const { sizes, error } = readSizes("tmp/baseline/sizes.json"); +``` + +It never throws. On success `error` is `""`; otherwise `sizes` is `[]` and `error` says what was wrong: the file could not be read or parsed, it holds the `{ error }` object `size-limit --json` prints when its own run fails, it is not an array of `{ name, size, sizeLimit?, passed? }` with non-negative sizes, or two entries share a `name`. + ## Get help ```sh diff --git a/packages/bundlesize/src/__tests__/readSizes.test.ts b/packages/bundlesize/src/__tests__/readSizes.test.ts new file mode 100644 index 0000000..2199ccc --- /dev/null +++ b/packages/bundlesize/src/__tests__/readSizes.test.ts @@ -0,0 +1,77 @@ +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +// The package's "." export, so this also locks the public surface. +import { readSizes } from "../config.js"; + +let dir: string; +const write = (name: string, content: unknown) => { + const file = join(dir, name); + writeFileSync( + file, + typeof content === "string" ? content : JSON.stringify(content), + ); + return file; +}; + +beforeEach(() => { + dir = mkdtempSync(join(tmpdir(), "bundlesize-read-sizes-")); +}); + +afterEach(() => { + rmSync(dir, { recursive: true, force: true }); +}); + +describe("readSizes", () => { + it("is exported from the package entry", () => { + expect(typeof readSizes).toBe("function"); + }); + + it("returns the entries of a valid sizes file", () => { + const sizes = [ + { name: "first-paint", passed: true, size: 2048, sizeLimit: 4096 }, + { name: "lazy", size: 0 }, + ]; + expect(readSizes(write("sizes.json", sizes))).toEqual({ + sizes, + error: "", + }); + }); + + it("reports a file that cannot be read", () => { + const result = readSizes(join(dir, "missing.json")); + expect(result.sizes).toEqual([]); + expect(result.error).toMatch(/^Failed to read JSON file: /); + }); + + it("surfaces the error size-limit prints when its own run fails", () => { + const file = write("sizes.json", { error: "Cannot find dist/main.js" }); + expect(readSizes(file)).toEqual({ + sizes: [], + error: `Sizes file ${file} reports an error: Cannot find dist/main.js`, + }); + }); + + it.each([ + ["a non-array", { name: "a", size: 1 }], + ["a missing name", [{ size: 1 }]], + ["a negative size", [{ name: "a", size: -1 }]], + ["a non-numeric limit", [{ name: "a", size: 1, sizeLimit: "1 kB" }]], + ["a non-boolean passed", [{ name: "a", size: 1, passed: "yes" }]], + ])("rejects %s", (_label, content) => { + const result = readSizes(write("sizes.json", content)); + expect(result.sizes).toEqual([]); + expect(result.error).toMatch(/^Invalid sizes file /); + }); + + it("rejects duplicate entry names", () => { + const file = write("sizes.json", [ + { name: "a", size: 1 }, + { name: "a", size: 2 }, + ]); + const result = readSizes(file); + expect(result.sizes).toEqual([]); + expect(result.error).toContain('Duplicate entry name "a"'); + }); +}); diff --git a/packages/bundlesize/src/config.ts b/packages/bundlesize/src/config.ts index a21dbb5..34ac725 100644 --- a/packages/bundlesize/src/config.ts +++ b/packages/bundlesize/src/config.ts @@ -6,6 +6,7 @@ export { compareSizes, type SizeResult, } from "./compare.js"; +export { readSizes } from "./compareStats.js"; /** * Configuration for a single file size entry.