From cf99d218ab61d7f6dca1d5d9f8aeb63b1c400726 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:59:05 +0000 Subject: [PATCH 1/8] feat: celebrate bundle size decreases and report no-change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Setting pack-size-threshold to -1 now shows 🎉 when bundle size decreases, and ⚠️ when it increases (reporting all changes) - When packed bundle size diff is 0, a "No bundle size changes" message is posted instead of silently doing nothing - Add tests for all new bundle-size behaviors - Update README and action.yml docs for pack-size-threshold=-1 https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- README.md | 19 +++- action.yml | 2 +- src/checks/bundle-size.ts | 68 ++++++++++++++ src/packs.ts | 3 +- .../__snapshots__/bundle-size_test.ts.snap | 57 ++++++++++++ test/checks/bundle-size_test.ts | 92 +++++++++++++++++++ 6 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 test/checks/__snapshots__/bundle-size_test.ts.snap create mode 100644 test/checks/bundle-size_test.ts diff --git a/README.md b/README.md index 072abd3..0c10058 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ jobs: | `duplicate-threshold` | Threshold for warning about packages with multiple versions | No | `1` | | `base-packages` | Glob pattern for base branch pack files (e.g., `"./base-packs/*.tgz"`) | No | None | | `source-packages` | Glob pattern for source branch pack files (e.g., `"./source-packs/*.tgz"`) | No | None | -| `pack-size-threshold` | Threshold (in bytes) for warning about significant increase in total pack size | No | `50000` | +| `pack-size-threshold` | Threshold (in bytes) for warning about significant increase in total pack size. Set to `-1` to always report size changes. | No | `50000` | | `detect-replacements` | Detect modules which have community suggested alternatives | No | `true` | | `working-directory` | Working directory to scan for package lock file | No | None | @@ -90,11 +90,26 @@ The action accepts glob patterns to locate package tarballs for comparison: - **`base-packages`** - Glob pattern for base branch pack files (e.g., `"./base-packs/*.tgz"`) - **`source-packages`** - Glob pattern for source branch pack files (e.g., `"./source-packs/*.tgz"`) -- **`pack-size-threshold`** - Threshold in bytes for warning about significant pack size increases +- **`pack-size-threshold`** - Threshold in bytes for warning about significant pack size increases. Set to `-1` to always report bundle size changes. > [!NOTE] > Package bundle analysis only runs when both `base-packages` and `source-packages` are provided. If these inputs are not set, this feature is skipped entirely. +When the bundle size does not change between base and source, a message is posted confirming there was no bundle size change. + +### Always Report Bundle Size Changes + +To always report bundle size changes and celebrate decreases, set `pack-size-threshold` to `-1`: + +```yaml +- name: Create Diff + uses: e18e/action-dependency-diff@v1 + with: + base-packages: './base-packs/*.tgz' + source-packages: './source-packs/*.tgz' + pack-size-threshold: -1 +``` + You can see an example of how to set this up in the [bundle difference workflow](./recipes/bundle-diff.yml). ## Module Replacements diff --git a/action.yml b/action.yml index 9c4c9b4..658e4a9 100644 --- a/action.yml +++ b/action.yml @@ -33,7 +33,7 @@ inputs: description: 'Glob pattern for source branch pack files (e.g., "./source-packs/*.tgz")' required: false pack-size-threshold: - description: 'Threshold (in bytes) for warning about significant increase in total pack size' + description: 'Threshold (in bytes) for warning about significant increase in total pack size. Set to -1 to always report size changes.' required: false default: '50000' duplicate-threshold: diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index 8400e4e..8d4ed7f 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -11,6 +11,74 @@ export async function scanForBundleSize( return; } const comparison = comparePackSizes(basePacks, sourcePacks, threshold); + + const totalSizeChange = comparison.packChanges.reduce( + (sum, change) => sum + change.sizeChange, + 0 + ); + + if (totalSizeChange === 0) { + messages.push(`## 📦 Package Bundle Size\n\nNo bundle size changes.`); + return; + } + + if (threshold === -1) { + const decreases = comparison.packChanges.filter( + (change) => change.exceedsThreshold && change.sizeChange < 0 + ); + const increases = comparison.packChanges.filter( + (change) => change.exceedsThreshold && change.sizeChange > 0 + ); + + if (decreases.length > 0) { + const packRows = decreases + .map((change) => { + const baseSize = change.baseSize + ? formatBytes(change.baseSize) + : 'New'; + const sourceSize = change.sourceSize + ? formatBytes(change.sourceSize) + : 'Removed'; + const sizeChange = formatBytes(Math.abs(change.sizeChange)); + return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; + }) + .join('\n'); + + messages.push( + `## 🎉 Package Size Decrease + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📉 Size Change | +| --- | --- | --- | --- | +${packRows}` + ); + } + + if (increases.length > 0) { + const packRows = increases + .map((change) => { + const baseSize = change.baseSize + ? formatBytes(change.baseSize) + : 'New'; + const sourceSize = change.sourceSize + ? formatBytes(change.sourceSize) + : 'Removed'; + const sizeChange = formatBytes(change.sizeChange); + return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; + }) + .join('\n'); + + messages.push( + `## ⚠️ Package Size Increase + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +${packRows}` + ); + } + + return; + } + const packWarnings = comparison.packChanges.filter( (change) => change.exceedsThreshold && change.sizeChange > 0 ); diff --git a/src/packs.ts b/src/packs.ts index 06d05cb..5955e37 100644 --- a/src/packs.ts +++ b/src/packs.ts @@ -175,7 +175,8 @@ export function comparePackSizes( const sourceSize = sourcePack?.size ?? null; const sizeChange = (sourceSize ?? 0) - (baseSize ?? 0); - const exceedsThreshold = sizeChange >= threshold; + const exceedsThreshold = + threshold === -1 ? sizeChange !== 0 : sizeChange >= threshold; packChanges.push({ name: packName, diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap new file mode 100644 index 0000000..a334a6d --- /dev/null +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -0,0 +1,57 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`scanForBundleSize > should celebrate size decrease when threshold is -1 1`] = ` +[ + "## 🎉 Package Size Decrease + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📉 Size Change | +| --- | --- | --- | --- | +| my-package | 200 kB | 100 kB | 100 kB |", +] +`; + +exports[`scanForBundleSize > should report no bundle size change when diff is 0 1`] = ` +[ + "## 📦 Package Bundle Size + +No bundle size changes.", +] +`; + +exports[`scanForBundleSize > should report no bundle size change with threshold=-1 when diff is 0 1`] = ` +[ + "## 📦 Package Bundle Size + +No bundle size changes.", +] +`; + +exports[`scanForBundleSize > should show both decreases and increases when threshold is -1 1`] = ` +[ + "## 📦 Package Bundle Size + +No bundle size changes.", +] +`; + +exports[`scanForBundleSize > should show only increases when threshold is -1 and no decreases 1`] = ` +[ + "## ⚠️ Package Size Increase + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| my-package | 100 kB | 200 kB | 100 kB |", +] +`; + +exports[`scanForBundleSize > should warn about size increase exceeding threshold 1`] = ` +[ + "## ⚠️ Package Size Increase + +These packages exceed the size increase threshold of 50 kB: + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| my-package | 100 kB | 200 kB | 100 kB |", +] +`; diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts new file mode 100644 index 0000000..6e766f1 --- /dev/null +++ b/test/checks/bundle-size_test.ts @@ -0,0 +1,92 @@ +import {describe, expect, it} from 'vitest'; +import {scanForBundleSize} from '../../src/checks/bundle-size.js'; +import type {PackInfo} from '../../src/packs.js'; + +function makePack(packageName: string, size: number): PackInfo { + return { + name: `${packageName}-1.0.0.tgz`, + packageName, + path: `/tmp/${packageName}-1.0.0.tgz`, + size + }; +} + +describe('scanForBundleSize', () => { + it('should do nothing when no packs are provided', async () => { + const messages: string[] = []; + await scanForBundleSize(messages, [], [], 50000); + expect(messages).toHaveLength(0); + }); + + it('should report no bundle size change when diff is 0', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 100000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(1); + expect(messages[0]).toContain('No bundle size changes'); + expect(messages).toMatchSnapshot(); + }); + + it('should report no bundle size change with threshold=-1 when diff is 0', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 100000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, -1); + + expect(messages).toMatchSnapshot(); + }); + + it('should warn about size increase exceeding threshold', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 200000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toMatchSnapshot(); + }); + + it('should not warn about size increase below threshold', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 120000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(0); + }); + + it('should celebrate size decrease when threshold is -1', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 200000)]; + const sourcePacks = [makePack('my-package', 100000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, -1); + + expect(messages).toMatchSnapshot(); + }); + + it('should show both decreases and increases when threshold is -1', async () => { + const messages: string[] = []; + const basePacks = [makePack('pkg-a', 200000), makePack('pkg-b', 50000)]; + const sourcePacks = [makePack('pkg-a', 100000), makePack('pkg-b', 150000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, -1); + + expect(messages).toMatchSnapshot(); + }); + + it('should show only increases when threshold is -1 and no decreases', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 200000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, -1); + + expect(messages).toMatchSnapshot(); + }); +}); From 6a55249c23fc6ba5d948c4207642f06a40689672 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:09:26 +0000 Subject: [PATCH 2/8] fix: use per-package zero-check for no-change and net total for threshold warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous totalSizeChange === 0 check incorrectly triggered the "No bundle size changes" message when individual package changes cancelled each other out (e.g. one package shrinks by 100 kB while another grows by 100 kB). Replace with a per-package allUnchanged check so the message only fires when every package is truly unchanged. For the warning path, compare net total size change against the threshold rather than individual packages. This means if pkg-a shrinks 100 kB and pkg-b grows 100 kB with a 50 kB threshold, no warning is shown since the total bundle size is unchanged — consistent with how a size threshold over the total makes sense. Also add tests covering the two previously untested edge cases. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- src/checks/bundle-size.ts | 18 +++++++++++----- .../__snapshots__/bundle-size_test.ts.snap | 11 ++++++++-- test/checks/bundle-size_test.ts | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index 8d4ed7f..a7aa22a 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -12,12 +12,11 @@ export async function scanForBundleSize( } const comparison = comparePackSizes(basePacks, sourcePacks, threshold); - const totalSizeChange = comparison.packChanges.reduce( - (sum, change) => sum + change.sizeChange, - 0 + const allUnchanged = comparison.packChanges.every( + (change) => change.sizeChange === 0 ); - if (totalSizeChange === 0) { + if (allUnchanged) { messages.push(`## 📦 Package Bundle Size\n\nNo bundle size changes.`); return; } @@ -79,8 +78,17 @@ ${packRows}` return; } + const totalSizeChange = comparison.packChanges.reduce( + (sum, change) => sum + change.sizeChange, + 0 + ); + + if (totalSizeChange < threshold) { + return; + } + const packWarnings = comparison.packChanges.filter( - (change) => change.exceedsThreshold && change.sizeChange > 0 + (change) => change.sizeChange > 0 ); if (packWarnings.length > 0) { diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap index a334a6d..af53b82 100644 --- a/test/checks/__snapshots__/bundle-size_test.ts.snap +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -28,9 +28,16 @@ No bundle size changes.", exports[`scanForBundleSize > should show both decreases and increases when threshold is -1 1`] = ` [ - "## 📦 Package Bundle Size + "## 🎉 Package Size Decrease -No bundle size changes.", +| 📦 Package | 📏 Base Size | 📏 Source Size | 📉 Size Change | +| --- | --- | --- | --- | +| pkg-a | 200 kB | 100 kB | 100 kB |", + "## ⚠️ Package Size Increase + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| pkg-b | 50 kB | 150 kB | 100 kB |", ] `; diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index 6e766f1..4973a50 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -60,6 +60,26 @@ describe('scanForBundleSize', () => { expect(messages).toHaveLength(0); }); + it('should not report no-change when individual changes cancel out', async () => { + const messages: string[] = []; + const basePacks = [makePack('pkg-a', 200000), makePack('pkg-b', 50000)]; + const sourcePacks = [makePack('pkg-a', 100000), makePack('pkg-b', 150000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(0); + }); + + it('should not report no-change when changes exist but are below threshold', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 120000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(0); + }); + it('should celebrate size decrease when threshold is -1', async () => { const messages: string[] = []; const basePacks = [makePack('my-package', 200000)]; @@ -77,6 +97,7 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, -1); + expect(messages).toHaveLength(2); expect(messages).toMatchSnapshot(); }); From 50d7d76c4c1fbefa5a1b9cac4fd80c1972f05fb9 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Tue, 24 Mar 2026 07:46:50 +0000 Subject: [PATCH 3/8] refactor: combine decreases and increases into one table when threshold is -1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of two separate messages (one for 🎉 decreases, one for ⚠️ increases), all changed packages are now shown in a single table. The heading adapts based on direction: 🎉 for pure decreases, ⚠️ for pure increases, 📦 for mixed. The size change column uses signed values (negative for decreases) so direction is clear at a glance. Update tests and README accordingly. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- README.md | 2 +- src/checks/bundle-size.ts | 44 ++++++------------- .../__snapshots__/bundle-size_test.ts.snap | 18 +++----- test/checks/bundle-size_test.ts | 2 +- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 0c10058..84740ff 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ When the bundle size does not change between base and source, a message is poste ### Always Report Bundle Size Changes -To always report bundle size changes and celebrate decreases, set `pack-size-threshold` to `-1`: +To always report bundle size changes, set `pack-size-threshold` to `-1`. All changed packages are shown in a single table. When only decreases occur, the report is marked with 🎉: ```yaml - name: Create Diff diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index a7aa22a..95da3d3 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -22,38 +22,22 @@ export async function scanForBundleSize( } if (threshold === -1) { - const decreases = comparison.packChanges.filter( - (change) => change.exceedsThreshold && change.sizeChange < 0 + const changedPacks = comparison.packChanges.filter( + (change) => change.exceedsThreshold ); - const increases = comparison.packChanges.filter( - (change) => change.exceedsThreshold && change.sizeChange > 0 - ); - - if (decreases.length > 0) { - const packRows = decreases - .map((change) => { - const baseSize = change.baseSize - ? formatBytes(change.baseSize) - : 'New'; - const sourceSize = change.sourceSize - ? formatBytes(change.sourceSize) - : 'Removed'; - const sizeChange = formatBytes(Math.abs(change.sizeChange)); - return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; - }) - .join('\n'); - messages.push( - `## 🎉 Package Size Decrease + if (changedPacks.length > 0) { + const hasDecreases = changedPacks.some((c) => c.sizeChange < 0); + const hasIncreases = changedPacks.some((c) => c.sizeChange > 0); -| 📦 Package | 📏 Base Size | 📏 Source Size | 📉 Size Change | -| --- | --- | --- | --- | -${packRows}` - ); - } + const heading = + hasDecreases && hasIncreases + ? '## 📦 Package Bundle Size Changes' + : hasDecreases + ? '## 🎉 Package Size Decrease' + : '## ⚠️ Package Size Increase'; - if (increases.length > 0) { - const packRows = increases + const packRows = changedPacks .map((change) => { const baseSize = change.baseSize ? formatBytes(change.baseSize) @@ -67,9 +51,9 @@ ${packRows}` .join('\n'); messages.push( - `## ⚠️ Package Size Increase + `${heading} -| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | | --- | --- | --- | --- | ${packRows}` ); diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap index af53b82..b067f9c 100644 --- a/test/checks/__snapshots__/bundle-size_test.ts.snap +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -4,9 +4,9 @@ exports[`scanForBundleSize > should celebrate size decrease when threshold is -1 [ "## 🎉 Package Size Decrease -| 📦 Package | 📏 Base Size | 📏 Source Size | 📉 Size Change | +| 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | | --- | --- | --- | --- | -| my-package | 200 kB | 100 kB | 100 kB |", +| my-package | 200 kB | 100 kB | -100 kB |", ] `; @@ -28,16 +28,12 @@ No bundle size changes.", exports[`scanForBundleSize > should show both decreases and increases when threshold is -1 1`] = ` [ - "## 🎉 Package Size Decrease - -| 📦 Package | 📏 Base Size | 📏 Source Size | 📉 Size Change | -| --- | --- | --- | --- | -| pkg-a | 200 kB | 100 kB | 100 kB |", - "## ⚠️ Package Size Increase + "## 📦 Package Bundle Size Changes -| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | | --- | --- | --- | --- | -| pkg-b | 50 kB | 150 kB | 100 kB |", +| pkg-b | 50 kB | 150 kB | 100 kB | +| pkg-a | 200 kB | 100 kB | -100 kB |", ] `; @@ -45,7 +41,7 @@ exports[`scanForBundleSize > should show only increases when threshold is -1 and [ "## ⚠️ Package Size Increase -| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | | --- | --- | --- | --- | | my-package | 100 kB | 200 kB | 100 kB |", ] diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index 4973a50..b73e0b6 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -97,7 +97,7 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, -1); - expect(messages).toHaveLength(2); + expect(messages).toHaveLength(1); expect(messages).toMatchSnapshot(); }); From 784a8b59735509ae9206e3a3efb759fab6377fba Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Tue, 24 Mar 2026 07:49:25 +0000 Subject: [PATCH 4/8] feat: show signed size changes (+/-) in bundle size table Add a formatBytesSigned helper that prefixes positive values with '+' so the size change column is unambiguous in both the threshold=-1 path (which can mix increases and decreases) and the threshold>0 warning path. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- src/checks/bundle-size.ts | 8 ++++++-- test/checks/__snapshots__/bundle-size_test.ts.snap | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index 95da3d3..59f23e1 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -1,6 +1,10 @@ import {formatBytes} from '../common.js'; import {comparePackSizes, type PackInfo} from '../packs.js'; +function formatBytesSigned(bytes: number): string { + return `${bytes > 0 ? '+' : ''}${formatBytes(bytes)}`; +} + export async function scanForBundleSize( messages: string[], basePacks: PackInfo[], @@ -45,7 +49,7 @@ export async function scanForBundleSize( const sourceSize = change.sourceSize ? formatBytes(change.sourceSize) : 'Removed'; - const sizeChange = formatBytes(change.sizeChange); + const sizeChange = formatBytesSigned(change.sizeChange); return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; }) .join('\n'); @@ -82,7 +86,7 @@ ${packRows}` const sourceSize = change.sourceSize ? formatBytes(change.sourceSize) : 'Removed'; - const sizeChange = formatBytes(change.sizeChange); + const sizeChange = formatBytesSigned(change.sizeChange); return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; }) .join('\n'); diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap index b067f9c..4d93873 100644 --- a/test/checks/__snapshots__/bundle-size_test.ts.snap +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -32,7 +32,7 @@ exports[`scanForBundleSize > should show both decreases and increases when thres | 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | | --- | --- | --- | --- | -| pkg-b | 50 kB | 150 kB | 100 kB | +| pkg-b | 50 kB | 150 kB | +100 kB | | pkg-a | 200 kB | 100 kB | -100 kB |", ] `; @@ -43,7 +43,7 @@ exports[`scanForBundleSize > should show only increases when threshold is -1 and | 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | | --- | --- | --- | --- | -| my-package | 100 kB | 200 kB | 100 kB |", +| my-package | 100 kB | 200 kB | +100 kB |", ] `; @@ -55,6 +55,6 @@ These packages exceed the size increase threshold of 50 kB: | 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | | --- | --- | --- | --- | -| my-package | 100 kB | 200 kB | 100 kB |", +| my-package | 100 kB | 200 kB | +100 kB |", ] `; From 576d8d1b044065b4f9e53039d33c62f6049f6b36 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Tue, 24 Mar 2026 07:55:07 +0000 Subject: [PATCH 5/8] fix: only show 'no bundle size changes' message when threshold is -1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pack-size-threshold is not set (defaults to 50000) and bundle sizes are unchanged, no message should appear — consistent with how all other checks behave: stay silent unless a threshold is exceeded. The 'No bundle size changes' message is now scoped to threshold=-1, which is the only mode where the user has opted into always seeing output. Add a test covering the no-op case for threshold>0 and move the explicit assertion for the no-change message into the threshold=-1 test. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- src/checks/bundle-size.ts | 14 +++++--------- .../__snapshots__/bundle-size_test.ts.snap | 8 -------- test/checks/bundle-size_test.ts | 16 +++++++++++++--- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index 59f23e1..b5e6619 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -16,20 +16,16 @@ export async function scanForBundleSize( } const comparison = comparePackSizes(basePacks, sourcePacks, threshold); - const allUnchanged = comparison.packChanges.every( - (change) => change.sizeChange === 0 - ); - - if (allUnchanged) { - messages.push(`## 📦 Package Bundle Size\n\nNo bundle size changes.`); - return; - } - if (threshold === -1) { const changedPacks = comparison.packChanges.filter( (change) => change.exceedsThreshold ); + if (changedPacks.length === 0) { + messages.push(`## 📦 Package Bundle Size\n\nNo bundle size changes.`); + return; + } + if (changedPacks.length > 0) { const hasDecreases = changedPacks.some((c) => c.sizeChange < 0); const hasIncreases = changedPacks.some((c) => c.sizeChange > 0); diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap index 4d93873..8abeb23 100644 --- a/test/checks/__snapshots__/bundle-size_test.ts.snap +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -10,14 +10,6 @@ exports[`scanForBundleSize > should celebrate size decrease when threshold is -1 ] `; -exports[`scanForBundleSize > should report no bundle size change when diff is 0 1`] = ` -[ - "## 📦 Package Bundle Size - -No bundle size changes.", -] -`; - exports[`scanForBundleSize > should report no bundle size change with threshold=-1 when diff is 0 1`] = ` [ "## 📦 Package Bundle Size diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index b73e0b6..2bcb586 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -25,9 +25,7 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, 50000); - expect(messages).toHaveLength(1); - expect(messages[0]).toContain('No bundle size changes'); - expect(messages).toMatchSnapshot(); + expect(messages).toHaveLength(0); }); it('should report no bundle size change with threshold=-1 when diff is 0', async () => { @@ -37,9 +35,21 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, -1); + expect(messages).toHaveLength(1); + expect(messages[0]).toContain('No bundle size changes'); expect(messages).toMatchSnapshot(); }); + it('should not report anything when diff is 0 and threshold is not -1', async () => { + const messages: string[] = []; + const basePacks = [makePack('my-package', 100000)]; + const sourcePacks = [makePack('my-package', 100000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(0); + }); + it('should warn about size increase exceeding threshold', async () => { const messages: string[] = []; const basePacks = [makePack('my-package', 100000)]; From e4115956ee312da9b5247f39ac00fbab984f20f7 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:37:42 +0000 Subject: [PATCH 6/8] docs: correct README description of no-change bundle size behaviour The message is only posted when pack-size-threshold is -1, not for the default threshold. Update the docs to reflect this. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84740ff..6cdbe9c 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ The action accepts glob patterns to locate package tarballs for comparison: > [!NOTE] > Package bundle analysis only runs when both `base-packages` and `source-packages` are provided. If these inputs are not set, this feature is skipped entirely. -When the bundle size does not change between base and source, a message is posted confirming there was no bundle size change. +When the bundle size does not change between base and source, no message is posted. Set `pack-size-threshold` to `-1` to always report, including a confirmation when there is no change. ### Always Report Bundle Size Changes From 8ab1a37037b5a82c8ebb927a584381ae900c151a Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:40:24 +0000 Subject: [PATCH 7/8] fix: base threshold comparison on sum of increases only, not net change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summing all sizeChanges (positive and negative) meant a large decrease in one package could mask a large increase in another, causing the threshold check to silently skip a real warning. Now only positive changes are summed for the threshold comparison, consistent with the warning table which already filters to increases. sizeChange is signed ((sourceSize ?? 0) - (baseSize ?? 0)), so no Math.abs is needed — filtering to > 0 is sufficient and correct. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- src/checks/bundle-size.ts | 7 +++---- test/checks/__snapshots__/bundle-size_test.ts.snap | 12 ++++++++++++ test/checks/bundle-size_test.ts | 8 ++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index b5e6619..476c3c5 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -62,10 +62,9 @@ ${packRows}` return; } - const totalSizeChange = comparison.packChanges.reduce( - (sum, change) => sum + change.sizeChange, - 0 - ); + const totalSizeChange = comparison.packChanges + .filter((change) => change.sizeChange > 0) + .reduce((sum, change) => sum + change.sizeChange, 0); if (totalSizeChange < threshold) { return; diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap index 8abeb23..f44ec1f 100644 --- a/test/checks/__snapshots__/bundle-size_test.ts.snap +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -39,6 +39,18 @@ exports[`scanForBundleSize > should show only increases when threshold is -1 and ] `; +exports[`scanForBundleSize > should warn about an increase even when a decrease in another package cancels it out in total 1`] = ` +[ + "## ⚠️ Package Size Increase + +These packages exceed the size increase threshold of 50 kB: + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| pkg-b | 50 kB | 150 kB | +100 kB |", +] +`; + exports[`scanForBundleSize > should warn about size increase exceeding threshold 1`] = ` [ "## ⚠️ Package Size Increase diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index 2bcb586..ff4785f 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -70,14 +70,18 @@ describe('scanForBundleSize', () => { expect(messages).toHaveLength(0); }); - it('should not report no-change when individual changes cancel out', async () => { + it('should warn about an increase even when a decrease in another package cancels it out in total', async () => { const messages: string[] = []; + // pkg-a shrinks by 100 KB, pkg-b grows by 100 KB → net = 0, but pkg-b exceeds threshold const basePacks = [makePack('pkg-a', 200000), makePack('pkg-b', 50000)]; const sourcePacks = [makePack('pkg-a', 100000), makePack('pkg-b', 150000)]; await scanForBundleSize(messages, basePacks, sourcePacks, 50000); - expect(messages).toHaveLength(0); + expect(messages).toHaveLength(1); + expect(messages[0]).toContain('pkg-b'); + expect(messages[0]).not.toContain('pkg-a'); + expect(messages).toMatchSnapshot(); }); it('should not report no-change when changes exist but are below threshold', async () => { From 8d65bd4367015e3b35f09fc6547ab33a9a2de597 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:41:36 +0000 Subject: [PATCH 8/8] test: add tests proving threshold is based on increases only, not net change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three new cases that directly encode the reasoning from the fix: 1. A pure decrease, however large, never triggers a warning — sizeChange is signed so a shrink produces a negative value that cannot exceed a positive threshold. 2. A large decrease in one package does not mask an increase in another even when the net total is negative — the old sum-all approach would have returned -400 KB < 50 KB and stayed silent. 3. Multiple sub-threshold increases are summed together and can collectively exceed the threshold — two ×30 KB increases (each below 50 KB) combine to 60 KB and trigger the warning. https://claude.ai/code/session_01Mdf48qd8iQh4WG1F1dbHT1 --- test/checks/bundle-size_test.ts | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index ff4785f..d94f5e6 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -50,6 +50,46 @@ describe('scanForBundleSize', () => { expect(messages).toHaveLength(0); }); + it('should never warn about a pure size decrease, no matter how large', async () => { + const messages: string[] = []; + // sizeChange is signed: (sourceSize - baseSize) = negative for a shrink + // A massive decrease should never cross a positive threshold + const basePacks = [makePack('my-package', 1000000)]; + const sourcePacks = [makePack('my-package', 1)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(0); + }); + + it('should warn even when net total change is negative due to a large decrease masking an increase', async () => { + const messages: string[] = []; + // pkg-a shrinks by 500 KB, pkg-b grows by 100 KB → net = -400 KB + // Without filtering to increases only, -400 KB < 50 KB threshold → silent (wrong) + const basePacks = [makePack('pkg-a', 500000), makePack('pkg-b', 50000)]; + const sourcePacks = [makePack('pkg-a', 0), makePack('pkg-b', 150000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(1); + expect(messages[0]).toContain('pkg-b'); + expect(messages[0]).not.toContain('pkg-a'); + }); + + it('should sum multiple increases when checking against the threshold', async () => { + const messages: string[] = []; + // Each increase is 30 KB (below the 50 KB threshold individually) + // but combined they are 60 KB (above threshold) → should warn + const basePacks = [makePack('pkg-a', 100000), makePack('pkg-b', 100000)]; + const sourcePacks = [makePack('pkg-a', 130000), makePack('pkg-b', 130000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, 50000); + + expect(messages).toHaveLength(1); + expect(messages[0]).toContain('pkg-a'); + expect(messages[0]).toContain('pkg-b'); + }); + it('should warn about size increase exceeding threshold', async () => { const messages: string[] = []; const basePacks = [makePack('my-package', 100000)];