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 01/13] 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 02/13] 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 03/13] 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 04/13] 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 05/13] 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 06/13] 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 07/13] 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 08/13] 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)]; From b341eb869c619a0fcc7d98c192826e70f817e994 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:28:28 +0100 Subject: [PATCH 09/13] refactor: consolidate bundle size logic --- README.md | 3 -- build/main.js | 28 +++++++++++---- src/checks/bundle-size.ts | 71 +++++++++------------------------------ 3 files changed, 37 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 600e2c6..0ad489d 100644 --- a/README.md +++ b/README.md @@ -93,13 +93,10 @@ 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. 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, 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 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 🎉: diff --git a/build/main.js b/build/main.js index 8dd934b..b2e9225 100644 --- a/build/main.js +++ b/build/main.js @@ -24393,7 +24393,7 @@ function comparePackSizes(basePacks, sourcePacks, threshold) { const baseSize = basePack?.size ?? null; 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, baseSize, @@ -24815,23 +24815,35 @@ ${provenanceRows.join("\n")}` } // src/checks/bundle-size.ts +function formatBytesSigned(bytes) { + return `${bytes > 0 ? "+" : "-"}${formatBytes(bytes)}`; +} async function scanForBundleSize(messages, basePacks, sourcePacks, threshold) { if (basePacks.length === 0 && sourcePacks.length === 0) { return; } const comparison = comparePackSizes(basePacks, sourcePacks, threshold); const packWarnings = comparison.packChanges.filter( - (change) => change.exceedsThreshold && change.sizeChange > 0 + (change) => change.exceedsThreshold ); + if (threshold === -1 && packWarnings.length === 0) { + messages.push(`## \u{1F4E6} Package Bundle Size + +No bundle size changes.`); + return; + } if (packWarnings.length > 0) { + const hasDecreases = packWarnings.some((c) => c.sizeChange < 0); + const hasIncreases = packWarnings.some((c) => c.sizeChange > 0); + const heading = hasDecreases && hasIncreases ? "## \u{1F4E6} Package Bundle Size Changes" : hasDecreases ? "## \u{1F389} Package Size Decrease" : "## \u26A0\uFE0F Package Size Increase"; const packRows = packWarnings.map((change) => { const baseSize = change.baseSize ? formatBytes(change.baseSize) : "New"; 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"); messages.push( - `## \u26A0\uFE0F Package Size Increase + `${heading} These packages exceed the size increase threshold of ${formatBytes(threshold)}: @@ -24984,7 +24996,9 @@ async function analyzeAndComment() { `Parsed current lockfile with ${parsedCurrentLock.packages.length} packages` ); } catch (err) { - throw new Error(`Failed to parse current lockfile: ${err}`); + throw new Error(`Failed to parse current lockfile: ${err}`, { + cause: err + }); } try { parsedBaseLock = await parse2( @@ -24996,7 +25010,9 @@ async function analyzeAndComment() { `Parsed base lockfile with ${parsedBaseLock.packages.length} packages` ); } catch (err) { - throw new Error(`Failed to parse base lockfile: ${err}`); + throw new Error(`Failed to parse base lockfile: ${err}`, { + cause: err + }); } const currentDeps = computeDependencyVersions(parsedCurrentLock); const baseDeps = computeDependencyVersions(parsedBaseLock); diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index 476c3c5..3ad21d3 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -2,7 +2,7 @@ import {formatBytes} from '../common.js'; import {comparePackSizes, type PackInfo} from '../packs.js'; function formatBytesSigned(bytes: number): string { - return `${bytes > 0 ? '+' : ''}${formatBytes(bytes)}`; + return `${bytes > 0 ? '+' : '-'}${formatBytes(bytes)}`; } export async function scanForBundleSize( @@ -16,65 +16,24 @@ export async function scanForBundleSize( } const comparison = comparePackSizes(basePacks, sourcePacks, threshold); - 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); - - const heading = - hasDecreases && hasIncreases - ? '## 📦 Package Bundle Size Changes' - : hasDecreases - ? '## 🎉 Package Size Decrease' - : '## ⚠️ Package Size Increase'; - - const packRows = changedPacks - .map((change) => { - const baseSize = change.baseSize - ? formatBytes(change.baseSize) - : 'New'; - const sourceSize = change.sourceSize - ? formatBytes(change.sourceSize) - : 'Removed'; - const sizeChange = formatBytesSigned(change.sizeChange); - return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; - }) - .join('\n'); - - messages.push( - `${heading} - -| 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | -| --- | --- | --- | --- | -${packRows}` - ); - } - - return; - } - - const totalSizeChange = comparison.packChanges - .filter((change) => change.sizeChange > 0) - .reduce((sum, change) => sum + change.sizeChange, 0); + const packWarnings = comparison.packChanges.filter( + (change) => change.exceedsThreshold + ); - if (totalSizeChange < threshold) { + if (threshold === -1 && packWarnings.length === 0) { + messages.push(`## 📦 Package Bundle Size\n\nNo bundle size changes.`); return; } - const packWarnings = comparison.packChanges.filter( - (change) => change.sizeChange > 0 - ); - if (packWarnings.length > 0) { + const hasDecreases = packWarnings.some((c) => c.sizeChange < 0); + const hasIncreases = packWarnings.some((c) => c.sizeChange > 0); + const heading = + hasDecreases && hasIncreases + ? '## 📦 Package Bundle Size Changes' + : hasDecreases + ? '## 🎉 Package Size Decrease' + : '## ⚠️ Package Size Increase'; const packRows = packWarnings .map((change) => { const baseSize = change.baseSize ? formatBytes(change.baseSize) : 'New'; @@ -87,7 +46,7 @@ ${packRows}` .join('\n'); messages.push( - `## ⚠️ Package Size Increase + `${heading} These packages exceed the size increase threshold of ${formatBytes(threshold)}: From 5fbecbe4f9adf9067f65dbd7ed57665fde5e8e72 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:32:28 +0100 Subject: [PATCH 10/13] fix: remove extra minus --- build/main.js | 9 +++++---- src/checks/bundle-size.ts | 11 +++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/build/main.js b/build/main.js index b2e9225..d85813c 100644 --- a/build/main.js +++ b/build/main.js @@ -24816,7 +24816,7 @@ ${provenanceRows.join("\n")}` // src/checks/bundle-size.ts function formatBytesSigned(bytes) { - return `${bytes > 0 ? "+" : "-"}${formatBytes(bytes)}`; + return `${bytes > 0 ? "+" : ""}${formatBytes(bytes)}`; } async function scanForBundleSize(messages, basePacks, sourcePacks, threshold) { if (basePacks.length === 0 && sourcePacks.length === 0) { @@ -24842,11 +24842,12 @@ No bundle size changes.`); const sizeChange = formatBytesSigned(change.sizeChange); return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; }).join("\n"); + const thresholdText = threshold === -1 ? "" : ` +These packages exceed the size change threshold of ${formatBytes(threshold)}. +`; messages.push( `${heading} - -These packages exceed the size increase threshold of ${formatBytes(threshold)}: - +${thresholdText} | \u{1F4E6} Package | \u{1F4CF} Base Size | \u{1F4CF} Source Size | \u{1F4C8} Size Change | | --- | --- | --- | --- | ${packRows}` diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index 3ad21d3..fc928c9 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -2,7 +2,7 @@ import {formatBytes} from '../common.js'; import {comparePackSizes, type PackInfo} from '../packs.js'; function formatBytesSigned(bytes: number): string { - return `${bytes > 0 ? '+' : '-'}${formatBytes(bytes)}`; + return `${bytes > 0 ? '+' : ''}${formatBytes(bytes)}`; } export async function scanForBundleSize( @@ -45,11 +45,14 @@ export async function scanForBundleSize( }) .join('\n'); + const thresholdText = + threshold === -1 + ? '' + : `\nThese packages exceed the size change threshold of ${formatBytes(threshold)}.\n`; + messages.push( `${heading} - -These packages exceed the size increase threshold of ${formatBytes(threshold)}: - +${thresholdText} | 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | | --- | --- | --- | --- | ${packRows}` From 4f1b86e93316becc227f3c15847c9bb9d4a061c1 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:42:47 +0100 Subject: [PATCH 11/13] test: clean up tests --- README.md | 2 +- build/main.js | 2 +- src/checks/bundle-size.ts | 2 +- .../__snapshots__/bundle-size_test.ts.snap | 46 ++++++++---- test/checks/bundle-size_test.ts | 75 ++++++------------- 5 files changed, 58 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 0ad489d..afde6e4 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ The action accepts glob patterns to locate package tarballs for comparison: ### Always Report Bundle Size Changes -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 🎉: +To always report bundle size changes, set `pack-size-threshold` to `-1`. This will display bundle size differences even if they are reductions, giving you full visibility into how your changes affect the published package size. ```yaml - name: Create Diff diff --git a/build/main.js b/build/main.js index d85813c..1e3815f 100644 --- a/build/main.js +++ b/build/main.js @@ -24843,7 +24843,7 @@ No bundle size changes.`); return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`; }).join("\n"); const thresholdText = threshold === -1 ? "" : ` -These packages exceed the size change threshold of ${formatBytes(threshold)}. +These packages exceed the size change threshold of ${formatBytes(threshold)}: `; messages.push( `${heading} diff --git a/src/checks/bundle-size.ts b/src/checks/bundle-size.ts index fc928c9..cd1ef6d 100644 --- a/src/checks/bundle-size.ts +++ b/src/checks/bundle-size.ts @@ -48,7 +48,7 @@ export async function scanForBundleSize( const thresholdText = threshold === -1 ? '' - : `\nThese packages exceed the size change threshold of ${formatBytes(threshold)}.\n`; + : `\nThese packages exceed the size change threshold of ${formatBytes(threshold)}:\n`; messages.push( `${heading} diff --git a/test/checks/__snapshots__/bundle-size_test.ts.snap b/test/checks/__snapshots__/bundle-size_test.ts.snap index f44ec1f..af71a71 100644 --- a/test/checks/__snapshots__/bundle-size_test.ts.snap +++ b/test/checks/__snapshots__/bundle-size_test.ts.snap @@ -1,20 +1,20 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`scanForBundleSize > should celebrate size decrease when threshold is -1 1`] = ` +exports[`scanForBundleSize > should report no bundle size change with threshold=-1 when diff is 0 1`] = ` [ - "## 🎉 Package Size Decrease + "## 📦 Package Bundle Size -| 📦 Package | 📏 Base Size | 📏 Source Size | 📊 Size Change | -| --- | --- | --- | --- | -| my-package | 200 kB | 100 kB | -100 kB |", +No bundle size changes.", ] `; -exports[`scanForBundleSize > should report no bundle size change with threshold=-1 when diff is 0 1`] = ` +exports[`scanForBundleSize > should report size decrease when threshold is -1 1`] = ` [ - "## 📦 Package Bundle Size + "## 🎉 Package Size Decrease -No bundle size changes.", +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| my-package | 200 kB | 100 kB | -100 kB |", ] `; @@ -22,28 +22,48 @@ exports[`scanForBundleSize > should show both decreases and increases when thres [ "## 📦 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-a | 200 kB | 100 kB | -100 kB |", ] `; +exports[`scanForBundleSize > should show new packages when threshold is -1 1`] = ` +[ + "## ⚠️ Package Size Increase + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| new-package | New | 50 kB | +50 kB |", +] +`; + exports[`scanForBundleSize > should show only increases when threshold is -1 and no decreases 1`] = ` [ "## ⚠️ 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 |", ] `; -exports[`scanForBundleSize > should warn about an increase even when a decrease in another package cancels it out in total 1`] = ` +exports[`scanForBundleSize > should show removed packages when threshold is -1 1`] = ` +[ + "## 🎉 Package Size Decrease + +| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | +| --- | --- | --- | --- | +| old-package | 50 kB | Removed | -50 kB |", +] +`; + +exports[`scanForBundleSize > should warn about an increase regardless of other packages decreasing 1`] = ` [ "## ⚠️ Package Size Increase -These packages exceed the size increase threshold of 50 kB: +These packages exceed the size change threshold of 50 kB: | 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | | --- | --- | --- | --- | @@ -55,7 +75,7 @@ exports[`scanForBundleSize > should warn about size increase exceeding threshold [ "## ⚠️ Package Size Increase -These packages exceed the size increase threshold of 50 kB: +These packages exceed the size change threshold of 50 kB: | 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change | | --- | --- | --- | --- | diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index d94f5e6..7ac1e0b 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -50,46 +50,6 @@ 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)]; @@ -110,9 +70,8 @@ describe('scanForBundleSize', () => { expect(messages).toHaveLength(0); }); - it('should warn about an increase even when a decrease in another package cancels it out in total', async () => { + it('should warn about an increase regardless of other packages decreasing', 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)]; @@ -124,17 +83,7 @@ describe('scanForBundleSize', () => { expect(messages).toMatchSnapshot(); }); - 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 () => { + it('should report size decrease when threshold is -1', async () => { const messages: string[] = []; const basePacks = [makePack('my-package', 200000)]; const sourcePacks = [makePack('my-package', 100000)]; @@ -164,4 +113,24 @@ describe('scanForBundleSize', () => { expect(messages).toMatchSnapshot(); }); + + it('should show new packages when threshold is -1', async () => { + const messages: string[] = []; + const basePacks: PackInfo[] = []; + const sourcePacks = [makePack('new-package', 50000)]; + + await scanForBundleSize(messages, basePacks, sourcePacks, -1); + + expect(messages).toMatchSnapshot(); + }); + + it('should show removed packages when threshold is -1', async () => { + const messages: string[] = []; + const basePacks = [makePack('old-package', 50000)]; + const sourcePacks: PackInfo[] = []; + + await scanForBundleSize(messages, basePacks, sourcePacks, -1); + + expect(messages).toMatchSnapshot(); + }); }); From 8c12799b859ce08e7f20475b0e096ac146bb7b96 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:46:26 +0100 Subject: [PATCH 12/13] test: remove redundant assertions --- test/checks/bundle-size_test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/checks/bundle-size_test.ts b/test/checks/bundle-size_test.ts index 7ac1e0b..a9a691e 100644 --- a/test/checks/bundle-size_test.ts +++ b/test/checks/bundle-size_test.ts @@ -15,6 +15,7 @@ describe('scanForBundleSize', () => { it('should do nothing when no packs are provided', async () => { const messages: string[] = []; await scanForBundleSize(messages, [], [], 50000); + expect(messages).toHaveLength(0); }); @@ -35,8 +36,6 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, -1); - expect(messages).toHaveLength(1); - expect(messages[0]).toContain('No bundle size changes'); expect(messages).toMatchSnapshot(); }); @@ -77,9 +76,6 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, 50000); - expect(messages).toHaveLength(1); - expect(messages[0]).toContain('pkg-b'); - expect(messages[0]).not.toContain('pkg-a'); expect(messages).toMatchSnapshot(); }); @@ -100,7 +96,6 @@ describe('scanForBundleSize', () => { await scanForBundleSize(messages, basePacks, sourcePacks, -1); - expect(messages).toHaveLength(1); expect(messages).toMatchSnapshot(); }); From b12d3e2df34b716b6bf371ce10f3352f848c520e Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:47:26 +0100 Subject: [PATCH 13/13] chore: format readme --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index afde6e4..9deecf7 100644 --- a/README.md +++ b/README.md @@ -37,21 +37,21 @@ jobs: ## Inputs -| Name | Description | Required | Default | -| ---------------------- | ------------------------------------------------------------------------------ | -------- | ----------------------------------------- | -| `base-ref` | Base ref to compare against (defaults to main or PR target) | No | Auto-detected from PR or `main` | -| `github-token` | The GitHub token for authentication | Yes | `${{ github.token }}` | -| `pr-number` | The number of the pull request to comment on | Yes | `${{ github.event.pull_request.number }}` | -| `dependency-threshold` | Threshold for warning about significant increase in number of dependencies | No | `10` | -| `size-threshold` | Threshold (in bytes) for warning about significant increase in package size | No | `100000` | -| `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 | +| Name | Description | Required | Default | +| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------- | +| `base-ref` | Base ref to compare against (defaults to main or PR target) | No | Auto-detected from PR or `main` | +| `github-token` | The GitHub token for authentication | Yes | `${{ github.token }}` | +| `pr-number` | The number of the pull request to comment on | Yes | `${{ github.event.pull_request.number }}` | +| `dependency-threshold` | Threshold for warning about significant increase in number of dependencies | No | `10` | +| `size-threshold` | Threshold (in bytes) for warning about significant increase in package size | No | `100000` | +| `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. 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 | -| `mode` | Run mode: `comment`, `artifact`, or `comment-from-artifact` | No | `comment` | -| `artifact-path` | Path to the artifact JSON file (for `comment-from-artifact` mode) | No | None | +| `detect-replacements` | Detect modules which have community suggested alternatives | No | `true` | +| `working-directory` | Working directory to scan for package lock file | No | None | +| `mode` | Run mode: `comment`, `artifact`, or `comment-from-artifact` | No | `comment` | +| `artifact-path` | Path to the artifact JSON file (for `comment-from-artifact` mode) | No | None | ## Example with custom inputs