From 01373e1e45daa8fae72f98bb40732f0533add115 Mon Sep 17 00:00:00 2001 From: Arno V Date: Sat, 31 Jan 2026 08:31:36 -0500 Subject: [PATCH] fix(bundlecheck): Make bars equal when formatted sizes match Detect when all displayed (formatted) gzip/raw sizes are identical and treat them as equal for bar calculation. Update calcBarLength to accept an allSame flag and return the full barWidth when values are effectively the same to avoid misleading visual differences for values that format to the same string. Add tests to verify equal bar lengths for values that format identically and differing bar lengths when formatted values differ. --- .../bundlecheck/src/__tests__/trend.test.ts | 46 +++++++++++++++++++ packages/bundlecheck/src/trend.ts | 31 +++++++++++-- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/packages/bundlecheck/src/__tests__/trend.test.ts b/packages/bundlecheck/src/__tests__/trend.test.ts index 01ec203c..b8354687 100644 --- a/packages/bundlecheck/src/__tests__/trend.test.ts +++ b/packages/bundlecheck/src/__tests__/trend.test.ts @@ -177,6 +177,52 @@ describe("renderTrendGraph", () => { ); }); + it("should use equal bar lengths when formatted values are the same", () => { + // These values differ by a few bytes but format to the same string + // 3295 bytes and 3297 bytes both format to "3.22 kB" + const results: TrendResult[] = [ + { version: "3.0.0", rawSize: 3297, gzipSize: 1650 }, + { version: "2.0.0", rawSize: 3296, gzipSize: 1649 }, + { version: "1.0.0", rawSize: 3295, gzipSize: 1648 }, + ]; + + const lines = renderTrendGraph("test-package", results, true); + + // Find gzip size lines (between "Gzip Size:" and "Raw Size:") + const gzipStartIdx = lines.findIndex((l) => l.includes("Gzip Size:")); + const rawStartIdx = lines.findIndex((l) => l.includes("Raw Size:")); + const gzipLines = lines + .slice(gzipStartIdx + 1, rawStartIdx) + .filter((l) => l.includes("▇")); + + // All bars should have the same number of bar characters + const barCounts = gzipLines.map((l) => (l.match(/▇/g) || []).length); + expect(new Set(barCounts).size).toBe(1); // All same length + expect(barCounts[0]).toBe(30); // Should be max bar width + }); + + it("should use different bar lengths when formatted values differ", () => { + // These values format to different strings + const results: TrendResult[] = [ + { version: "3.0.0", rawSize: 3000, gzipSize: 1500 }, + { version: "2.0.0", rawSize: 2000, gzipSize: 1000 }, + { version: "1.0.0", rawSize: 1000, gzipSize: 500 }, + ]; + + const lines = renderTrendGraph("test-package", results, true); + + // Find gzip size lines + const gzipStartIdx = lines.findIndex((l) => l.includes("Gzip Size:")); + const rawStartIdx = lines.findIndex((l) => l.includes("Raw Size:")); + const gzipLines = lines + .slice(gzipStartIdx + 1, rawStartIdx) + .filter((l) => l.includes("▇")); + + // Bars should have different lengths + const barCounts = gzipLines.map((l) => (l.match(/▇/g) || []).length); + expect(new Set(barCounts).size).toBeGreaterThan(1); // Different lengths + }); + it("should include bar characters", () => { const results: TrendResult[] = [ { version: "2.0.0", rawSize: 2048, gzipSize: 1024 }, diff --git a/packages/bundlecheck/src/trend.ts b/packages/bundlecheck/src/trend.ts index 8d1ae634..363f86be 100644 --- a/packages/bundlecheck/src/trend.ts +++ b/packages/bundlecheck/src/trend.ts @@ -106,6 +106,12 @@ export function renderTrendGraph( const minRawSize = Math.min(...rawSizes); const maxRawSize = Math.max(...rawSizes); + // Check if all formatted values are the same (to avoid misleading bar differences) + const formattedGzipSizes = results.map((r) => formatBytes(r.gzipSize)); + const formattedRawSizes = results.map((r) => formatBytes(r.rawSize)); + const allGzipSame = new Set(formattedGzipSizes).size === 1; + const allRawSame = new Set(formattedRawSizes).size === 1; + // Find max version length for alignment const maxVersionLen = Math.max(...results.map((r) => r.version.length)); @@ -114,9 +120,14 @@ export function renderTrendGraph( const minBarWidth = 10; // Minimum bar length for smallest value // Helper to calculate bar length with min-max scaling - const calcBarLength = (value: number, min: number, max: number): number => { - if (max === min) { - return barWidth; // All values are the same + const calcBarLength = ( + value: number, + min: number, + max: number, + allSame: boolean, + ): number => { + if (max === min || allSame) { + return barWidth; // All values are effectively the same } // Scale from minBarWidth to barWidth based on position between min and max const ratio = (value - min) / (max - min); @@ -132,7 +143,12 @@ export function renderTrendGraph( // Gzip size bars lines.push(blue("Gzip Size:")); for (const result of results) { - const barLength = calcBarLength(result.gzipSize, minGzipSize, maxGzipSize); + const barLength = calcBarLength( + result.gzipSize, + minGzipSize, + maxGzipSize, + allGzipSame, + ); const bar = "▇".repeat(barLength); const padding = " ".repeat(maxVersionLen - result.version.length); const sizeStr = formatBytes(result.gzipSize); @@ -144,7 +160,12 @@ export function renderTrendGraph( // Raw size bars lines.push(blue("Raw Size:")); for (const result of results) { - const barLength = calcBarLength(result.rawSize, minRawSize, maxRawSize); + const barLength = calcBarLength( + result.rawSize, + minRawSize, + maxRawSize, + allRawSame, + ); const bar = "▇".repeat(barLength); const padding = " ".repeat(maxVersionLen - result.version.length); const sizeStr = formatBytes(result.rawSize);