@node-cli/bundlesize 4.2.4 → 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 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,78 @@ 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
+
202
+ #### Report with subgroup headers (multiple tables)
203
+
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.
205
+
206
+ ```js
207
+ export default {
208
+ report: {
209
+ header: "## Bundle Size With Groups",
210
+ previous: "stats/previous.json",
211
+ current: "stats/current.json"
212
+ },
213
+ sizes: [
214
+ { header: "### Core" },
215
+ { path: "dist/core.js", limit: "20 kB" },
216
+ { path: "dist/core-extra.js", limit: "10 kB" },
217
+ { header: "### Widgets" },
218
+ { path: "dist/widget-a.js", limit: "15 kB" },
219
+ { path: "dist/widget-b.js", limit: "15 kB" }
220
+ ]
221
+ };
222
+ ```
223
+
224
+ Example output:
225
+
226
+ ```
227
+ ## Bundle Size With Groups
228
+
229
+ ### Core
230
+
231
+ | Status | File | Size (Gzip) | Limits |
232
+ | --- | --- | --- | --- |
233
+ | ✅ | core.js | 12.34 KB (-1.2 KB -8.80%) | 20 kB |
234
+ | ✅ | core-extra.js | 3.21 KB | 10 kB |
235
+
236
+ Sub-bundle size: 15.55 KB (-1.2 KB -7.17%)
237
+
238
+
239
+ ### Widgets
240
+
241
+ | Status | File | Size (Gzip) | Limits |
242
+ | --- | --- | --- | --- |
243
+ | ✅ | widget-a.js | 5.00 KB (+500 B +10.84%) | 15 kB |
244
+ | ✅ | widget-b.js | 4.50 KB | 15 kB |
245
+
246
+ Sub-bundle size: 9.50 KB (+500 B +5.56%)
247
+
248
+
249
+ Overall bundle size: 25.05 KB (-700 B -2.72%)
250
+ Overall status: ✅
251
+ ```
252
+
253
+ Notes:
254
+
255
+ - If no header objects are present, the legacy single-table format is used (backwards compatible).
256
+ - Headers are rendered in the order they appear in `sizes`.
257
+ - Only size entries that resolve in the current stats are printed; missing ones are skipped silently.
258
+ - Sub-bundle diff lines only show percentage / size diff when previous stats for at least one file in the subgroup exist.
259
+
187
260
  ## Usage
188
261
 
189
262
  ### Print the stats at the command line
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- /* v8 ignore start */ import { Logger } from "@node-cli/logger";
2
+ /* istanbul ignore file */ import { Logger } from "@node-cli/logger";
3
3
  import fs from "fs-extra";
4
4
  import { getRawStats } from "./getRawStats.js";
5
5
  import { config } from "./parse.js";
@@ -70,6 +70,6 @@ if (flags.type === "report") {
70
70
  }
71
71
  }
72
72
  log.error("Invalid type, please use 'size' or 'report'");
73
- process.exit(1); /* v8 ignore stop */
73
+ process.exit(1);
74
74
 
75
75
  //# sourceMappingURL=bundlesize.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bundlesize.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/* v8 ignore start */\n\nimport { Logger } from \"@node-cli/logger\";\nimport fs from \"fs-extra\";\nimport { getRawStats } from \"./getRawStats.js\";\nimport { config } from \"./parse.js\";\nimport { reportStats } from \"./reportStats.js\";\nimport { IGNORE, STDOUT } from \"./utilities.js\";\n\nconst flags = config.flags;\n\nconst log = new Logger({\n\tboring: flags.boring,\n});\n\nif (flags.type === \"size\") {\n\ttry {\n\t\tconst result = await getRawStats({ flags });\n\n\t\tif (result.outputFile === IGNORE) {\n\t\t\tprocess.exit(result.exitCode);\n\t\t}\n\n\t\tif (result.exitMessage !== \"\") {\n\t\t\tlog.error(result.exitMessage);\n\t\t\tprocess.exit(result.exitCode);\n\t\t}\n\n\t\tif (result.outputFile === STDOUT) {\n\t\t\tlog.info(`Configuration: ${flags.configuration}`);\n\t\t\tlog.info(`Output: ${result.outputFile}`);\n\t\t\tlog.info(`Output prefix: ${result.prefix}`);\n\t\t\tlog.log(result.data);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tfs.outputJsonSync(result.outputFile, result.data, { spaces: 2 });\n\t\t\t} catch (error) {\n\t\t\t\tlog.error(`Failed to write to file: ${error.message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t\tprocess.exit(result.exitCode);\n\t} catch (error) {\n\t\tlog.error(error);\n\t\tprocess.exit(1);\n\t}\n}\n\nif (flags.type === \"report\") {\n\ttry {\n\t\tconst result = await reportStats({ flags });\n\n\t\tif (result.exitMessage !== \"\") {\n\t\t\tlog.error(result.exitMessage);\n\t\t\tprocess.exit(result.exitCode);\n\t\t}\n\n\t\tif (result.outputFile === STDOUT) {\n\t\t\tlog.info(`Configuration: ${flags.configuration}`);\n\t\t\tlog.info(`Output: ${result.outputFile}`);\n\t\t\tlog.log(result.data);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tfs.outputFileSync(result.outputFile, result.data);\n\t\t\t} catch (error) {\n\t\t\t\tlog.error(`Failed to write to file: ${error.message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t\tprocess.exit(result.exitCode);\n\t} catch (error) {\n\t\tlog.error(error);\n\t\tprocess.exit(1);\n\t}\n}\n\nlog.error(\"Invalid type, please use 'size' or 'report'\");\nprocess.exit(1);\n/* v8 ignore stop */\n"],"names":["Logger","fs","getRawStats","config","reportStats","IGNORE","STDOUT","flags","log","boring","type","result","outputFile","process","exit","exitCode","exitMessage","error","info","configuration","prefix","data","outputJsonSync","spaces","message","outputFileSync"],"mappings":";AAEA,mBAAmB,GAEnB,SAASA,MAAM,QAAQ,mBAAmB;AAC1C,OAAOC,QAAQ,WAAW;AAC1B,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,MAAM,QAAQ,aAAa;AACpC,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,MAAM,EAAEC,MAAM,QAAQ,iBAAiB;AAEhD,MAAMC,QAAQJ,OAAOI,KAAK;AAE1B,MAAMC,MAAM,IAAIR,OAAO;IACtBS,QAAQF,MAAME,MAAM;AACrB;AAEA,IAAIF,MAAMG,IAAI,KAAK,QAAQ;IAC1B,IAAI;QACH,MAAMC,SAAS,MAAMT,YAAY;YAAEK;QAAM;QAEzC,IAAII,OAAOC,UAAU,KAAKP,QAAQ;YACjCQ,QAAQC,IAAI,CAACH,OAAOI,QAAQ;QAC7B;QAEA,IAAIJ,OAAOK,WAAW,KAAK,IAAI;YAC9BR,IAAIS,KAAK,CAACN,OAAOK,WAAW;YAC5BH,QAAQC,IAAI,CAACH,OAAOI,QAAQ;QAC7B;QAEA,IAAIJ,OAAOC,UAAU,KAAKN,QAAQ;YACjCE,IAAIU,IAAI,CAAC,CAAC,eAAe,EAAEX,MAAMY,aAAa,EAAE;YAChDX,IAAIU,IAAI,CAAC,CAAC,QAAQ,EAAEP,OAAOC,UAAU,EAAE;YACvCJ,IAAIU,IAAI,CAAC,CAAC,eAAe,EAAEP,OAAOS,MAAM,EAAE;YAC1CZ,IAAIA,GAAG,CAACG,OAAOU,IAAI;QACpB,OAAO;YACN,IAAI;gBACHpB,GAAGqB,cAAc,CAACX,OAAOC,UAAU,EAAED,OAAOU,IAAI,EAAE;oBAAEE,QAAQ;gBAAE;YAC/D,EAAE,OAAON,OAAO;gBACfT,IAAIS,KAAK,CAAC,CAAC,yBAAyB,EAAEA,MAAMO,OAAO,EAAE;gBACrDX,QAAQC,IAAI,CAAC;YACd;QACD;QACAD,QAAQC,IAAI,CAACH,OAAOI,QAAQ;IAC7B,EAAE,OAAOE,OAAO;QACfT,IAAIS,KAAK,CAACA;QACVJ,QAAQC,IAAI,CAAC;IACd;AACD;AAEA,IAAIP,MAAMG,IAAI,KAAK,UAAU;IAC5B,IAAI;QACH,MAAMC,SAAS,MAAMP,YAAY;YAAEG;QAAM;QAEzC,IAAII,OAAOK,WAAW,KAAK,IAAI;YAC9BR,IAAIS,KAAK,CAACN,OAAOK,WAAW;YAC5BH,QAAQC,IAAI,CAACH,OAAOI,QAAQ;QAC7B;QAEA,IAAIJ,OAAOC,UAAU,KAAKN,QAAQ;YACjCE,IAAIU,IAAI,CAAC,CAAC,eAAe,EAAEX,MAAMY,aAAa,EAAE;YAChDX,IAAIU,IAAI,CAAC,CAAC,QAAQ,EAAEP,OAAOC,UAAU,EAAE;YACvCJ,IAAIA,GAAG,CAACG,OAAOU,IAAI;QACpB,OAAO;YACN,IAAI;gBACHpB,GAAGwB,cAAc,CAACd,OAAOC,UAAU,EAAED,OAAOU,IAAI;YACjD,EAAE,OAAOJ,OAAO;gBACfT,IAAIS,KAAK,CAAC,CAAC,yBAAyB,EAAEA,MAAMO,OAAO,EAAE;gBACrDX,QAAQC,IAAI,CAAC;YACd;QACD;QACAD,QAAQC,IAAI,CAACH,OAAOI,QAAQ;IAC7B,EAAE,OAAOE,OAAO;QACfT,IAAIS,KAAK,CAACA;QACVJ,QAAQC,IAAI,CAAC;IACd;AACD;AAEAN,IAAIS,KAAK,CAAC;AACVJ,QAAQC,IAAI,CAAC,IACb,kBAAkB"}
