Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
| `pack-size-threshold` | Threshold (in bytes) for warning about significant increase in total pack size | 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 |
| 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 |

## Example with custom inputs

Expand Down Expand Up @@ -93,11 +93,23 @@ 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

> [!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.

### Always Report Bundle Size Changes

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
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
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
35 changes: 26 additions & 9 deletions build/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -24815,26 +24815,39 @@ ${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");
const thresholdText = threshold === -1 ? "" : `
These packages exceed the size change threshold of ${formatBytes(threshold)}:
`;
messages.push(
`## \u26A0\uFE0F Package Size Increase

These packages exceed the size increase threshold of ${formatBytes(threshold)}:

`${heading}
${thresholdText}
| \u{1F4E6} Package | \u{1F4CF} Base Size | \u{1F4CF} Source Size | \u{1F4C8} Size Change |
| --- | --- | --- | --- |
${packRows}`
Expand Down Expand Up @@ -24984,7 +24997,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(
Expand All @@ -24996,7 +25011,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);
Expand Down
33 changes: 27 additions & 6 deletions src/checks/bundle-size.ts
Original file line number Diff line number Diff line change
@@ -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[],
Expand All @@ -11,27 +15,44 @@ export async function scanForBundleSize(
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(`## 📦 Package Bundle Size\n\nNo 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
? '## 📦 Package Bundle Size Changes'
: hasDecreases
? '## 🎉 Package Size Decrease'
: '## ⚠️ 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(
`## ⚠️ Package Size Increase

These packages exceed the size increase threshold of ${formatBytes(threshold)}:
const thresholdText =
threshold === -1
? ''
: `\nThese packages exceed the size change threshold of ${formatBytes(threshold)}:\n`;

messages.push(
`${heading}
${thresholdText}
| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change |
| --- | --- | --- | --- |
${packRows}`
Expand Down
3 changes: 2 additions & 1 deletion src/packs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
84 changes: 84 additions & 0 deletions test/checks/__snapshots__/bundle-size_test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

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 report 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 show both decreases and increases when threshold is -1 1`] = `
[
"## 📦 Package Bundle Size Changes

| 📦 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 |
| --- | --- | --- | --- |
| my-package | 100 kB | 200 kB | +100 kB |",
]
`;

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 change 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

These packages exceed the size change threshold of 50 kB:

| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change |
| --- | --- | --- | --- |
| my-package | 100 kB | 200 kB | +100 kB |",
]
`;
Loading