@node-cli/bundlesize 4.3.0 → 4.4.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 +15 -0
- package/dist/defaults.d.ts +1 -0
- package/dist/defaults.js +1 -0
- package/dist/defaults.js.map +1 -1
- package/dist/reportStats.js +12 -2
- package/dist/reportStats.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ For the report option, it must export an object named "report" which is an objec
|
|
|
26
26
|
- `previous`: the previous path to the report to compare against (required, string)
|
|
27
27
|
- `current`: the current path to the report to compare against (required, string)
|
|
28
28
|
- `columns`: the columns to display (optional, array of objects)
|
|
29
|
+
- `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.
|
|
29
30
|
|
|
30
31
|
## Examples
|
|
31
32
|
|
|
@@ -184,6 +185,20 @@ export default {
|
|
|
184
185
|
};
|
|
185
186
|
```
|
|
186
187
|
|
|
188
|
+
#### Simple report with custom threshold
|
|
189
|
+
|
|
190
|
+
By default, all gzip size changes are reported. You can set a threshold to ignore small changes:
|
|
191
|
+
|
|
192
|
+
```js
|
|
193
|
+
export default {
|
|
194
|
+
report: {
|
|
195
|
+
threshold: 10, // ignore changes smaller than 10 bytes
|
|
196
|
+
prev: "stats/previous.json",
|
|
197
|
+
current: "stats/current.json"
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
```
|
|
201
|
+
|
|
187
202
|
#### Report with subgroup headers (multiple tables)
|
|
188
203
|
|
|
189
204
|
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/defaults.d.ts
CHANGED
package/dist/defaults.js
CHANGED
package/dist/defaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["/* istanbul ignore file */\n\nexport const defaultFlags = {\n\tboring: false,\n\tconfiguration: \"\",\n\tsilent: false,\n\ttype: \"size\",\n\tforce: false,\n};\n"],"names":["defaultFlags","boring","configuration","silent","type","force"],"mappings":"AAAA,wBAAwB,GAExB,OAAO,MAAMA,eAAe;IAC3BC,QAAQ;IACRC,eAAe;IACfC,QAAQ;IACRC,MAAM;IACNC,OAAO;AACR,EAAE"}
|
|
1
|
+
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["/* istanbul ignore file */\n\nexport const defaultFlags = {\n\tboring: false,\n\tconfiguration: \"\",\n\tsilent: false,\n\ttype: \"size\",\n\tforce: false,\n};\n\nexport const DEFAULT_THRESHOLD = 0;\n"],"names":["defaultFlags","boring","configuration","silent","type","force","DEFAULT_THRESHOLD"],"mappings":"AAAA,wBAAwB,GAExB,OAAO,MAAMA,eAAe;IAC3BC,QAAQ;IACRC,eAAe;IACfC,QAAQ;IACRC,MAAM;IACNC,OAAO;AACR,EAAE;AAEF,OAAO,MAAMC,oBAAoB,EAAE"}
|
package/dist/reportStats.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { basename, dirname, join } from "node:path";
|
|
2
2
|
import bytes from "bytes";
|
|
3
|
+
import { DEFAULT_THRESHOLD } from "./defaults.js";
|
|
3
4
|
import { addMDrow, formatFooter, getMostRecentVersion, getOutputFile, percentFormatter, readJSONFile, validateConfigurationFile } from "./utilities.js";
|
|
4
5
|
const isHeaderEntry = (entry)=>"header" in entry;
|
|
5
6
|
export const reportStats = async ({ flags })=>{
|
|
@@ -44,6 +45,7 @@ export const reportStats = async ({ flags })=>{
|
|
|
44
45
|
let limitReached = false;
|
|
45
46
|
let overallDiff = 0;
|
|
46
47
|
let totalGzipSize = 0;
|
|
48
|
+
const threshold = configuration.report.threshold ?? DEFAULT_THRESHOLD;
|
|
47
49
|
const headerString = configuration.report.header || "## Bundle Size";
|
|
48
50
|
const footerFormatter = configuration.report.footer || formatFooter;
|
|
49
51
|
const columns = configuration.report.columns || [
|
|
@@ -109,7 +111,11 @@ export const reportStats = async ({ flags })=>{
|
|
|
109
111
|
if (!hasSubGroup) {
|
|
110
112
|
return;
|
|
111
113
|
}
|
|
112
|
-
|
|
114
|
+
let diff = subGroupAccumulatedSize - subGroupAccumulatedPrevSize;
|
|
115
|
+
// Apply threshold: if the absolute diff is below threshold, treat as no change
|
|
116
|
+
if (Math.abs(diff) < threshold) {
|
|
117
|
+
diff = 0;
|
|
118
|
+
}
|
|
113
119
|
let diffString = "";
|
|
114
120
|
if (diff !== 0 && subGroupAccumulatedPrevSize !== 0) {
|
|
115
121
|
const sign = diff > 0 ? "+" : "-";
|
|
@@ -150,7 +156,11 @@ export const reportStats = async ({ flags })=>{
|
|
|
150
156
|
if (previousStats && previousVersion) {
|
|
151
157
|
const previousFileStats = previousStats.data[previousVersion] && previousStats.data[previousVersion][key];
|
|
152
158
|
previousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;
|
|
153
|
-
|
|
159
|
+
let diff = item.fileSizeGzip - previousFileSizeGzip;
|
|
160
|
+
// Apply threshold: if the absolute diff is below threshold, treat as no change
|
|
161
|
+
if (Math.abs(diff) < threshold) {
|
|
162
|
+
diff = 0;
|
|
163
|
+
}
|
|
154
164
|
overallDiff += diff;
|
|
155
165
|
diffString = diff === 0 || diff === item.fileSizeGzip ? "" : ` (${diff > 0 ? "+" : "-"}${bytes(Math.abs(diff), {
|
|
156
166
|
unitSeparator: " "
|
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 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};\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 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\tconst diff = subGroupAccumulatedSize - subGroupAccumulatedPrevSize;\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\tconst diff = item.fileSizeGzip - previousFileSizeGzip;\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","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","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","diffString","sign","Math","abs","unitSeparator","item","name","passed","previousFileSizeGzip","previousFileStats","fileSizeGzip","limit","template"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,OAAO,EAAEC,IAAI,QAAQ,YAAY;AACpD,OAAOC,WAAW,QAAQ;AAE1B,SACCC,QAAQ,EACRC,YAAY,EACZC,oBAAoB,EACpBC,aAAa,EACbC,gBAAgB,EAChBC,YAAY,EACZC,yBAAyB,QACnB,iBAAiB;AAoBxB,MAAMC,gBAAgB,CACrBC,QAC8B,YAAYA;AAS3C,OAAO,MAAMC,cAAc,OAAO,EAAEC,KAAK,EAAE;IAC1C,MAAMC,SAAwB;QAC7BC,UAAU;QACVC,aAAa;QACbC,YAAY;QACZC,MAAM;IACP;IAEA,IAAIC,eAAeC;IACnB,MAAMC,sBAAsBZ,0BAA0BI,MAAMS,aAAa;IACzE,IAAID,oBAAoBL,WAAW,KAAK,IAAI;QAC3C,OAAO;YAAE,GAAGF,MAAM;YAAE,GAAGO,mBAAmB;QAAC;IAC5C;IAEA,MAAME,oBAAoBF,oBAAoBH,IAAI;IAClD,MAAMD,aAAaX,cAAcO,MAAMW,MAAM;IAC7CV,OAAOG,UAAU,GAAGA;IAEpB,MAAMK,gBAGF,MAAM,MAAM,CAACC,mBAAmBE,IAAI,CAAC,CAACC,IAAMA,EAAEC,OAAO;IAEzD,MAAMC,eAAepB,aACpBP,KAAKD,QAAQuB,oBAAoBD,cAAcO,MAAM,CAACC,OAAO;IAE9D,IAAIF,aAAaZ,WAAW,KAAK,IAAI;QACpC,OAAO;YAAE,GAAGF,MAAM;YAAE,GAAGc,YAAY;QAAC;IACrC;IAEA,IAAI;QACHT,gBAAgBX,aACfP,KAAKD,QAAQuB,oBAAoBD,cAAcO,MAAM,CAACE,QAAQ;QAE/D,IAAIZ,cAAcH,WAAW,KAAK,IAAI;YACrC,OAAO;gBAAE,GAAGF,MAAM;gBAAE,GAAGK,aAAa;YAAC;QACtC;QACAC,kBAAkBf,qBAAqBc,cAAcD,IAAI;IAC1D,EAAE,OAAM;IACP,+BAA+B;IAChC;IAEA,MAAMc,iBAAiB3B,qBAAqBuB,aAAaV,IAAI;IAE7D,IAAIe,eAAe;IACnB,IAAIC,cAAc;IAClB,IAAIC,gBAAgB;IAEpB,MAAMC,eAAed,cAAcO,MAAM,CAACQ,MAAM,IAAI;IACpD,MAAMC,kBAAkBhB,cAAcO,MAAM,CAACU,MAAM,IAAInC;IACvD,MAAMoC,UAAUlB,cAAcO,MAAM,CAACW,OAAO,IAAI;QAC/C;YAAEC,QAAQ;QAAS;QACnB;YAAEC,MAAM;QAAO;QACf;YAAEC,MAAM;QAAc;QACtB;YAAEC,QAAQ;QAAS;KACnB;IAED,MAAMC,SAAmB,EAAE;IAC3B,MAAMC,kBAAkBC,QAAQzB,cAAc0B,KAAK,EAAEC,KAAKvC;IAC1D,IAAI,CAACoC,iBAAiB;QACrBD,OAAOK,IAAI,CAAC/C,SAAS;YAAEgD,MAAM;YAAUX;QAAQ;IAChD;IAEA,8DAA8D;IAC9D,MAAMY,cAID,EAAE;IACP,IAAI9B,cAAc0B,KAAK,IAAIK,MAAMC,OAAO,CAAChC,cAAc0B,KAAK,GAAG;QAC9D,KAAK,MAAMrC,SAASW,cAAc0B,KAAK,CAAE;YACxC,IAAItC,cAAcC,QAAQ;gBACzByC,YAAYF,IAAI,CAAC;oBAAEC,MAAM;oBAAUI,OAAO;oBAAIlB,QAAQ1B,MAAM0B,MAAM;gBAAC;gBACnE;YACD;YACA,+BAA+B;YAC/B,IAAI1B,MAAM6C,KAAK,IAAI5B,aAAaV,IAAI,CAACc,eAAe,CAACrB,MAAM6C,KAAK,CAAC,EAAE;gBAClEJ,YAAYF,IAAI,CAAC;oBAAEC,MAAM;oBAAQI,OAAO5C,MAAM6C,KAAK;gBAAC;YACrD,OAAO,IAAI5B,aAAaV,IAAI,CAACc,eAAe,CAACrB,MAAM8C,IAAI,CAAC,EAAE;gBACzDL,YAAYF,IAAI,CAAC;oBAAEC,MAAM;oBAAQI,OAAO5C,MAAM8C,IAAI;gBAAC;YACpD;QACD;IACD;IAEA,IAAIL,YAAYM,MAAM,KAAK,GAAG;QAC7B,KAAK,MAAMC,OAAOC,OAAOC,IAAI,CAACjC,aAAaV,IAAI,CAACc,eAAe,EAAG;YACjEoB,YAAYF,IAAI,CAAC;gBAAEC,MAAM;gBAAQI,OAAOI;YAAI;QAC7C;IACD;IAEA,IAAIG,0BAA0B;IAC9B,IAAIC,8BAA8B;IAClC,IAAIC,cAAc;IAClB,MAAMC,gBAAgB;QACrB,IAAI,CAACD,aAAa;YACjB;QACD;QACA,MAAME,OAAOJ,0BAA0BC;QACvC,IAAII,aAAa;QACjB,IAAID,SAAS,KAAKH,gCAAgC,GAAG;YACpD,MAAMK,OAAOF,OAAO,IAAI,MAAM;YAC9BC,aAAa,CAAC,EAAE,EAAEC,OAAOlE,MAAMmE,KAAKC,GAAG,CAACJ,OAAO;gBAAEK,eAAe;YAAI,GAAG,CAAC,EAAEhE,iBACzE2D,OAAOH,6BACN,CAAC,CAAC;QACL;QACA,IAAIjB,iBAAiB;YACpBD,OAAOK,IAAI,CACV,CAAC,mBAAmB,EAAEhD,MAAM4D,yBAAyB;gBACpDS,eAAe;YAChB,KAAKJ,YAAY,EACjB;QAEF;QACAL,0BAA0B;QAC1BC,8BAA8B;QAC9BC,cAAc;IACf;IAEA,KAAK,MAAMrD,SAASyC,YAAa;QAChC,IAAIzC,MAAMwC,IAAI,KAAK,UAAU;YAC5Bc;YACApB,OAAOK,IAAI,CAAC,IAAIvC,MAAM0B,MAAM,EAAY;YACxCQ,OAAOK,IAAI,CAAC/C,SAAS;gBAAEgD,MAAM;gBAAUX;YAAQ;YAC/C;QACD;QACA,MAAMmB,MAAMhD,MAAM4C,KAAK;QACvB,MAAMiB,OAAO5C,aAAaV,IAAI,CAACc,eAAe,CAAC2B,IAAI;QACnD,IAAI,CAACa,MAAM;YACV;QACD;QACA,MAAMC,OAAO1E,SAAS4D;QACtB,IAAI,CAACa,KAAKE,MAAM,EAAE;YACjBzC,eAAe;QAChB;QAEA,IAAIkC,aAAa;QACjB,IAAIQ,uBAAuB;QAC3B,IAAIxD,iBAAiBC,iBAAiB;YACrC,MAAMwD,oBACLzD,cAAcD,IAAI,CAACE,gBAAgB,IACnCD,cAAcD,IAAI,CAACE,gBAAgB,CAACuC,IAAI;YACzCgB,uBAAuBC,mBAAmBC,gBAAgB;YAC1D,MAAMX,OAAOM,KAAKK,YAAY,GAAGF;YACjCzC,eAAegC;YACfC,aACCD,SAAS,KAAKA,SAASM,KAAKK,YAAY,GACrC,KACA,CAAC,EAAE,EAAEX,OAAO,IAAI,MAAM,MAAMhE,MAAMmE,KAAKC,GAAG,CAACJ,OAAO;gBAAEK,eAAe;YAAI,GAAG,CAAC,EAAEhE,iBAC7EoE,yBAAyB,IAAI,IAAIT,OAAOS,sBACvC,CAAC,CAAC;QACR;QAEAxC,iBAAiBqC,KAAKK,YAAY;QAClCf,2BAA2BU,KAAKK,YAAY;QAC5Cd,+BAA+BY;QAC/BX,cAAc;QAEdnB,OAAOK,IAAI,CACV/C,SAAS;YACRgD,MAAM;YACNuB,QAAQF,KAAKE,MAAM;YACnBD,MAAMA;YACN9B,MAAM6B,KAAKK,YAAY;YACvBX,MAAMC;YACNW,OAAON,KAAKM,KAAK;YACjBtC;QACD;IAEF;IAEAyB;IAEA,MAAMc,WAAW,CAAC;AACnB,EAAE3C,aAAa;AACf,EAAES,OAAO5C,IAAI,CAAC,MAAM;;AAEpB,EAAEqC,gBAAgB;QAAEL;QAAcC;QAAaC;IAAc,GAAG;AAChE,CAAC;IAEA,IAAIF,cAAc;QACjBnB,OAAOC,QAAQ,GAAG;IACnB;IACAD,OAAOI,IAAI,GAAG6D;IACd,OAAOjE;AACR,EAAE"}
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-cli/bundlesize",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.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",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"@vitest/coverage-v8": "3.2.4",
|
|
46
46
|
"vitest": "3.2.4"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "ffc82a19b07c42aa655d4a15e8e1459e1530ecdc"
|
|
49
49
|
}
|