@node-cli/bundlesize 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/reportStats.js +7 -5
- package/dist/reportStats.js.map +1 -1
- package/package.json +3 -2
- package/schemas/bundlesize.config.schema.json +151 -0
package/README.md
CHANGED
|
@@ -14,6 +14,39 @@
|
|
|
14
14
|
|
|
15
15
|
A configuration file must be provided via the `-c` parameter.
|
|
16
16
|
|
|
17
|
+
### JSON Schema Support
|
|
18
|
+
|
|
19
|
+
For IDE autocompletion and validation, you can add a `$schema` property to your configuration file:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
export default {
|
|
23
|
+
$schema:
|
|
24
|
+
"https://cdn.jsdelivr.net/npm/@node-cli/bundlesize/schemas/bundlesize.config.schema.json",
|
|
25
|
+
sizes: [
|
|
26
|
+
{
|
|
27
|
+
path: "dist/bundle.js",
|
|
28
|
+
limit: "10 kB"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This provides:
|
|
35
|
+
|
|
36
|
+
- Autocompletion for configuration options
|
|
37
|
+
- Inline documentation for each property
|
|
38
|
+
- Validation of configuration values
|
|
39
|
+
|
|
40
|
+
**Note**: For a specific version of the schema, use:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
https://cdn.jsdelivr.net/npm/@node-cli/bundlesize@VERSION/schemas/bundlesize.config.schema.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Configuration Options
|
|
47
|
+
|
|
48
|
+
### Configuration Options
|
|
49
|
+
|
|
17
50
|
For the size option, it must export an object named "size" which is an array of objects with the following properties:
|
|
18
51
|
|
|
19
52
|
- `path`: the path to the file to check
|
|
@@ -199,6 +232,8 @@ export default {
|
|
|
199
232
|
};
|
|
200
233
|
```
|
|
201
234
|
|
|
235
|
+
**Note on threshold with subgroups**: When using subgroup headers, the threshold is applied consistently. If individual file changes are below the threshold (and thus treated as zero), the sub-bundle total will also reflect this. For example, if file A changes by +3 bytes and file B changes by +4 bytes with a threshold of 5, both individual changes are ignored, and the sub-bundle will show no change (even though the raw total would be +7 bytes).
|
|
236
|
+
|
|
202
237
|
#### Report with subgroup headers (multiple tables)
|
|
203
238
|
|
|
204
239
|
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.
|
package/dist/reportStats.js
CHANGED
|
@@ -105,17 +105,15 @@ export const reportStats = async ({ flags })=>{
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
let subGroupAccumulatedSize = 0;
|
|
108
|
+
let subGroupAccumulatedDiff = 0;
|
|
108
109
|
let subGroupAccumulatedPrevSize = 0;
|
|
109
110
|
let hasSubGroup = false;
|
|
110
111
|
const flushSubGroup = ()=>{
|
|
111
112
|
if (!hasSubGroup) {
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (Math.abs(diff) < threshold) {
|
|
117
|
-
diff = 0;
|
|
118
|
-
}
|
|
115
|
+
// Use the accumulated thresholded diff directly
|
|
116
|
+
const diff = subGroupAccumulatedDiff;
|
|
119
117
|
let diffString = "";
|
|
120
118
|
if (diff !== 0 && subGroupAccumulatedPrevSize !== 0) {
|
|
121
119
|
const sign = diff > 0 ? "+" : "-";
|
|
@@ -129,6 +127,7 @@ export const reportStats = async ({ flags })=>{
|
|
|
129
127
|
})}${diffString}`, "");
|
|
130
128
|
}
|
|
131
129
|
subGroupAccumulatedSize = 0;
|
|
130
|
+
subGroupAccumulatedDiff = 0;
|
|
132
131
|
subGroupAccumulatedPrevSize = 0;
|
|
133
132
|
hasSubGroup = false;
|
|
134
133
|
};
|
|
@@ -153,6 +152,7 @@ export const reportStats = async ({ flags })=>{
|
|
|
153
152
|
}
|
|
154
153
|
let diffString = "";
|
|
155
154
|
let previousFileSizeGzip = 0;
|
|
155
|
+
let thresholdedDiff = 0;
|
|
156
156
|
if (previousStats && previousVersion) {
|
|
157
157
|
const previousFileStats = previousStats.data[previousVersion] && previousStats.data[previousVersion][key];
|
|
158
158
|
previousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;
|
|
@@ -161,6 +161,7 @@ export const reportStats = async ({ flags })=>{
|
|
|
161
161
|
if (Math.abs(diff) < threshold) {
|
|
162
162
|
diff = 0;
|
|
163
163
|
}
|
|
164
|
+
thresholdedDiff = diff;
|
|
164
165
|
overallDiff += diff;
|
|
165
166
|
diffString = diff === 0 || diff === item.fileSizeGzip ? "" : ` (${diff > 0 ? "+" : "-"}${bytes(Math.abs(diff), {
|
|
166
167
|
unitSeparator: " "
|
|
@@ -168,6 +169,7 @@ export const reportStats = async ({ flags })=>{
|
|
|
168
169
|
}
|
|
169
170
|
totalGzipSize += item.fileSizeGzip;
|
|
170
171
|
subGroupAccumulatedSize += item.fileSizeGzip;
|
|
172
|
+
subGroupAccumulatedDiff += thresholdedDiff;
|
|
171
173
|
subGroupAccumulatedPrevSize += previousFileSizeGzip;
|
|
172
174
|
hasSubGroup = true;
|
|
173
175
|
rowsMD.push(addMDrow({
|
package/dist/reportStats.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reportStats.ts"],"sourcesContent":["import { basename, dirname, join } from \"node:path\";\nimport bytes from \"bytes\";\nimport { DEFAULT_THRESHOLD } from \"./defaults.js\";\nimport type { FooterProperties } from \"./utilities.js\";\nimport {\n\taddMDrow,\n\tformatFooter,\n\tgetMostRecentVersion,\n\tgetOutputFile,\n\tpercentFormatter,\n\treadJSONFile,\n\tvalidateConfigurationFile,\n} from \"./utilities.js\";\n\ntype ReportConfiguration = {\n\tcurrent: string;\n\tprevious: string;\n\theader?: string;\n\tfooter?: (arguments_: FooterProperties) => string;\n\tcolumns?: { [key: string]: string }[];\n\tthreshold?: number;\n};\n\ninterface HeaderSizeEntry {\n\theader: string;\n}\ninterface FileSizeEntry {\n\tpath: string;\n\tlimit: string;\n\talias?: string;\n}\ntype SizesConfiguration = Array<HeaderSizeEntry | FileSizeEntry>;\n\nconst isHeaderEntry = (\n\tentry: HeaderSizeEntry | FileSizeEntry,\n): entry is HeaderSizeEntry => \"header\" in entry;\n\ntype ReportCompare = {\n\tdata: string;\n\texitCode: number;\n\texitMessage: string;\n\toutputFile: string;\n};\n\nexport const reportStats = async ({ flags }): Promise<ReportCompare> => {\n\tconst result: ReportCompare = {\n\t\texitCode: 0,\n\t\texitMessage: \"\",\n\t\toutputFile: \"\",\n\t\tdata: \"\",\n\t};\n\n\tlet previousStats, previousVersion: string;\n\tconst isValidConfigResult = validateConfigurationFile(flags.configuration);\n\tif (isValidConfigResult.exitMessage !== \"\") {\n\t\treturn { ...result, ...isValidConfigResult };\n\t}\n\n\tconst configurationFile = isValidConfigResult.data;\n\tconst outputFile = getOutputFile(flags.output);\n\tresult.outputFile = outputFile;\n\n\tconst configuration: {\n\t\treport: ReportConfiguration;\n\t\tsizes?: SizesConfiguration;\n\t} = await import(configurationFile).then((m) => m.default);\n\n\tconst currentStats = readJSONFile(\n\t\tjoin(dirname(configurationFile), configuration.report.current),\n\t);\n\tif (currentStats.exitMessage !== \"\") {\n\t\treturn { ...result, ...currentStats };\n\t}\n\n\ttry {\n\t\tpreviousStats = readJSONFile(\n\t\t\tjoin(dirname(configurationFile), configuration.report.previous),\n\t\t);\n\t\tif (previousStats.exitMessage !== \"\") {\n\t\t\treturn { ...result, ...previousStats };\n\t\t}\n\t\tpreviousVersion = getMostRecentVersion(previousStats.data);\n\t} catch {\n\t\t// no previous stats available.\n\t}\n\n\tconst currentVersion = getMostRecentVersion(currentStats.data);\n\n\tlet limitReached = false;\n\tlet overallDiff = 0;\n\tlet totalGzipSize = 0;\n\n\tconst threshold = configuration.report.threshold ?? DEFAULT_THRESHOLD;\n\tconst headerString = configuration.report.header || \"## Bundle Size\";\n\tconst footerFormatter = configuration.report.footer || formatFooter;\n\tconst columns = configuration.report.columns || [\n\t\t{ status: \"Status\" },\n\t\t{ file: \"File\" },\n\t\t{ size: \"Size (Gzip)\" },\n\t\t{ limits: \"Limits\" },\n\t];\n\n\tconst rowsMD: string[] = [];\n\tconst hasGroupHeaders = Boolean(configuration.sizes?.some(isHeaderEntry));\n\tif (!hasGroupHeaders) {\n\t\trowsMD.push(addMDrow({ type: \"header\", columns }));\n\t}\n\n\t// Build ordered keys based on configuration.sizes if present.\n\tconst orderedKeys: Array<{\n\t\ttype: \"header\" | \"item\";\n\t\tvalue: string;\n\t\theader?: string;\n\t}> = [];\n\tif (configuration.sizes && Array.isArray(configuration.sizes)) {\n\t\tfor (const entry of configuration.sizes) {\n\t\t\tif (isHeaderEntry(entry)) {\n\t\t\t\torderedKeys.push({ type: \"header\", value: \"\", header: entry.header });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// entry is FileSizeEntry here.\n\t\t\tif (entry.alias && currentStats.data[currentVersion][entry.alias]) {\n\t\t\t\torderedKeys.push({ type: \"item\", value: entry.alias });\n\t\t\t} else if (currentStats.data[currentVersion][entry.path]) {\n\t\t\t\torderedKeys.push({ type: \"item\", value: entry.path });\n\t\t\t}\n\t\t}\n\t}\n\n\tif (orderedKeys.length === 0) {\n\t\tfor (const key of Object.keys(currentStats.data[currentVersion])) {\n\t\t\torderedKeys.push({ type: \"item\", value: key });\n\t\t}\n\t}\n\n\tlet subGroupAccumulatedSize = 0;\n\tlet subGroupAccumulatedPrevSize = 0;\n\tlet hasSubGroup = false;\n\tconst flushSubGroup = () => {\n\t\tif (!hasSubGroup) {\n\t\t\treturn;\n\t\t}\n\t\tlet diff = subGroupAccumulatedSize - subGroupAccumulatedPrevSize;\n\t\t// Apply threshold: if the absolute diff is below threshold, treat as no change\n\t\tif (Math.abs(diff) < threshold) {\n\t\t\tdiff = 0;\n\t\t}\n\t\tlet diffString = \"\";\n\t\tif (diff !== 0 && subGroupAccumulatedPrevSize !== 0) {\n\t\t\tconst sign = diff > 0 ? \"+\" : \"-\";\n\t\t\tdiffString = ` (${sign}${bytes(Math.abs(diff), { unitSeparator: \" \" })} ${percentFormatter(\n\t\t\t\tdiff / subGroupAccumulatedPrevSize,\n\t\t\t)})`;\n\t\t}\n\t\tif (hasGroupHeaders) {\n\t\t\trowsMD.push(\n\t\t\t\t`\\nSub-bundle size: ${bytes(subGroupAccumulatedSize, {\n\t\t\t\t\tunitSeparator: \" \",\n\t\t\t\t})}${diffString}`,\n\t\t\t\t\"\",\n\t\t\t);\n\t\t}\n\t\tsubGroupAccumulatedSize = 0;\n\t\tsubGroupAccumulatedPrevSize = 0;\n\t\thasSubGroup = false;\n\t};\n\n\tfor (const entry of orderedKeys) {\n\t\tif (entry.type === \"header\") {\n\t\t\tflushSubGroup();\n\t\t\trowsMD.push(\"\", entry.header as string, \"\");\n\t\t\trowsMD.push(addMDrow({ type: \"header\", columns }));\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = entry.value;\n\t\tconst item = currentStats.data[currentVersion][key];\n\t\tif (!item) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst name = basename(key);\n\t\tif (!item.passed) {\n\t\t\tlimitReached = true;\n\t\t}\n\n\t\tlet diffString = \"\";\n\t\tlet previousFileSizeGzip = 0;\n\t\tif (previousStats && previousVersion) {\n\t\t\tconst previousFileStats =\n\t\t\t\tpreviousStats.data[previousVersion] &&\n\t\t\t\tpreviousStats.data[previousVersion][key];\n\t\t\tpreviousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;\n\t\t\tlet diff = item.fileSizeGzip - previousFileSizeGzip;\n\t\t\t// Apply threshold: if the absolute diff is below threshold, treat as no change\n\t\t\tif (Math.abs(diff) < threshold) {\n\t\t\t\tdiff = 0;\n\t\t\t}\n\t\t\toverallDiff += diff;\n\t\t\tdiffString =\n\t\t\t\tdiff === 0 || diff === item.fileSizeGzip\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: ` (${diff > 0 ? \"+\" : \"-\"}${bytes(Math.abs(diff), { unitSeparator: \" \" })} ${percentFormatter(\n\t\t\t\t\t\t\tpreviousFileSizeGzip === 0 ? 0 : diff / previousFileSizeGzip,\n\t\t\t\t\t\t)})`;\n\t\t}\n\n\t\ttotalGzipSize += item.fileSizeGzip;\n\t\tsubGroupAccumulatedSize += item.fileSizeGzip;\n\t\tsubGroupAccumulatedPrevSize += previousFileSizeGzip;\n\t\thasSubGroup = true;\n\n\t\trowsMD.push(\n\t\t\taddMDrow({\n\t\t\t\ttype: \"row\",\n\t\t\t\tpassed: item.passed,\n\t\t\t\tname: name,\n\t\t\t\tsize: item.fileSizeGzip,\n\t\t\t\tdiff: diffString,\n\t\t\t\tlimit: item.limit,\n\t\t\t\tcolumns,\n\t\t\t}),\n\t\t);\n\t}\n\n\tflushSubGroup();\n\n\tconst template = `\n${headerString}\n${rowsMD.join(\"\\n\")}\n\n${footerFormatter({ limitReached, overallDiff, totalGzipSize })}\n`;\n\n\tif (limitReached) {\n\t\tresult.exitCode = 1;\n\t}\n\tresult.data = template;\n\treturn result;\n};\n"],"names":["basename","dirname","join","bytes","DEFAULT_THRESHOLD","addMDrow","formatFooter","getMostRecentVersion","getOutputFile","percentFormatter","readJSONFile","validateConfigurationFile","isHeaderEntry","entry","reportStats","flags","result","exitCode","exitMessage","outputFile","data","previousStats","previousVersion","isValidConfigResult","configuration","configurationFile","output","then","m","default","currentStats","report","current","previous","currentVersion","limitReached","overallDiff","totalGzipSize","threshold","headerString","header","footerFormatter","footer","columns","status","file","size","limits","rowsMD","hasGroupHeaders","Boolean","sizes","some","push","type","orderedKeys","Array","isArray","value","alias","path","length","key","Object","keys","subGroupAccumulatedSize","subGroupAccumulatedPrevSize","hasSubGroup","flushSubGroup","diff","Math","abs","diffString","sign","unitSeparator","item","name","passed","previousFileSizeGzip","previousFileStats","fileSizeGzip","limit","template"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/reportStats.ts"],"sourcesContent":["import { basename, dirname, join } from \"node:path\";\nimport bytes from \"bytes\";\nimport { DEFAULT_THRESHOLD } from \"./defaults.js\";\nimport type { FooterProperties } from \"./utilities.js\";\nimport {\n\taddMDrow,\n\tformatFooter,\n\tgetMostRecentVersion,\n\tgetOutputFile,\n\tpercentFormatter,\n\treadJSONFile,\n\tvalidateConfigurationFile,\n} from \"./utilities.js\";\n\ntype ReportConfiguration = {\n\tcurrent: string;\n\tprevious: string;\n\theader?: string;\n\tfooter?: (arguments_: FooterProperties) => string;\n\tcolumns?: { [key: string]: string }[];\n\tthreshold?: number;\n};\n\ninterface HeaderSizeEntry {\n\theader: string;\n}\ninterface FileSizeEntry {\n\tpath: string;\n\tlimit: string;\n\talias?: string;\n}\ntype SizesConfiguration = Array<HeaderSizeEntry | FileSizeEntry>;\n\nconst isHeaderEntry = (\n\tentry: HeaderSizeEntry | FileSizeEntry,\n): entry is HeaderSizeEntry => \"header\" in entry;\n\ntype ReportCompare = {\n\tdata: string;\n\texitCode: number;\n\texitMessage: string;\n\toutputFile: string;\n};\n\nexport const reportStats = async ({ flags }): Promise<ReportCompare> => {\n\tconst result: ReportCompare = {\n\t\texitCode: 0,\n\t\texitMessage: \"\",\n\t\toutputFile: \"\",\n\t\tdata: \"\",\n\t};\n\n\tlet previousStats, previousVersion: string;\n\tconst isValidConfigResult = validateConfigurationFile(flags.configuration);\n\tif (isValidConfigResult.exitMessage !== \"\") {\n\t\treturn { ...result, ...isValidConfigResult };\n\t}\n\n\tconst configurationFile = isValidConfigResult.data;\n\tconst outputFile = getOutputFile(flags.output);\n\tresult.outputFile = outputFile;\n\n\tconst configuration: {\n\t\treport: ReportConfiguration;\n\t\tsizes?: SizesConfiguration;\n\t} = await import(configurationFile).then((m) => m.default);\n\n\tconst currentStats = readJSONFile(\n\t\tjoin(dirname(configurationFile), configuration.report.current),\n\t);\n\tif (currentStats.exitMessage !== \"\") {\n\t\treturn { ...result, ...currentStats };\n\t}\n\n\ttry {\n\t\tpreviousStats = readJSONFile(\n\t\t\tjoin(dirname(configurationFile), configuration.report.previous),\n\t\t);\n\t\tif (previousStats.exitMessage !== \"\") {\n\t\t\treturn { ...result, ...previousStats };\n\t\t}\n\t\tpreviousVersion = getMostRecentVersion(previousStats.data);\n\t} catch {\n\t\t// no previous stats available.\n\t}\n\n\tconst currentVersion = getMostRecentVersion(currentStats.data);\n\n\tlet limitReached = false;\n\tlet overallDiff = 0;\n\tlet totalGzipSize = 0;\n\n\tconst threshold = configuration.report.threshold ?? DEFAULT_THRESHOLD;\n\tconst headerString = configuration.report.header || \"## Bundle Size\";\n\tconst footerFormatter = configuration.report.footer || formatFooter;\n\tconst columns = configuration.report.columns || [\n\t\t{ status: \"Status\" },\n\t\t{ file: \"File\" },\n\t\t{ size: \"Size (Gzip)\" },\n\t\t{ limits: \"Limits\" },\n\t];\n\n\tconst rowsMD: string[] = [];\n\tconst hasGroupHeaders = Boolean(configuration.sizes?.some(isHeaderEntry));\n\tif (!hasGroupHeaders) {\n\t\trowsMD.push(addMDrow({ type: \"header\", columns }));\n\t}\n\n\t// Build ordered keys based on configuration.sizes if present.\n\tconst orderedKeys: Array<{\n\t\ttype: \"header\" | \"item\";\n\t\tvalue: string;\n\t\theader?: string;\n\t}> = [];\n\tif (configuration.sizes && Array.isArray(configuration.sizes)) {\n\t\tfor (const entry of configuration.sizes) {\n\t\t\tif (isHeaderEntry(entry)) {\n\t\t\t\torderedKeys.push({ type: \"header\", value: \"\", header: entry.header });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// entry is FileSizeEntry here.\n\t\t\tif (entry.alias && currentStats.data[currentVersion][entry.alias]) {\n\t\t\t\torderedKeys.push({ type: \"item\", value: entry.alias });\n\t\t\t} else if (currentStats.data[currentVersion][entry.path]) {\n\t\t\t\torderedKeys.push({ type: \"item\", value: entry.path });\n\t\t\t}\n\t\t}\n\t}\n\n\tif (orderedKeys.length === 0) {\n\t\tfor (const key of Object.keys(currentStats.data[currentVersion])) {\n\t\t\torderedKeys.push({ type: \"item\", value: key });\n\t\t}\n\t}\n\n\tlet subGroupAccumulatedSize = 0;\n\tlet subGroupAccumulatedDiff = 0;\n\tlet subGroupAccumulatedPrevSize = 0;\n\tlet hasSubGroup = false;\n\tconst flushSubGroup = () => {\n\t\tif (!hasSubGroup) {\n\t\t\treturn;\n\t\t}\n\t\t// Use the accumulated thresholded diff directly\n\t\tconst diff = subGroupAccumulatedDiff;\n\t\tlet diffString = \"\";\n\t\tif (diff !== 0 && subGroupAccumulatedPrevSize !== 0) {\n\t\t\tconst sign = diff > 0 ? \"+\" : \"-\";\n\t\t\tdiffString = ` (${sign}${bytes(Math.abs(diff), { unitSeparator: \" \" })} ${percentFormatter(\n\t\t\t\tdiff / subGroupAccumulatedPrevSize,\n\t\t\t)})`;\n\t\t}\n\t\tif (hasGroupHeaders) {\n\t\t\trowsMD.push(\n\t\t\t\t`\\nSub-bundle size: ${bytes(subGroupAccumulatedSize, {\n\t\t\t\t\tunitSeparator: \" \",\n\t\t\t\t})}${diffString}`,\n\t\t\t\t\"\",\n\t\t\t);\n\t\t}\n\t\tsubGroupAccumulatedSize = 0;\n\t\tsubGroupAccumulatedDiff = 0;\n\t\tsubGroupAccumulatedPrevSize = 0;\n\t\thasSubGroup = false;\n\t};\n\n\tfor (const entry of orderedKeys) {\n\t\tif (entry.type === \"header\") {\n\t\t\tflushSubGroup();\n\t\t\trowsMD.push(\"\", entry.header as string, \"\");\n\t\t\trowsMD.push(addMDrow({ type: \"header\", columns }));\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = entry.value;\n\t\tconst item = currentStats.data[currentVersion][key];\n\t\tif (!item) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst name = basename(key);\n\t\tif (!item.passed) {\n\t\t\tlimitReached = true;\n\t\t}\n\n\t\tlet diffString = \"\";\n\t\tlet previousFileSizeGzip = 0;\n\t\tlet thresholdedDiff = 0;\n\t\tif (previousStats && previousVersion) {\n\t\t\tconst previousFileStats =\n\t\t\t\tpreviousStats.data[previousVersion] &&\n\t\t\t\tpreviousStats.data[previousVersion][key];\n\t\t\tpreviousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;\n\t\t\tlet diff = item.fileSizeGzip - previousFileSizeGzip;\n\t\t\t// Apply threshold: if the absolute diff is below threshold, treat as no change\n\t\t\tif (Math.abs(diff) < threshold) {\n\t\t\t\tdiff = 0;\n\t\t\t}\n\t\t\tthresholdedDiff = diff;\n\t\t\toverallDiff += diff;\n\t\t\tdiffString =\n\t\t\t\tdiff === 0 || diff === item.fileSizeGzip\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: ` (${diff > 0 ? \"+\" : \"-\"}${bytes(Math.abs(diff), { unitSeparator: \" \" })} ${percentFormatter(\n\t\t\t\t\t\t\tpreviousFileSizeGzip === 0 ? 0 : diff / previousFileSizeGzip,\n\t\t\t\t\t\t)})`;\n\t\t}\n\n\t\ttotalGzipSize += item.fileSizeGzip;\n\t\tsubGroupAccumulatedSize += item.fileSizeGzip;\n\t\tsubGroupAccumulatedDiff += thresholdedDiff;\n\t\tsubGroupAccumulatedPrevSize += previousFileSizeGzip;\n\t\thasSubGroup = true;\n\n\t\trowsMD.push(\n\t\t\taddMDrow({\n\t\t\t\ttype: \"row\",\n\t\t\t\tpassed: item.passed,\n\t\t\t\tname: name,\n\t\t\t\tsize: item.fileSizeGzip,\n\t\t\t\tdiff: diffString,\n\t\t\t\tlimit: item.limit,\n\t\t\t\tcolumns,\n\t\t\t}),\n\t\t);\n\t}\n\n\tflushSubGroup();\n\n\tconst template = `\n${headerString}\n${rowsMD.join(\"\\n\")}\n\n${footerFormatter({ limitReached, overallDiff, totalGzipSize })}\n`;\n\n\tif (limitReached) {\n\t\tresult.exitCode = 1;\n\t}\n\tresult.data = template;\n\treturn result;\n};\n"],"names":["basename","dirname","join","bytes","DEFAULT_THRESHOLD","addMDrow","formatFooter","getMostRecentVersion","getOutputFile","percentFormatter","readJSONFile","validateConfigurationFile","isHeaderEntry","entry","reportStats","flags","result","exitCode","exitMessage","outputFile","data","previousStats","previousVersion","isValidConfigResult","configuration","configurationFile","output","then","m","default","currentStats","report","current","previous","currentVersion","limitReached","overallDiff","totalGzipSize","threshold","headerString","header","footerFormatter","footer","columns","status","file","size","limits","rowsMD","hasGroupHeaders","Boolean","sizes","some","push","type","orderedKeys","Array","isArray","value","alias","path","length","key","Object","keys","subGroupAccumulatedSize","subGroupAccumulatedDiff","subGroupAccumulatedPrevSize","hasSubGroup","flushSubGroup","diff","diffString","sign","Math","abs","unitSeparator","item","name","passed","previousFileSizeGzip","thresholdedDiff","previousFileStats","fileSizeGzip","limit","template"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-cli/bundlesize",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"description": "Simple CLI tool that checks file(s) size and report if limits have been reached",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"bin": "dist/bundlesize.js",
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
+
"schemas",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
15
16
|
"node": ">=18",
|
|
@@ -45,5 +46,5 @@
|
|
|
45
46
|
"@vitest/coverage-v8": "3.2.4",
|
|
46
47
|
"vitest": "3.2.4"
|
|
47
48
|
},
|
|
48
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "b2f3d976bb47479d29242c68e36dcc85df41f7b9"
|
|
49
50
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://cdn.jsdelivr.net/npm/@node-cli/bundlesize/schemas/bundlesize.config.schema.json",
|
|
4
|
+
"title": "Bundlesize Configuration",
|
|
5
|
+
"description": "Configuration schema for @node-cli/bundlesize - a CLI tool that checks file(s) size and reports if limits have been reached",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Path to the JSON Schema for this configuration file"
|
|
11
|
+
},
|
|
12
|
+
"sizes": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"description": "Array of file size entries to check",
|
|
15
|
+
"items": {
|
|
16
|
+
"oneOf": [
|
|
17
|
+
{
|
|
18
|
+
"$ref": "#/$defs/SizeEntry"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"$ref": "#/$defs/HeaderEntry"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"report": {
|
|
27
|
+
"$ref": "#/$defs/ReportConfiguration"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"$defs": {
|
|
32
|
+
"SizeEntry": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"description": "A file size entry to check",
|
|
35
|
+
"properties": {
|
|
36
|
+
"path": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Path to the file to check. Supports glob patterns and special placeholders: <hash> for content hashes, <semver> for semantic versions"
|
|
39
|
+
},
|
|
40
|
+
"limit": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Maximum allowed size for the file (gzipped). Examples: '1.5 kB', '500 B', '2 MB'",
|
|
43
|
+
"pattern": "^[0-9]+(\\.[0-9]+)?\\s*(B|kB|KB|MB|GB|TB|PB)$"
|
|
44
|
+
},
|
|
45
|
+
"alias": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Optional alias for the file in reports. Useful when using dynamic path patterns"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"required": ["path", "limit"],
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
},
|
|
53
|
+
"HeaderEntry": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"description": "A header entry for grouping files in reports",
|
|
56
|
+
"properties": {
|
|
57
|
+
"header": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Header text for a group of files in the report (e.g., '### Main Bundle')"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": ["header"],
|
|
63
|
+
"additionalProperties": false
|
|
64
|
+
},
|
|
65
|
+
"ColumnDefinition": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"description": "A column definition for the report table",
|
|
68
|
+
"minProperties": 1,
|
|
69
|
+
"maxProperties": 1,
|
|
70
|
+
"additionalProperties": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Column header text"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"ReportConfiguration": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"description": "Configuration for generating size comparison reports",
|
|
78
|
+
"properties": {
|
|
79
|
+
"current": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"description": "Path to the current stats JSON file (relative to config file)"
|
|
82
|
+
},
|
|
83
|
+
"previous": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "Path to the previous stats JSON file for comparison (relative to config file)"
|
|
86
|
+
},
|
|
87
|
+
"header": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"description": "Custom header for the report. Defaults to '## Bundle Size'"
|
|
90
|
+
},
|
|
91
|
+
"threshold": {
|
|
92
|
+
"type": "number",
|
|
93
|
+
"description": "Minimum byte difference to report as a change. Differences below this threshold are treated as no change. Defaults to 0",
|
|
94
|
+
"minimum": 0
|
|
95
|
+
},
|
|
96
|
+
"columns": {
|
|
97
|
+
"type": "array",
|
|
98
|
+
"description": "Custom column definitions for the report table. Default columns: status, file, size, limits",
|
|
99
|
+
"items": {
|
|
100
|
+
"$ref": "#/$defs/ColumnDefinition"
|
|
101
|
+
},
|
|
102
|
+
"examples": [
|
|
103
|
+
[
|
|
104
|
+
{ "status": "Status" },
|
|
105
|
+
{ "file": "File" },
|
|
106
|
+
{ "size": "Size (Gzip)" },
|
|
107
|
+
{ "limits": "Limits" }
|
|
108
|
+
]
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"required": ["current", "previous"],
|
|
113
|
+
"additionalProperties": true
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"examples": [
|
|
117
|
+
{
|
|
118
|
+
"sizes": [
|
|
119
|
+
{
|
|
120
|
+
"path": "dist/index.js",
|
|
121
|
+
"limit": "10 kB"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"path": "dist/vendor.<hash>.js",
|
|
125
|
+
"limit": "50 kB",
|
|
126
|
+
"alias": "vendor"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"sizes": [
|
|
132
|
+
{ "header": "### Core" },
|
|
133
|
+
{
|
|
134
|
+
"path": "dist/core.js",
|
|
135
|
+
"limit": "5 kB"
|
|
136
|
+
},
|
|
137
|
+
{ "header": "### Plugins" },
|
|
138
|
+
{
|
|
139
|
+
"path": "dist/plugins/*.js",
|
|
140
|
+
"limit": "2 kB"
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"report": {
|
|
144
|
+
"current": "stats/current.json",
|
|
145
|
+
"previous": "stats/previous.json",
|
|
146
|
+
"header": "## Bundle Size Report",
|
|
147
|
+
"threshold": 100
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
}
|