1
+ {"version":3,"sources":["../src/bundlesize.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/* istanbul ignore file */\n\nimport { Logger } from \"@node-cli/logger\";\nimport fs from \"fs-extra\";\nimport { getRawStats } from \"./getRawStats.js\";\nimport { config } from \"./parse.js\";\nimport { reportStats } from \"./reportStats.js\";\nimport { IGNORE, STDOUT } from \"./utilities.js\";\n\nconst flags = config.flags;\n\nconst log = new Logger({\n\tboring: flags.boring,\n});\n\nif (flags.type === \"size\") {\n\ttry {\n\t\tconst result = await getRawStats({ flags });\n\n\t\tif (result.outputFile === IGNORE) {\n\t\t\tprocess.exit(result.exitCode);\n\t\t}\n\n\t\tif (result.exitMessage !== \"\") {\n\t\t\tlog.error(result.exitMessage);\n\t\t\tprocess.exit(result.exitCode);\n\t\t}\n\n\t\tif (result.outputFile === STDOUT) {\n\t\t\tlog.info(`Configuration: ${flags.configuration}`);\n\t\t\tlog.info(`Output: ${result.outputFile}`);\n\t\t\tlog.info(`Output prefix: ${result.prefix}`);\n\t\t\tlog.log(result.data);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tfs.outputJsonSync(result.outputFile, result.data, { spaces: 2 });\n\t\t\t} catch (error) {\n\t\t\t\tlog.error(`Failed to write to file: ${error.message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t\tprocess.exit(result.exitCode);\n\t} catch (error) {\n\t\tlog.error(error);\n\t\tprocess.exit(1);\n\t}\n}\n\nif (flags.type === \"report\") {\n\ttry {\n\t\tconst result = await reportStats({ flags });\n\n\t\tif (result.exitMessage !== \"\") {\n\t\t\tlog.error(result.exitMessage);\n\t\t\tprocess.exit(result.exitCode);\n\t\t}\n\n\t\tif (result.outputFile === STDOUT) {\n\t\t\tlog.info(`Configuration: ${flags.configuration}`);\n\t\t\tlog.info(`Output: ${result.outputFile}`);\n\t\t\tlog.log(result.data);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tfs.outputFileSync(result.outputFile, result.data);\n\t\t\t} catch (error) {\n\t\t\t\tlog.error(`Failed to write to file: ${error.message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t\tprocess.exit(result.exitCode);\n\t} catch (error) {\n\t\tlog.error(error);\n\t\tprocess.exit(1);\n\t}\n}\n\nlog.error(\"Invalid type, please use 'size' or 'report'\");\nprocess.exit(1);\n"],"names":["Logger","fs","getRawStats","config","reportStats","IGNORE","STDOUT","flags","log","boring","type","result","outputFile","process","exit","exitCode","exitMessage","error","info","configuration","prefix","data","outputJsonSync","spaces","message","outputFileSync"],"mappings":";AAEA,wBAAwB,GAExB,SAASA,MAAM,QAAQ,mBAAmB;AAC1C,OAAOC,QAAQ,WAAW;AAC1B,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,MAAM,QAAQ,aAAa;AACpC,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,MAAM,EAAEC,MAAM,QAAQ,iBAAiB;AAEhD,MAAMC,QAAQJ,OAAOI,KAAK;AAE1B,MAAMC,MAAM,IAAIR,OAAO;IACtBS,QAAQF,MAAME,MAAM;AACrB;AAEA,IAAIF,MAAMG,IAAI,KAAK,QAAQ;IAC1B,IAAI;QACH,MAAMC,SAAS,MAAMT,YAAY;YAAEK;QAAM;QAEzC,IAAII,OAAOC,UAAU,KAAKP,QAAQ;YACjCQ,QAAQC,IAAI,CAACH,OAAOI,QAAQ;QAC7B;QAEA,IAAIJ,OAAOK,WAAW,KAAK,IAAI;YAC9BR,IAAIS,KAAK,CAACN,OAAOK,WAAW;YAC5BH,QAAQC,IAAI,CAACH,OAAOI,QAAQ;QAC7B;QAEA,IAAIJ,OAAOC,UAAU,KAAKN,QAAQ;YACjCE,IAAIU,IAAI,CAAC,CAAC,eAAe,EAAEX,MAAMY,aAAa,EAAE;YAChDX,IAAIU,IAAI,CAAC,CAAC,QAAQ,EAAEP,OAAOC,UAAU,EAAE;YACvCJ,IAAIU,IAAI,CAAC,CAAC,eAAe,EAAEP,OAAOS,MAAM,EAAE;YAC1CZ,IAAIA,GAAG,CAACG,OAAOU,IAAI;QACpB,OAAO;YACN,IAAI;gBACHpB,GAAGqB,cAAc,CAACX,OAAOC,UAAU,EAAED,OAAOU,IAAI,EAAE;oBAAEE,QAAQ;gBAAE;YAC/D,EAAE,OAAON,OAAO;gBACfT,IAAIS,KAAK,CAAC,CAAC,yBAAyB,EAAEA,MAAMO,OAAO,EAAE;gBACrDX,QAAQC,IAAI,CAAC;YACd;QACD;QACAD,QAAQC,IAAI,CAACH,OAAOI,QAAQ;IAC7B,EAAE,OAAOE,OAAO;QACfT,IAAIS,KAAK,CAACA;QACVJ,QAAQC,IAAI,CAAC;IACd;AACD;AAEA,IAAIP,MAAMG,IAAI,KAAK,UAAU;IAC5B,IAAI;QACH,MAAMC,SAAS,MAAMP,YAAY;YAAEG;QAAM;QAEzC,IAAII,OAAOK,WAAW,KAAK,IAAI;YAC9BR,IAAIS,KAAK,CAACN,OAAOK,WAAW;YAC5BH,QAAQC,IAAI,CAACH,OAAOI,QAAQ;QAC7B;QAEA,IAAIJ,OAAOC,UAAU,KAAKN,QAAQ;YACjCE,IAAIU,IAAI,CAAC,CAAC,eAAe,EAAEX,MAAMY,aAAa,EAAE;YAChDX,IAAIU,IAAI,CAAC,CAAC,QAAQ,EAAEP,OAAOC,UAAU,EAAE;YACvCJ,IAAIA,GAAG,CAACG,OAAOU,IAAI;QACpB,OAAO;YACN,IAAI;gBACHpB,GAAGwB,cAAc,CAACd,OAAOC,UAAU,EAAED,OAAOU,IAAI;YACjD,EAAE,OAAOJ,OAAO;gBACfT,IAAIS,KAAK,CAAC,CAAC,yBAAyB,EAAEA,MAAMO,OAAO,EAAE;gBACrDX,QAAQC,IAAI,CAAC;YACd;QACD;QACAD,QAAQC,IAAI,CAACH,OAAOI,QAAQ;IAC7B,EAAE,OAAOE,OAAO;QACfT,IAAIS,KAAK,CAACA;QACVJ,QAAQC,IAAI,CAAC;IACd;AACD;AAEAN,IAAIS,KAAK,CAAC;AACVJ,QAAQC,IAAI,CAAC"}
@@ -5,3 +5,4 @@ export declare const defaultFlags: {
5
5
  type: string;
6
6
  force: boolean;
7
7
  };
