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
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

15 changes: 15 additions & 0 deletions packages/bundlesize/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ For the report option, it must export an object named "report" which is an objec
- `previous`: the previous path to the report to compare against (required, string)
- `current`: the current path to the report to compare against (required, string)
- `columns`: the columns to display (optional, array of objects)
- `threshold`: the minimum gzip size change in bytes to consider as a change (optional, number, defaults to 0). Changes below this threshold are treated as no change.

## Examples

Expand Down Expand Up @@ -184,6 +185,20 @@ export default {
};
```

#### Simple report with custom threshold

By default, all gzip size changes are reported. You can set a threshold to ignore small changes:

```js
export default {
report: {
threshold: 10, // ignore changes smaller than 10 bytes
prev: "stats/previous.json",
current: "stats/current.json"
}
};
```

#### Report with subgroup headers (multiple tables)

You can interleave header objects inside the `sizes` array to break the report output into multiple markdown tables. Each header object must contain a `header` property (any markdown heading is allowed). A sub-bundle total line will be printed after each group along with its diff to previous stats (if available), followed by the overall bundle size and status at the end.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
report: {
previous: "../stats/previous-threshold.json",
current: "../stats/current-threshold.json",
threshold: 0,
},
sizes: [
{
path: "../data/**/file.txt",
limit: "1.5 kB",
},
{
path: "../data/**/file-small-change.txt",
limit: "1.5 kB",
},
{
path: "../data/**/file-no-change.txt",
limit: "1.5 kB",
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
report: {
previous: "../stats/previous-threshold.json",
current: "../stats/current-threshold.json",
threshold: 5,
},
sizes: [
{
path: "../data/**/file.txt",
limit: "1.5 kB",
},
{
path: "../data/**/file-small-change.txt",
limit: "1.5 kB",
},
{
path: "../data/**/file-no-change.txt",
limit: "1.5 kB",
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"0.0.8": {
"data/file.txt": {
"fileSize": 281,
"fileSizeGzip": 1003,
"limit": "1.5 kB",
"passed": true
},
"data/file-small-change.txt": {
"fileSize": 281,
"fileSizeGzip": 1004,
"limit": "1.5 kB",
"passed": true
},
"data/file-no-change.txt": {
"fileSize": 281,
"fileSizeGzip": 1000,
"limit": "1.5 kB",
"passed": true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"0.0.7": {
"data/file.txt": {
"fileSize": 281,
"fileSizeGzip": 1000,
"limit": "1.5 kB",
"passed": true
},
"data/file-small-change.txt": {
"fileSize": 281,
"fileSizeGzip": 1000,
"limit": "1.5 kB",
"passed": true
},
"data/file-no-change.txt": {
"fileSize": 281,
"fileSizeGzip": 1000,
"limit": "1.5 kB",
"passed": true
}
}
}
48 changes: 48 additions & 0 deletions packages/bundlesize/src/__tests__/reportStats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,61 @@ import { fileURLToPath } from "node:url";
import kleur from "kleur";
import { describe, expect, it } from "vitest";

import { DEFAULT_THRESHOLD } from "../defaults.js";
import { reportStats } from "../reportStats.js";
import { STDOUT } from "../utilities.js";

kleur.enabled = false;

const __dirname = path.dirname(fileURLToPath(import.meta.url));

describe("when testing for reportStats with threshold", () => {
it("should ignore changes below the configured threshold", async () => {
// file.txt: 1000 -> 1003 (diff = 3 bytes, below threshold of 5)
// file-small-change.txt: 1000 -> 1004 (diff = 4 bytes, below threshold of 5)
// file-no-change.txt: 1000 -> 1000 (diff = 0 bytes)
const result = await reportStats({
flags: {
configuration: path.join(
__dirname,
"fixtures/configuration/with-threshold.js",
),
},
});

expect(result.exitCode).toBe(0);
// All diffs should be ignored since they are below threshold (5 bytes)
expect(result.data).not.toContain("+3 B");
expect(result.data).not.toContain("+4 B");
// Check that sizes are still reported
expect(result.data).toContain("1000 B");
expect(result.data).toContain("1003 B");
expect(result.data).toContain("1004 B");
});

it("should show all changes when threshold is set to 0", async () => {
// Same files but threshold is 0, so all changes should be shown
const result = await reportStats({
flags: {
configuration: path.join(
__dirname,
"fixtures/configuration/with-threshold-zero.js",
),
},
});

expect(result.exitCode).toBe(0);
// With threshold 0, all non-zero diffs should be shown
expect(result.data).toContain("+3 B");
expect(result.data).toContain("+4 B");
});

it("should use default threshold when not specified in config", async () => {
// Basic config doesn't specify threshold, should use DEFAULT_THRESHOLD (0)
expect(DEFAULT_THRESHOLD).toBe(0);
});
});

describe("when testing for reportStats with errors", () => {
it("should report there is a missing configuration file", async () => {
const result = await reportStats({
Expand Down
2 changes: 2 additions & 0 deletions packages/bundlesize/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export const defaultFlags = {
type: "size",
force: false,
};

export const DEFAULT_THRESHOLD = 0;
15 changes: 13 additions & 2 deletions packages/bundlesize/src/reportStats.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { basename, dirname, join } from "node:path";
import bytes from "bytes";
import { DEFAULT_THRESHOLD } from "./defaults.js";
import type { FooterProperties } from "./utilities.js";
import {
addMDrow,
Expand All @@ -17,6 +18,7 @@ type ReportConfiguration = {
header?: string;
footer?: (arguments_: FooterProperties) => string;
columns?: { [key: string]: string }[];
threshold?: number;
};

interface HeaderSizeEntry {
Expand Down Expand Up @@ -88,6 +90,7 @@ export const reportStats = async ({ flags }): Promise<ReportCompare> => {
let overallDiff = 0;
let totalGzipSize = 0;

const threshold = configuration.report.threshold ?? DEFAULT_THRESHOLD;
const headerString = configuration.report.header || "## Bundle Size";
const footerFormatter = configuration.report.footer || formatFooter;
const columns = configuration.report.columns || [
Expand Down Expand Up @@ -137,7 +140,11 @@ export const reportStats = async ({ flags }): Promise<ReportCompare> => {
if (!hasSubGroup) {
return;
}
const diff = subGroupAccumulatedSize - subGroupAccumulatedPrevSize;
let diff = subGroupAccumulatedSize - subGroupAccumulatedPrevSize;
// Apply threshold: if the absolute diff is below threshold, treat as no change
if (Math.abs(diff) < threshold) {
diff = 0;
}
let diffString = "";
if (diff !== 0 && subGroupAccumulatedPrevSize !== 0) {
const sign = diff > 0 ? "+" : "-";
Expand Down Expand Up @@ -182,7 +189,11 @@ export const reportStats = async ({ flags }): Promise<ReportCompare> => {
previousStats.data[previousVersion] &&
previousStats.data[previousVersion][key];
previousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;
const diff = item.fileSizeGzip - previousFileSizeGzip;
let diff = item.fileSizeGzip - previousFileSizeGzip;
// Apply threshold: if the absolute diff is below threshold, treat as no change
if (Math.abs(diff) < threshold) {
diff = 0;
}
overallDiff += diff;
diffString =
diff === 0 || diff === item.fileSizeGzip
Expand Down
4 changes: 3 additions & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ packages:
- examples/*

onlyBuiltDependencies:
- '@swc/core'
- "@swc/core"
- esbuild

shamefullyHoist: true