8
+ export declare const DEFAULT_THRESHOLD = 0;
package/dist/defaults.js CHANGED
@@ -1,9 +1,10 @@
1
- /* v8 ignore start */ export const defaultFlags = {
1
+ /* istanbul ignore file */ export const defaultFlags = {
2
2
  boring: false,
3
3
  configuration: "",
4
4
  silent: false,
5
5
  type: "size",
6
6
  force: false
7
- }; /* v8 ignore stop */
7
+ };
8
+ export const DEFAULT_THRESHOLD = 0;
8
9
 
9
10
  //# sourceMappingURL=defaults.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["/* v8 ignore start */\n\nexport const defaultFlags = {\n\tboring: false,\n\tconfiguration: \"\",\n\tsilent: false,\n\ttype: \"size\",\n\tforce: false,\n};\n/* v8 ignore stop */\n"],"names":["defaultFlags","boring","configuration","silent","type","force"],"mappings":"AAAA,mBAAmB,GAEnB,OAAO,MAAMA,eAAe;IAC3BC,QAAQ;IACRC,eAAe;IACfC,QAAQ;IACRC,MAAM;IACNC,OAAO;AACR,EAAE,CACF,kBAAkB"}
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"}
@@ -32,21 +32,27 @@ export const getRawStats = async ({ flags })=>{
32
32
  return result;
33
33
  }
34
34
  for (const artifact of configuration.sizes){
35
- const rootPath = artifact.path.startsWith("/") ? "" : dirname(configurationFile);
36
- const artifactPath = dirname(artifact.path);
37
- const hasHash = artifact.path.includes(HASH_KEY);
38
- const hasSemver = artifact.path.includes(SEMVER_KEY);
35
+ // Support header-only entries (group markers) that do not contribute to raw
36
+ // stats.
37
+ if (artifact.header) {
38
+ continue;
39
+ }
40
+ const sizeArtifact = artifact;
41
+ const rootPath = sizeArtifact.path.startsWith("/") ? "" : dirname(configurationFile);
42
+ const artifactPath = dirname(sizeArtifact.path);
43
+ const hasHash = sizeArtifact.path.includes(HASH_KEY);
44
+ const hasSemver = sizeArtifact.path.includes(SEMVER_KEY);
39
45
  if (hasSemver && hasHash) {
40
46
  result.exitCode = 1;
41
- result.exitMessage = `Invalid path: ${artifact.path}.\nCannot use ${HASH_KEY} and ${SEMVER_KEY} in the same path.`;
47
+ result.exitMessage = `Invalid path: ${sizeArtifact.path}.\nCannot use ${HASH_KEY} and ${SEMVER_KEY} in the same path.`;
42
48
  return result;
43
49
  }
44
- let location = artifact.path;
50
+ let location = sizeArtifact.path;
45
51
  if (hasHash) {
46
- location = artifact.path.replace(HASH_KEY, GLOB_HASH);
52
+ location = sizeArtifact.path.replace(HASH_KEY, GLOB_HASH);
47
53
  }
48
54
  if (hasSemver) {
49
- location = artifact.path.replace(SEMVER_KEY, GLOB_SEMVER);
55
+ location = sizeArtifact.path.replace(SEMVER_KEY, GLOB_SEMVER);
50
56
  }
51
57
  const fileGlob = join(rootPath, location);
52
58
  const files = glob.sync(fileGlob);
@@ -57,25 +63,25 @@ export const getRawStats = async ({ flags })=>{
57
63
  }
58
64
  if (files.length > 1) {
59
65
  result.exitCode = 1;
60
- result.exitMessage = `Multiple files found for: ${artifact.path}.\nPlease use a more specific path.`;
66
+ result.exitMessage = `Multiple files found for: ${sizeArtifact.path}.\nPlease use a more specific path.`;
61
67
  return result;
62
68
  }
63
69
  for (const file of files){
64
70
  const fileSize = statSync(file).size;
65
71
  const fileSizeGzip = gzipSizeFromFileSync(file);
66
- const passed = fileSizeGzip < bytes(artifact.limit);
72
+ const passed = fileSizeGzip < bytes(sizeArtifact.limit);
67
73
  if (passed === false) {
68
74
  result.pass = false;
69
75
  failed = true;
70
76
  }
71
- let index = hasHash || hasSemver ? artifact.path : join(artifactPath, basename(file));
72
- if (artifact.alias) {
73
- index = artifact.alias;
77
+ let index = hasHash || hasSemver ? sizeArtifact.path : join(artifactPath, basename(file));
78
+ if (sizeArtifact.alias) {
79
+ index = sizeArtifact.alias;
74
80
  }
75
81
  currentResults[index] = {
76
82
  fileSize,
77
83
  fileSizeGzip,
78
- limit: artifact.limit,
84
+ limit: sizeArtifact.limit,
79
85
  passed
80
86
  };
81
87
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/getRawStats.ts"],"sourcesContent":["import { statSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport bytes from \"bytes\";\nimport fs from \"fs-extra\";\nimport { glob } from \"glob\";\nimport {\n\tGLOB_HASH,\n\tGLOB_SEMVER,\n\tgetOutputFile,\n\tgzipSizeFromFileSync,\n\tHASH_KEY,\n\tIGNORE,\n\tSEMVER_KEY,\n\tSTDOUT,\n\tvalidateConfigurationFile,\n} from \"./utilities.js\";\n\ntype SizesConfiguration = {\n\tlimit: string;\n\tpath: string;\n\talias?: string;\n};\n\ntype ReportStats = {\n\tdata: Record<string, unknown>;\n\texitCode: number;\n\texitMessage: string;\n\toutputFile: string;\n\tpass: boolean;\n\tprefix: string;\n};\n\nexport const getRawStats = async ({ flags }): Promise<ReportStats> => {\n\tconst result: ReportStats = {\n\t\tpass: true,\n\t\texitCode: 0,\n\t\texitMessage: \"\",\n\t\toutputFile: \"\",\n\t\tprefix: \"\",\n\t\tdata: {},\n\t};\n\tlet failed = false;\n\tconst isValidConfigResult = validateConfigurationFile(flags.configuration);\n\tif (isValidConfigResult.exitMessage !== \"\") {\n\t\treturn {\n\t\t\t...result,\n\t\t\t...isValidConfigResult,\n\t\t};\n\t}\n\tconst configurationFile = isValidConfigResult.data;\n\tconst outputFile = getOutputFile(flags.output);\n\tconst prefix = flags.prefix || \"0.0.0\";\n\tconst currentResults = {};\n\n\tconst configuration: { sizes: SizesConfiguration[] } = await import(\n\t\tconfigurationFile\n\t).then((m) => m.default);\n\n\tif (configuration.sizes === undefined) {\n\t\tresult.exitMessage = \"Invalid configuration file: missing sizes object!\";\n\t\tresult.exitCode = 1;\n\t\treturn result;\n\t}\n\n\tfor (const artifact of configuration.sizes) {\n\t\tconst rootPath = artifact.path.startsWith(\"/\")\n\t\t\t? \"\"\n\t\t\t: dirname(configurationFile);\n\t\tconst artifactPath = dirname(artifact.path);\n\t\tconst hasHash = artifact.path.includes(HASH_KEY);\n\t\tconst hasSemver = artifact.path.includes(SEMVER_KEY);\n\n\t\tif (hasSemver && hasHash) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.exitMessage = `Invalid path: ${artifact.path}.\\nCannot use ${HASH_KEY} and ${SEMVER_KEY} in the same path.`;\n\t\t\treturn result;\n\t\t}\n\n\t\tlet location = artifact.path;\n\t\tif (hasHash) {\n\t\t\tlocation = artifact.path.replace(HASH_KEY, GLOB_HASH);\n\t\t}\n\t\tif (hasSemver) {\n\t\t\tlocation = artifact.path.replace(SEMVER_KEY, GLOB_SEMVER);\n\t\t}\n\t\tconst fileGlob = join(rootPath, location);\n\t\tconst files = glob.sync(fileGlob);\n\n\t\tif (files.length === 0) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.exitMessage = `File not found: ${fileGlob}`;\n\t\t\treturn result;\n\t\t}\n\n\t\tif (files.length > 1) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.exitMessage = `Multiple files found for: ${artifact.path}.\\nPlease use a more specific path.`;\n\t\t\treturn result;\n\t\t}\n\n\t\tfor (const file of files) {\n\t\t\tconst fileSize = statSync(file).size;\n\t\t\tconst fileSizeGzip = gzipSizeFromFileSync(file);\n\t\t\tconst passed = fileSizeGzip < bytes(artifact.limit);\n\n\t\t\tif (passed === false) {\n\t\t\t\tresult.pass = false;\n\t\t\t\tfailed = true;\n\t\t\t}\n\n\t\t\tlet index =\n\t\t\t\thasHash || hasSemver\n\t\t\t\t\t? artifact.path\n\t\t\t\t\t: join(artifactPath, basename(file));\n\t\t\tif (artifact.alias) {\n\t\t\t\tindex = artifact.alias;\n\t\t\t}\n\n\t\t\tcurrentResults[index] = {\n\t\t\t\tfileSize,\n\t\t\t\tfileSizeGzip,\n\t\t\t\tlimit: artifact.limit,\n\t\t\t\tpassed,\n\t\t\t};\n\t\t}\n\t}\n\n\tlet existingResults = {};\n\tif (outputFile !== STDOUT) {\n\t\ttry {\n\t\t\texistingResults = fs.readJsonSync(outputFile);\n\t\t\t/* v8 ignore next 6 */\n\t\t} catch {\n\t\t\t/**\n\t\t\t * There are no existing results, so we can ignore this error, and simply\n\t\t\t * write the current results to the output file.\n\t\t\t */\n\t\t}\n\t}\n\n\t/**\n\t * If the prefix already exists in the output file,\n\t * - if --force flag is used, overwrite the existing results\n\t * - if --force flag is not used, ignore the new results and\n\t * keep the existing ones.\n\t */\n\tif (existingResults[prefix] !== undefined && flags.force === false) {\n\t\tresult.outputFile = IGNORE;\n\t} else {\n\t\tresult.outputFile = outputFile;\n\t\texistingResults[prefix] = currentResults;\n\t}\n\n\tresult.prefix = prefix;\n\tresult.exitCode = flags.silent === true ? 0 : Number(failed);\n\tresult.data = existingResults;\n\treturn result;\n};\n"],"names":["statSync","basename","dirname","join","bytes","fs","glob","GLOB_HASH","GLOB_SEMVER","getOutputFile","gzipSizeFromFileSync","HASH_KEY","IGNORE","SEMVER_KEY","STDOUT","validateConfigurationFile","getRawStats","flags","result","pass","exitCode","exitMessage","outputFile","prefix","data","failed","isValidConfigResult","configuration","configurationFile","output","currentResults","then","m","default","sizes","undefined","artifact","rootPath","path","startsWith","artifactPath","hasHash","includes","hasSemver","location","replace","fileGlob","files","sync","length","file","fileSize","size","fileSizeGzip","passed","limit","index","alias","existingResults","readJsonSync","force","silent","Number"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/getRawStats.ts"],"sourcesContent":["import { statSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport bytes from \"bytes\";\nimport fs from \"fs-extra\";\nimport { glob } from \"glob\";\nimport {\n\tGLOB_HASH,\n\tGLOB_SEMVER,\n\tgetOutputFile,\n\tgzipSizeFromFileSync,\n\tHASH_KEY,\n\tIGNORE,\n\tSEMVER_KEY,\n\tSTDOUT,\n\tvalidateConfigurationFile,\n} from \"./utilities.js\";\n\ntype SizeEntry = {\n\tlimit: string;\n\tpath: string;\n\talias?: string;\n};\n\ntype HeaderEntry = { header: string };\n\ntype SizesConfiguration = SizeEntry | HeaderEntry;\n\ntype ReportStats = {\n\tdata: Record<string, unknown>;\n\texitCode: number;\n\texitMessage: string;\n\toutputFile: string;\n\tpass: boolean;\n\tprefix: string;\n};\n\nexport const getRawStats = async ({ flags }): Promise<ReportStats> => {\n\tconst result: ReportStats = {\n\t\tpass: true,\n\t\texitCode: 0,\n\t\texitMessage: \"\",\n\t\toutputFile: \"\",\n\t\tprefix: \"\",\n\t\tdata: {},\n\t};\n\tlet failed = false;\n\tconst isValidConfigResult = validateConfigurationFile(flags.configuration);\n\tif (isValidConfigResult.exitMessage !== \"\") {\n\t\treturn {\n\t\t\t...result,\n\t\t\t...isValidConfigResult,\n\t\t};\n\t}\n\tconst configurationFile = isValidConfigResult.data;\n\tconst outputFile = getOutputFile(flags.output);\n\tconst prefix = flags.prefix || \"0.0.0\";\n\tconst currentResults = {};\n\n\tconst configuration: { sizes: SizesConfiguration[] } = await import(\n\t\tconfigurationFile\n\t).then((m) => m.default);\n\n\tif (configuration.sizes === undefined) {\n\t\tresult.exitMessage = \"Invalid configuration file: missing sizes object!\";\n\t\tresult.exitCode = 1;\n\t\treturn result;\n\t}\n\n\tfor (const artifact of configuration.sizes) {\n\t\t// Support header-only entries (group markers) that do not contribute to raw\n\t\t// stats.\n\t\tif ((artifact as HeaderEntry).header) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst sizeArtifact = artifact as SizeEntry;\n\t\tconst rootPath = sizeArtifact.path.startsWith(\"/\")\n\t\t\t? \"\"\n\t\t\t: dirname(configurationFile);\n\t\tconst artifactPath = dirname(sizeArtifact.path);\n\t\tconst hasHash = sizeArtifact.path.includes(HASH_KEY);\n\t\tconst hasSemver = sizeArtifact.path.includes(SEMVER_KEY);\n\n\t\tif (hasSemver && hasHash) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.exitMessage = `Invalid path: ${sizeArtifact.path}.\\nCannot use ${HASH_KEY} and ${SEMVER_KEY} in the same path.`;\n\t\t\treturn result;\n\t\t}\n\n\t\tlet location = sizeArtifact.path;\n\t\tif (hasHash) {\n\t\t\tlocation = sizeArtifact.path.replace(HASH_KEY, GLOB_HASH);\n\t\t}\n\t\tif (hasSemver) {\n\t\t\tlocation = sizeArtifact.path.replace(SEMVER_KEY, GLOB_SEMVER);\n\t\t}\n\t\tconst fileGlob = join(rootPath, location);\n\t\tconst files = glob.sync(fileGlob);\n\n\t\tif (files.length === 0) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.exitMessage = `File not found: ${fileGlob}`;\n\t\t\treturn result;\n\t\t}\n\n\t\tif (files.length > 1) {\n\t\t\tresult.exitCode = 1;\n\t\t\tresult.exitMessage = `Multiple files found for: ${sizeArtifact.path}.\\nPlease use a more specific path.`;\n\t\t\treturn result;\n\t\t}\n\n\t\tfor (const file of files) {\n\t\t\tconst fileSize = statSync(file).size;\n\t\t\tconst fileSizeGzip = gzipSizeFromFileSync(file);\n\t\t\tconst passed = fileSizeGzip < bytes(sizeArtifact.limit);\n\n\t\t\tif (passed === false) {\n\t\t\t\tresult.pass = false;\n\t\t\t\tfailed = true;\n\t\t\t}\n\n\t\t\tlet index =\n\t\t\t\thasHash || hasSemver\n\t\t\t\t\t? sizeArtifact.path\n\t\t\t\t\t: join(artifactPath, basename(file));\n\t\t\tif (sizeArtifact.alias) {\n\t\t\t\tindex = sizeArtifact.alias;\n\t\t\t}\n\n\t\t\tcurrentResults[index] = {\n\t\t\t\tfileSize,\n\t\t\t\tfileSizeGzip,\n\t\t\t\tlimit: sizeArtifact.limit,\n\t\t\t\tpassed,\n\t\t\t};\n\t\t}\n\t}\n\n\tlet existingResults = {};\n\tif (outputFile !== STDOUT) {\n\t\ttry {\n\t\t\texistingResults = fs.readJsonSync(outputFile);\n\t\t\t/* v8 ignore next 6 */\n\t\t} catch {\n\t\t\t/**\n\t\t\t * There are no existing results, so we can ignore this error, and simply\n\t\t\t * write the current results to the output file.\n\t\t\t */\n\t\t}\n\t}\n\n\t/**\n\t * If the prefix already exists in the output file,\n\t * - if --force flag is used, overwrite the existing results\n\t * - if --force flag is not used, ignore the new results and\n\t * keep the existing ones.\n\t */\n\tif (existingResults[prefix] !== undefined && flags.force === false) {\n\t\tresult.outputFile = IGNORE;\n\t} else {\n\t\tresult.outputFile = outputFile;\n\t\texistingResults[prefix] = currentResults;\n\t}\n\n\tresult.prefix = prefix;\n\tresult.exitCode = flags.silent === true ? 0 : Number(failed);\n\tresult.data = existingResults;\n\treturn result;\n};\n"],"names":["statSync","basename","dirname","join","bytes","fs","glob","GLOB_HASH","GLOB_SEMVER","getOutputFile","gzipSizeFromFileSync","HASH_KEY","IGNORE","SEMVER_KEY","STDOUT","validateConfigurationFile","getRawStats","flags","result","pass","exitCode","exitMessage","outputFile","prefix","data","failed","isValidConfigResult","configuration","configurationFile","output","currentResults","then","m","default","sizes","undefined","artifact","header","sizeArtifact","rootPath","path","startsWith","artifactPath","hasHash","includes","hasSemver","location","replace","fileGlob","files","sync","length","file","fileSize","size","fileSizeGzip","passed","limit","index","alias","existingResults","readJsonSync","force","silent","Number"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,UAAU;AACnC,SAASC,QAAQ,EAAEC,OAAO,EAAEC,IAAI,QAAQ,YAAY;AACpD,OAAOC,WAAW,QAAQ;AAC1B,OAAOC,QAAQ,WAAW;AAC1B,SAASC,IAAI,QAAQ,OAAO;AAC5B,SACCC,SAAS,EACTC,WAAW,EACXC,aAAa,EACbC,oBAAoB,EACpBC,QAAQ,EACRC,MAAM,EACNC,UAAU,EACVC,MAAM,EACNC,yBAAyB,QACnB,iBAAiB;AAqBxB,OAAO,MAAMC,cAAc,OAAO,EAAEC,KAAK,EAAE;IAC1C,MAAMC,SAAsB;QAC3BC,MAAM;QACNC,UAAU;QACVC,aAAa;QACbC,YAAY;QACZC,QAAQ;QACRC,MAAM,CAAC;IACR;IACA,IAAIC,SAAS;IACb,MAAMC,sBAAsBX,0BAA0BE,MAAMU,aAAa;IACzE,IAAID,oBAAoBL,WAAW,KAAK,IAAI;QAC3C,OAAO;YACN,GAAGH,MAAM;YACT,GAAGQ,mBAAmB;QACvB;IACD;IACA,MAAME,oBAAoBF,oBAAoBF,IAAI;IAClD,MAAMF,aAAab,cAAcQ,MAAMY,MAAM;IAC7C,MAAMN,SAASN,MAAMM,MAAM,IAAI;IAC/B,MAAMO,iBAAiB,CAAC;IAExB,MAAMH,gBAAiD,MAAM,MAAM,CAClEC,mBACCG,IAAI,CAAC,CAACC,IAAMA,EAAEC,OAAO;IAEvB,IAAIN,cAAcO,KAAK,KAAKC,WAAW;QACtCjB,OAAOG,WAAW,GAAG;QACrBH,OAAOE,QAAQ,GAAG;QAClB,OAAOF;IACR;IAEA,KAAK,MAAMkB,YAAYT,cAAcO,KAAK,CAAE;QAC3C,4EAA4E;QAC5E,SAAS;QACT,IAAI,AAACE,SAAyBC,MAAM,EAAE;YACrC;QACD;QAEA,MAAMC,eAAeF;QACrB,MAAMG,WAAWD,aAAaE,IAAI,CAACC,UAAU,CAAC,OAC3C,KACAvC,QAAQ0B;QACX,MAAMc,eAAexC,QAAQoC,aAAaE,IAAI;QAC9C,MAAMG,UAAUL,aAAaE,IAAI,CAACI,QAAQ,CAACjC;QAC3C,MAAMkC,YAAYP,aAAaE,IAAI,CAACI,QAAQ,CAAC/B;QAE7C,IAAIgC,aAAaF,SAAS;YACzBzB,OAAOE,QAAQ,GAAG;YAClBF,OAAOG,WAAW,GAAG,CAAC,cAAc,EAAEiB,aAAaE,IAAI,CAAC,cAAc,EAAE7B,SAAS,KAAK,EAAEE,WAAW,kBAAkB,CAAC;YACtH,OAAOK;QACR;QAEA,IAAI4B,WAAWR,aAAaE,IAAI;QAChC,IAAIG,SAAS;YACZG,WAAWR,aAAaE,IAAI,CAACO,OAAO,CAACpC,UAAUJ;QAChD;QACA,IAAIsC,WAAW;YACdC,WAAWR,aAAaE,IAAI,CAACO,OAAO,CAAClC,YAAYL;QAClD;QACA,MAAMwC,WAAW7C,KAAKoC,UAAUO;QAChC,MAAMG,QAAQ3C,KAAK4C,IAAI,CAACF;QAExB,IAAIC,MAAME,MAAM,KAAK,GAAG;YACvBjC,OAAOE,QAAQ,GAAG;YAClBF,OAAOG,WAAW,GAAG,CAAC,gBAAgB,EAAE2B,UAAU;YAClD,OAAO9B;QACR;QAEA,IAAI+B,MAAME,MAAM,GAAG,GAAG;YACrBjC,OAAOE,QAAQ,GAAG;YAClBF,OAAOG,WAAW,GAAG,CAAC,0BAA0B,EAAEiB,aAAaE,IAAI,CAAC,mCAAmC,CAAC;YACxG,OAAOtB;QACR;QAEA,KAAK,MAAMkC,QAAQH,MAAO;YACzB,MAAMI,WAAWrD,SAASoD,MAAME,IAAI;YACpC,MAAMC,eAAe7C,qBAAqB0C;YAC1C,MAAMI,SAASD,eAAenD,MAAMkC,aAAamB,KAAK;YAEtD,IAAID,WAAW,OAAO;gBACrBtC,OAAOC,IAAI,GAAG;gBACdM,SAAS;YACV;YAEA,IAAIiC,QACHf,WAAWE,YACRP,aAAaE,IAAI,GACjBrC,KAAKuC,cAAczC,SAASmD;YAChC,IAAId,aAAaqB,KAAK,EAAE;gBACvBD,QAAQpB,aAAaqB,KAAK;YAC3B;YAEA7B,cAAc,CAAC4B,MAAM,GAAG;gBACvBL;gBACAE;gBACAE,OAAOnB,aAAamB,KAAK;gBACzBD;YACD;QACD;IACD;IAEA,IAAII,kBAAkB,CAAC;IACvB,IAAItC,eAAeR,QAAQ;QAC1B,IAAI;YACH8C,kBAAkBvD,GAAGwD,YAAY,CAACvC;QAClC,oBAAoB,GACrB,EAAE,OAAM;QACP;;;IAGC,GACF;IACD;IAEA;;;;;EAKC,GACD,IAAIsC,eAAe,CAACrC,OAAO,KAAKY,aAAalB,MAAM6C,KAAK,KAAK,OAAO;QACnE5C,OAAOI,UAAU,GAAGV;IACrB,OAAO;QACNM,OAAOI,UAAU,GAAGA;QACpBsC,eAAe,CAACrC,OAAO,GAAGO;IAC3B;IAEAZ,OAAOK,MAAM,GAAGA;IAChBL,OAAOE,QAAQ,GAAGH,MAAM8C,MAAM,KAAK,OAAO,IAAIC,OAAOvC;IACrDP,OAAOM,IAAI,GAAGoC;IACd,OAAO1C;AACR,EAAE"}
package/dist/parse.js CHANGED
@@ -1,4 +1,4 @@
1
- /* v8 ignore start */ import { parser } from "@node-cli/parser";
1
+ /* istanbul ignore file */ import { parser } from "@node-cli/parser";
2
2
  import { defaultFlags } from "./defaults.js";
3
3
  export const config = parser({
4
4
  meta: import.meta,
@@ -56,6 +56,6 @@ export const config = parser({
56
56
  },
57
57
  usage: true,
58
58
  defaultFlags
59
- }); /* v8 ignore stop */
59
+ });
60
60
 
61
61
  //# sourceMappingURL=parse.js.map
package/dist/parse.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/parse.ts"],"sourcesContent":["/* v8 ignore start */\nimport { parser } from \"@node-cli/parser\";\nimport { defaultFlags } from \"./defaults.js\";\n\nexport type Flags = {\n\tboring?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tconfiguration?: string;\n\toutput?: string;\n\tprefix?: string;\n\tsilent?: boolean;\n\ttype?: \"size\" | \"report\";\n\tforce?: boolean;\n};\n\nexport type Configuration = {\n\tflags?: Flags;\n\tusage?: boolean;\n\tshowHelp?: () => void;\n};\n\nexport const config: Configuration = parser({\n\tmeta: import.meta,\n\texamples: [],\n\tflags: {\n\t\tforce: {\n\t\t\tshortFlag: \"f\",\n\t\t\tdescription: \"Overwrite existing results\",\n\t\t\ttype: \"boolean\",\n\t\t\tdefault: defaultFlags.force,\n\t\t},\n\t\ttype: {\n\t\t\tshortFlag: \"t\",\n\t\t\tdescription: \"Specify the type of output: size or report\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: defaultFlags.type,\n\t\t},\n\t\tconfiguration: {\n\t\t\tshortFlag: \"c\",\n\t\t\tdescription: \"Specify a configuration file\",\n\t\t\ttype: \"string\",\n\t\t},\n\t\toutput: {\n\t\t\tshortFlag: \"o\",\n\t\t\tdescription: \"Specify the output file\",\n\t\t\ttype: \"string\",\n\t\t},\n\t\tprefix: {\n\t\t\tshortFlag: \"p\",\n\t\t\tdescription: \"Specify a prefix to use in the output file\",\n\t\t\ttype: \"string\",\n\t\t},\n\t\tsilent: {\n\t\t\tshortFlag: \"s\",\n\t\t\tdefault: defaultFlags.silent,\n\t\t\tdescription: \"Do not exit in error when a limit is exceeded\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\tboring: {\n\t\t\tshortFlag: \"b\",\n\t\t\tdefault: defaultFlags.boring,\n\t\t\tdescription: \"Do not use color output\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\thelp: {\n\t\t\tshortFlag: \"h\",\n\t\t\tdescription: \"Display help instructions\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\tversion: {\n\t\t\tshortFlag: \"v\",\n\t\t\tdescription: \"Output the current version\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t},\n\tusage: true,\n\tdefaultFlags,\n});\n/* v8 ignore stop */\n"],"names":["parser","defaultFlags","config","meta","examples","flags","force","shortFlag","description","type","default","configuration","output","prefix","silent","boring","help","version","usage"],"mappings":"AAAA,mBAAmB,GACnB,SAASA,MAAM,QAAQ,mBAAmB;AAC1C,SAASC,YAAY,QAAQ,gBAAgB;AAoB7C,OAAO,MAAMC,SAAwBF,OAAO;IAC3CG,MAAM;IACNC,UAAU,EAAE;IACZC,OAAO;QACNC,OAAO;YACNC,WAAW;YACXC,aAAa;YACbC,MAAM;YACNC,SAAST,aAAaK,KAAK;QAC5B;QACAG,MAAM;YACLF,WAAW;YACXC,aAAa;YACbC,MAAM;YACNC,SAAST,aAAaQ,IAAI;QAC3B;QACAE,eAAe;YACdJ,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAG,QAAQ;YACPL,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAI,QAAQ;YACPN,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAK,QAAQ;YACPP,WAAW;YACXG,SAAST,aAAaa,MAAM;YAC5BN,aAAa;YACbC,MAAM;QACP;QACAM,QAAQ;YACPR,WAAW;YACXG,SAAST,aAAac,MAAM;YAC5BP,aAAa;YACbC,MAAM;QACP;QACAO,MAAM;YACLT,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAQ,SAAS;YACRV,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;IACD;IACAS,OAAO;IACPjB;AACD,GAAG,CACH,kBAAkB"}
1
+ {"version":3,"sources":["../src/parse.ts"],"sourcesContent":["/* istanbul ignore file */\nimport { parser } from \"@node-cli/parser\";\nimport { defaultFlags } from \"./defaults.js\";\n\nexport type Flags = {\n\tboring?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tconfiguration?: string;\n\toutput?: string;\n\tprefix?: string;\n\tsilent?: boolean;\n\ttype?: \"size\" | \"report\";\n\tforce?: boolean;\n};\n\nexport type Configuration = {\n\tflags?: Flags;\n\tusage?: boolean;\n\tshowHelp?: () => void;\n};\n\nexport const config: Configuration = parser({\n\tmeta: import.meta,\n\texamples: [],\n\tflags: {\n\t\tforce: {\n\t\t\tshortFlag: \"f\",\n\t\t\tdescription: \"Overwrite existing results\",\n\t\t\ttype: \"boolean\",\n\t\t\tdefault: defaultFlags.force,\n\t\t},\n\t\ttype: {\n\t\t\tshortFlag: \"t\",\n\t\t\tdescription: \"Specify the type of output: size or report\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: defaultFlags.type,\n\t\t},\n\t\tconfiguration: {\n\t\t\tshortFlag: \"c\",\n\t\t\tdescription: \"Specify a configuration file\",\n\t\t\ttype: \"string\",\n\t\t},\n\t\toutput: {\n\t\t\tshortFlag: \"o\",\n\t\t\tdescription: \"Specify the output file\",\n\t\t\ttype: \"string\",\n\t\t},\n\t\tprefix: {\n\t\t\tshortFlag: \"p\",\n\t\t\tdescription: \"Specify a prefix to use in the output file\",\n\t\t\ttype: \"string\",\n\t\t},\n\t\tsilent: {\n\t\t\tshortFlag: \"s\",\n\t\t\tdefault: defaultFlags.silent,\n\t\t\tdescription: \"Do not exit in error when a limit is exceeded\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\tboring: {\n\t\t\tshortFlag: \"b\",\n\t\t\tdefault: defaultFlags.boring,\n\t\t\tdescription: \"Do not use color output\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\thelp: {\n\t\t\tshortFlag: \"h\",\n\t\t\tdescription: \"Display help instructions\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\tversion: {\n\t\t\tshortFlag: \"v\",\n\t\t\tdescription: \"Output the current version\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t},\n\tusage: true,\n\tdefaultFlags,\n});\n"],"names":["parser","defaultFlags","config","meta","examples","flags","force","shortFlag","description","type","default","configuration","output","prefix","silent","boring","help","version","usage"],"mappings":"AAAA,wBAAwB,GACxB,SAASA,MAAM,QAAQ,mBAAmB;AAC1C,SAASC,YAAY,QAAQ,gBAAgB;AAoB7C,OAAO,MAAMC,SAAwBF,OAAO;IAC3CG,MAAM;IACNC,UAAU,EAAE;IACZC,OAAO;QACNC,OAAO;YACNC,WAAW;YACXC,aAAa;YACbC,MAAM;YACNC,SAAST,aAAaK,KAAK;QAC5B;QACAG,MAAM;YACLF,WAAW;YACXC,aAAa;YACbC,MAAM;YACNC,SAAST,aAAaQ,IAAI;QAC3B;QACAE,eAAe;YACdJ,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAG,QAAQ;YACPL,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAI,QAAQ;YACPN,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAK,QAAQ;YACPP,WAAW;YACXG,SAAST,aAAaa,MAAM;YAC5BN,aAAa;YACbC,MAAM;QACP;QACAM,QAAQ;YACPR,WAAW;YACXG,SAAST,aAAac,MAAM;YAC5BP,aAAa;YACbC,MAAM;QACP;QACAO,MAAM;YACLT,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAQ,SAAS;YACRV,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;IACD;IACAS,OAAO;IACPjB;AACD,GAAG"}
@@ -1,6 +1,8 @@
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";
5
+ const isHeaderEntry = (entry)=>"header" in entry;
4
6
  export const reportStats = async ({ flags })=>{
5
7
  const result = {
6
8
  exitCode: 0,
@@ -37,12 +39,13 @@ export const reportStats = async ({ flags })=>{
37
39
  }
38
40
  previousVersion = getMostRecentVersion(previousStats.data);
39
41
  } catch {
40
- // nothing to declare officer.
42
+ // no previous stats available.
41
43
  }
42
44
  const currentVersion = getMostRecentVersion(currentStats.data);
43
45
  let limitReached = false;
44
46
  let overallDiff = 0;
45
47
  let totalGzipSize = 0;
48
+ const threshold = configuration.report.threshold ?? DEFAULT_THRESHOLD;
46
49
  const headerString = configuration.report.header || "## Bundle Size";
47
50
  const footerFormatter = configuration.report.footer || formatFooter;
48
51
  const columns = configuration.report.columns || [
@@ -60,27 +63,113 @@ export const reportStats = async ({ flags })=>{
60
63
  }
61
64
  ];
62
65
  const rowsMD = [];
63
- rowsMD.push(addMDrow({
64
- type: "header",
65
- columns
66
- }));
67
- for (const key of Object.keys(currentStats.data[currentVersion])){
66
+ const hasGroupHeaders = Boolean(configuration.sizes?.some(isHeaderEntry));
67
+ if (!hasGroupHeaders) {
68
+ rowsMD.push(addMDrow({
69
+ type: "header",
70
+ columns
71
+ }));
72
+ }
73
+ // Build ordered keys based on configuration.sizes if present.
74
+ const orderedKeys = [];
75
+ if (configuration.sizes && Array.isArray(configuration.sizes)) {
76
+ for (const entry of configuration.sizes){
77
+ if (isHeaderEntry(entry)) {
78
+ orderedKeys.push({
79
+ type: "header",
80
+ value: "",
81
+ header: entry.header
82
+ });
83
+ continue;
84
+ }
85
+ // entry is FileSizeEntry here.
86
+ if (entry.alias && currentStats.data[currentVersion][entry.alias]) {
87
+ orderedKeys.push({
88
+ type: "item",
89
+ value: entry.alias
90
+ });
91
+ } else if (currentStats.data[currentVersion][entry.path]) {
92
+ orderedKeys.push({
93
+ type: "item",
94
+ value: entry.path
95
+ });
96
+ }
97
+ }
98
+ }
99
+ if (orderedKeys.length === 0) {
100
+ for (const key of Object.keys(currentStats.data[currentVersion])){
101
+ orderedKeys.push({
102
+ type: "item",
103
+ value: key
104
+ });
105
+ }
106
+ }
107
+ let subGroupAccumulatedSize = 0;
108
+ let subGroupAccumulatedPrevSize = 0;
109
+ let hasSubGroup = false;
110
+ const flushSubGroup = ()=>{
111
+ if (!hasSubGroup) {
112
+ return;
113
+ }
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
+ }
119
+ let diffString = "";
120
+ if (diff !== 0 && subGroupAccumulatedPrevSize !== 0) {
121
+ const sign = diff > 0 ? "+" : "-";
122
+ diffString = ` (${sign}${bytes(Math.abs(diff), {
123
+ unitSeparator: " "
124
+ })} ${percentFormatter(diff / subGroupAccumulatedPrevSize)})`;
125
+ }
126
+ if (hasGroupHeaders) {
127
+ rowsMD.push(`\nSub-bundle size: ${bytes(subGroupAccumulatedSize, {
128
+ unitSeparator: " "
129
+ })}${diffString}`, "");
130
+ }
131
+ subGroupAccumulatedSize = 0;
132
+ subGroupAccumulatedPrevSize = 0;
133
+ hasSubGroup = false;
134
+ };
135
+ for (const entry of orderedKeys){
136
+ if (entry.type === "header") {
137
+ flushSubGroup();
138
+ rowsMD.push("", entry.header, "");
139
+ rowsMD.push(addMDrow({
140
+ type: "header",
141
+ columns
142
+ }));
143
+ continue;
144
+ }
145
+ const key = entry.value;
68
146
  const item = currentStats.data[currentVersion][key];
147
+ if (!item) {
148
+ continue;
149
+ }
69
150
  const name = basename(key);
70
151
  if (!item.passed) {
71
152
  limitReached = true;
72
153
  }
73
154
  let diffString = "";
155
+ let previousFileSizeGzip = 0;
74
156
  if (previousStats && previousVersion) {
75
157
  const previousFileStats = previousStats.data[previousVersion] && previousStats.data[previousVersion][key];
76
- const previousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;
77
- const diff = item.fileSizeGzip - previousFileSizeGzip;
158
+ previousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;
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
+ }
78
164
  overallDiff += diff;
79
165
  diffString = diff === 0 || diff === item.fileSizeGzip ? "" : ` (${diff > 0 ? "+" : "-"}${bytes(Math.abs(diff), {
80
166
  unitSeparator: " "
81
- })} ${percentFormatter(diff / previousFileSizeGzip)})`;
167
+ })} ${percentFormatter(previousFileSizeGzip === 0 ? 0 : diff / previousFileSizeGzip)})`;
82
168
  }
83
169
  totalGzipSize += item.fileSizeGzip;
170
+ subGroupAccumulatedSize += item.fileSizeGzip;
171
+ subGroupAccumulatedPrevSize += previousFileSizeGzip;
172
+ hasSubGroup = true;
84
173
  rowsMD.push(addMDrow({
85
174
  type: "row",
86
175
  passed: item.passed,
@@ -91,6 +180,7 @@ export const reportStats = async ({ flags })=>{
91
180
  columns
92
181
  }));
93
182
  }
183
+ flushSubGroup();
94
184
  const template = `
95
185
  ${headerString}
96
186
  ${rowsMD.join("\n")}
@@ -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\n\theader?: string;\n\tfooter?: (arguments_: FooterProperties) => string;\n\tcolumns?: { [key: string]: string }[];\n};\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\tlet previousStats, previousVersion: string;\n\tconst isValidConfigResult = validateConfigurationFile(flags.configuration);\n\tif (isValidConfigResult.exitMessage !== \"\") {\n\t\treturn {\n\t\t\t...result,\n\t\t\t...isValidConfigResult,\n\t\t};\n\t}\n\tconst configurationFile = isValidConfigResult.data;\n\tconst outputFile = getOutputFile(flags.output);\n\tresult.outputFile = outputFile;\n\n\tconst configuration: { report: ReportConfiguration } = await import(\n\t\tconfigurationFile\n\t).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 {\n\t\t\t...result,\n\t\t\t...currentStats,\n\t\t};\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 {\n\t\t\t\t...result,\n\t\t\t\t...previousStats,\n\t\t\t};\n\t\t}\n\t\tpreviousVersion = getMostRecentVersion(previousStats.data);\n\t} catch {\n\t\t// nothing to declare officer.\n\t}\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 = [];\n\trowsMD.push(addMDrow({ type: \"header\", columns }));\n\n\tfor (const key of Object.keys(currentStats.data[currentVersion])) {\n\t\tconst item = currentStats.data[currentVersion][key];\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\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\tconst previousFileSizeGzip = previousFileStats?.fileSizeGzip || 0;\n\t\t\tconst diff = item.fileSizeGzip - previousFileSizeGzip;\n\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), {\n\t\t\t\t\t\t\tunitSeparator: \" \",\n\t\t\t\t\t\t})} ${percentFormatter(diff / previousFileSizeGzip)})`;\n\t\t}\n\n\t\ttotalGzipSize += item.fileSizeGzip;\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\tconst template = `\n${headerString}\n${rowsMD.join(\"\\n\")}\n\n${footerFormatter({\n\tlimitReached,\n\toverallDiff,\n\ttotalGzipSize,\n})}\n`;\n\n\tif (limitReached) {\n\t\tresult.exitCode = 1;\n\t}\n\n\tresult.data = template;\n\n\treturn result;\n};\n"],"names":["basename","dirname","join","bytes","addMDrow","formatFooter","getMostRecentVersion","getOutputFile","percentFormatter","readJSONFile","validateConfigurationFile","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","push","type","key","Object","keys","item","name","passed","diffString","previousFileStats","previousFileSizeGzip","fileSizeGzip","diff","Math","abs","unitSeparator","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;AAkBxB,OAAO,MAAMC,cAAc,OAAO,EAAEC,KAAK,EAAE;IAC1C,MAAMC,SAAwB;QAC7BC,UAAU;QACVC,aAAa;QACbC,YAAY;QACZC,MAAM;IACP;IACA,IAAIC,eAAeC;IACnB,MAAMC,sBAAsBV,0BAA0BE,MAAMS,aAAa;IACzE,IAAID,oBAAoBL,WAAW,KAAK,IAAI;QAC3C,OAAO;YACN,GAAGF,MAAM;YACT,GAAGO,mBAAmB;QACvB;IACD;IACA,MAAME,oBAAoBF,oBAAoBH,IAAI;IAClD,MAAMD,aAAaT,cAAcK,MAAMW,MAAM;IAC7CV,OAAOG,UAAU,GAAGA;IAEpB,MAAMK,gBAAiD,MAAM,MAAM,CAClEC,mBACCE,IAAI,CAAC,CAACC,IAAMA,EAAEC,OAAO;IAEvB,MAAMC,eAAelB,aACpBP,KAAKD,QAAQqB,oBAAoBD,cAAcO,MAAM,CAACC,OAAO;IAE9D,IAAIF,aAAaZ,WAAW,KAAK,IAAI;QACpC,OAAO;YACN,GAAGF,MAAM;YACT,GAAGc,YAAY;QAChB;IACD;IAEA,IAAI;QACHT,gBAAgBT,aACfP,KAAKD,QAAQqB,oBAAoBD,cAAcO,MAAM,CAACE,QAAQ;QAE/D,IAAIZ,cAAcH,WAAW,KAAK,IAAI;YACrC,OAAO;gBACN,GAAGF,MAAM;gBACT,GAAGK,aAAa;YACjB;QACD;QACAC,kBAAkBb,qBAAqBY,cAAcD,IAAI;IAC1D,EAAE,OAAM;IACP,8BAA8B;IAC/B;IACA,MAAMc,iBAAiBzB,qBAAqBqB,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,IAAIjC;IACvD,MAAMkC,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,SAAS,EAAE;IACjBA,OAAOC,IAAI,CAACzC,SAAS;QAAE0C,MAAM;QAAUP;IAAQ;IAE/C,KAAK,MAAMQ,OAAOC,OAAOC,IAAI,CAACtB,aAAaV,IAAI,CAACc,eAAe,EAAG;QACjE,MAAMmB,OAAOvB,aAAaV,IAAI,CAACc,eAAe,CAACgB,IAAI;QACnD,MAAMI,OAAOnD,SAAS+C;QACtB,IAAI,CAACG,KAAKE,MAAM,EAAE;YACjBpB,eAAe;QAChB;QAEA,IAAIqB,aAAa;QACjB,IAAInC,iBAAiBC,iBAAiB;YACrC,MAAMmC,oBACLpC,cAAcD,IAAI,CAACE,gBAAgB,IACnCD,cAAcD,IAAI,CAACE,gBAAgB,CAAC4B,IAAI;YACzC,MAAMQ,uBAAuBD,mBAAmBE,gBAAgB;YAChE,MAAMC,OAAOP,KAAKM,YAAY,GAAGD;YAEjCtB,eAAewB;YACfJ,aACCI,SAAS,KAAKA,SAASP,KAAKM,YAAY,GACrC,KACA,CAAC,EAAE,EAAEC,OAAO,IAAI,MAAM,MAAMtD,MAAMuD,KAAKC,GAAG,CAACF,OAAO;gBAClDG,eAAe;YAChB,GAAG,CAAC,EAAEpD,iBAAiBiD,OAAOF,sBAAsB,CAAC,CAAC;QAC1D;QAEArB,iBAAiBgB,KAAKM,YAAY;QAElCZ,OAAOC,IAAI,CACVzC,SAAS;YACR0C,MAAM;YACNM,QAAQF,KAAKE,MAAM;YACnBD,MAAMA;YACNT,MAAMQ,KAAKM,YAAY;YACvBC,MAAMJ;YACNQ,OAAOX,KAAKW,KAAK;YACjBtB;QACD;IAEF;IAEA,MAAMuB,WAAW,CAAC;AACnB,EAAE3B,aAAa;AACf,EAAES,OAAO1C,IAAI,CAAC,MAAM;;AAEpB,EAAEmC,gBAAgB;QACjBL;QACAC;QACAC;IACD,GAAG;AACH,CAAC;IAEA,IAAIF,cAAc;QACjBnB,OAAOC,QAAQ,GAAG;IACnB;IAEAD,OAAOI,IAAI,GAAG6C;IAEd,OAAOjD;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.2.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",
@@ -41,9 +41,9 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@node-cli/comments": "0.2.5",
44
+ "@node-cli/comments": "0.2.6",
45
45
  "@vitest/coverage-v8": "3.2.4",
46
46
  "vitest": "3.2.4"
47
47
  },
48
- "gitHead": "cab44c95cf447b7c80af963e205be1d9459f4c42"
48
+ "gitHead": "ffc82a19b07c42aa655d4a15e8e1459e1530ecdc"
49
49
  }