@code-pushup/utils 0.39.0 → 0.42.1

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.
Files changed (32) hide show
  1. package/index.js +1042 -587
  2. package/package.json +5 -4
  3. package/src/index.d.ts +4 -1
  4. package/src/lib/git/git.commits-and-tags.d.ts +57 -0
  5. package/src/lib/{git.d.ts → git/git.d.ts} +0 -3
  6. package/src/lib/reports/constants.d.ts +26 -6
  7. package/src/lib/reports/formatting.d.ts +6 -0
  8. package/src/lib/reports/generate-md-report-categoy-section.d.ts +6 -0
  9. package/src/lib/reports/generate-md-report.d.ts +8 -0
  10. package/src/lib/reports/utils.d.ts +5 -3
  11. package/src/lib/semver.d.ts +3 -0
  12. package/src/lib/table.d.ts +6 -0
  13. package/src/lib/text-formats/constants.d.ts +3 -0
  14. package/src/lib/{reports/md → text-formats/html}/details.d.ts +2 -0
  15. package/src/lib/text-formats/html/font-style.d.ts +3 -0
  16. package/src/lib/text-formats/html/link.d.ts +1 -0
  17. package/src/lib/text-formats/html/table.d.ts +2 -0
  18. package/src/lib/text-formats/index.d.ts +44 -0
  19. package/src/lib/text-formats/md/font-style.d.ts +4 -0
  20. package/src/lib/{reports → text-formats}/md/headline.d.ts +1 -1
  21. package/src/lib/text-formats/md/link.d.ts +1 -0
  22. package/src/lib/{reports → text-formats}/md/list.d.ts +2 -0
  23. package/src/lib/text-formats/md/section.d.ts +2 -0
  24. package/src/lib/text-formats/md/table.d.ts +9 -0
  25. package/src/lib/text-formats/table.d.ts +6 -0
  26. package/src/lib/text-formats/types.d.ts +1 -0
  27. package/src/lib/reports/md/font-style.d.ts +0 -16
  28. package/src/lib/reports/md/index.d.ts +0 -8
  29. package/src/lib/reports/md/link.d.ts +0 -6
  30. package/src/lib/reports/md/table.d.ts +0 -10
  31. /package/src/lib/{reports → text-formats}/md/image.d.ts +0 -0
  32. /package/src/lib/{reports → text-formats}/md/paragraphs.d.ts +0 -0
package/index.js CHANGED
@@ -1,5 +1,372 @@
1
- // packages/models/src/lib/audit.ts
2
- import { z as z2 } from "zod";
1
+ // packages/utils/src/lib/text-formats/constants.ts
2
+ var NEW_LINE = "\n";
3
+ var TAB = " ";
4
+ var SPACE = " ";
5
+
6
+ // packages/utils/src/lib/text-formats/html/details.ts
7
+ function details(title, content, cfg = { open: false }) {
8
+ return `<details${cfg.open ? " open" : ""}>${NEW_LINE}<summary>${title}</summary>${NEW_LINE}${// ⚠️ The blank line is needed to ensure Markdown in content is rendered correctly.
9
+ NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
10
+ // ⚠️ The blank line ensure Markdown in content is rendered correctly.
11
+ NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
12
+ NEW_LINE}`;
13
+ }
14
+
15
+ // packages/utils/src/lib/text-formats/html/font-style.ts
16
+ var boldElement = "b";
17
+ function bold(text) {
18
+ return `<${boldElement}>${text}</${boldElement}>`;
19
+ }
20
+ var italicElement = "i";
21
+ function italic(text) {
22
+ return `<${italicElement}>${text}</${italicElement}>`;
23
+ }
24
+ var codeElement = "code";
25
+ function code(text) {
26
+ return `<${codeElement}>${text}</${codeElement}>`;
27
+ }
28
+
29
+ // packages/utils/src/lib/text-formats/html/link.ts
30
+ function link(href, text) {
31
+ return `<a href="${href}">${text || href}"</a>`;
32
+ }
33
+
34
+ // packages/utils/src/lib/transform.ts
35
+ import { platform } from "node:os";
36
+ function toArray(val) {
37
+ return Array.isArray(val) ? val : [val];
38
+ }
39
+ function objectToKeys(obj) {
40
+ return Object.keys(obj);
41
+ }
42
+ function objectToEntries(obj) {
43
+ return Object.entries(obj);
44
+ }
45
+ function objectFromEntries(entries) {
46
+ return Object.fromEntries(entries);
47
+ }
48
+ function countOccurrences(values) {
49
+ return values.reduce(
50
+ (acc, value) => ({ ...acc, [value]: (acc[value] ?? 0) + 1 }),
51
+ {}
52
+ );
53
+ }
54
+ function distinct(array) {
55
+ return [...new Set(array)];
56
+ }
57
+ function deepClone(obj) {
58
+ return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
59
+ }
60
+ function factorOf(items, filterFn) {
61
+ const itemCount = items.length;
62
+ if (!itemCount) {
63
+ return 1;
64
+ }
65
+ const filterCount = items.filter(filterFn).length;
66
+ return filterCount === 0 ? 1 : (itemCount - filterCount) / itemCount;
67
+ }
68
+ function objectToCliArgs(params) {
69
+ if (!params) {
70
+ return [];
71
+ }
72
+ return Object.entries(params).flatMap(([key, value]) => {
73
+ if (key === "_") {
74
+ return Array.isArray(value) ? value : [`${value}`];
75
+ }
76
+ const prefix = key.length === 1 ? "-" : "--";
77
+ if (Array.isArray(value)) {
78
+ return value.map((v) => `${prefix}${key}="${v}"`);
79
+ }
80
+ if (Array.isArray(value)) {
81
+ return value.map((v) => `${prefix}${key}="${v}"`);
82
+ }
83
+ if (typeof value === "string") {
84
+ return [`${prefix}${key}="${value}"`];
85
+ }
86
+ if (typeof value === "number") {
87
+ return [`${prefix}${key}=${value}`];
88
+ }
89
+ if (typeof value === "boolean") {
90
+ return [`${prefix}${value ? "" : "no-"}${key}`];
91
+ }
92
+ throw new Error(`Unsupported type ${typeof value} for key ${key}`);
93
+ });
94
+ }
95
+ function toUnixPath(path) {
96
+ return path.replace(/\\/g, "/");
97
+ }
98
+ function toUnixNewlines(text) {
99
+ return platform() === "win32" ? text.replace(/\r\n/g, "\n") : text;
100
+ }
101
+ function fromJsonLines(jsonLines) {
102
+ const unifiedNewLines = toUnixNewlines(jsonLines).trim();
103
+ return JSON.parse(`[${unifiedNewLines.split("\n").join(",")}]`);
104
+ }
105
+ function toJsonLines(json) {
106
+ return json.map((item) => JSON.stringify(item)).join("\n");
107
+ }
108
+ function capitalize(text) {
109
+ return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
110
+ 1
111
+ )}`;
112
+ }
113
+ function apostrophize(text, upperCase) {
114
+ const lastCharMatch = text.match(/(\w)\W*$/);
115
+ const lastChar = lastCharMatch?.[1] ?? "";
116
+ return `${text}'${lastChar.toLocaleLowerCase() === "s" ? "" : upperCase ? "S" : "s"}`;
117
+ }
118
+ function toNumberPrecision(value, decimalPlaces) {
119
+ return Number(
120
+ `${Math.round(
121
+ Number.parseFloat(`${value}e${decimalPlaces}`)
122
+ )}e-${decimalPlaces}`
123
+ );
124
+ }
125
+ function toOrdinal(value) {
126
+ if (value % 10 === 1 && value % 100 !== 11) {
127
+ return `${value}st`;
128
+ }
129
+ if (value % 10 === 2 && value % 100 !== 12) {
130
+ return `${value}nd`;
131
+ }
132
+ if (value % 10 === 3 && value % 100 !== 13) {
133
+ return `${value}rd`;
134
+ }
135
+ return `${value}th`;
136
+ }
137
+
138
+ // packages/utils/src/lib/table.ts
139
+ function rowToStringArray({ rows, columns = [] }) {
140
+ if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
141
+ throw new TypeError(
142
+ "Column can`t be object when rows are primitive values"
143
+ );
144
+ }
145
+ return rows.map((row) => {
146
+ if (Array.isArray(row)) {
147
+ return row.map(String);
148
+ }
149
+ const objectRow = row;
150
+ if (columns.length === 0 || typeof columns.at(0) === "string") {
151
+ return Object.values(objectRow).map(String);
152
+ }
153
+ return columns.map(
154
+ ({ key }) => String(objectRow[key])
155
+ );
156
+ });
157
+ }
158
+ function columnsToStringArray({ rows, columns = [] }) {
159
+ const firstRow = rows.at(0);
160
+ const primitiveRows = Array.isArray(firstRow);
161
+ if (typeof columns.at(0) === "string" && !primitiveRows) {
162
+ throw new Error("invalid union type. Caught by model parsing.");
163
+ }
164
+ if (columns.length === 0) {
165
+ if (Array.isArray(firstRow)) {
166
+ return firstRow.map((_, idx) => String(idx));
167
+ }
168
+ return Object.keys(firstRow);
169
+ }
170
+ if (typeof columns.at(0) === "string") {
171
+ return columns.map(String);
172
+ }
173
+ const cols = columns;
174
+ return cols.map(({ label, key }) => label ?? capitalize(key));
175
+ }
176
+ function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
177
+ const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
178
+ if (typeof column === "string") {
179
+ return column;
180
+ } else if (typeof column === "object") {
181
+ return column.align ?? "center";
182
+ } else {
183
+ return "center";
184
+ }
185
+ }
186
+ function getColumnAlignmentForIndex(targetIdx, columns = []) {
187
+ const column = columns.at(targetIdx);
188
+ if (column == null) {
189
+ return "center";
190
+ } else if (typeof column === "string") {
191
+ return column;
192
+ } else if (typeof column === "object") {
193
+ return column.align ?? "center";
194
+ } else {
195
+ return "center";
196
+ }
197
+ }
198
+ function getColumnAlignments({
199
+ rows,
200
+ columns = []
201
+ }) {
202
+ if (rows.at(0) == null) {
203
+ throw new Error("first row can`t be undefined.");
204
+ }
205
+ if (Array.isArray(rows.at(0))) {
206
+ const firstPrimitiveRow = rows.at(0);
207
+ return Array.from({ length: firstPrimitiveRow.length }).map(
208
+ (_, idx) => getColumnAlignmentForIndex(idx, columns)
209
+ );
210
+ }
211
+ const firstObject = rows.at(0);
212
+ return Object.keys(firstObject).map(
213
+ (key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
214
+ );
215
+ }
216
+
217
+ // packages/utils/src/lib/text-formats/html/table.ts
218
+ function wrap(elem, content) {
219
+ return `<${elem}>${content}</${elem}>${NEW_LINE}`;
220
+ }
221
+ function wrapRow(content) {
222
+ const elem = "tr";
223
+ return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
224
+ }
225
+ function table(tableData) {
226
+ if (tableData.rows.length === 0) {
227
+ throw new Error("Data can't be empty");
228
+ }
229
+ const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
230
+ const tableHeaderRow = wrapRow(tableHeaderCols);
231
+ const tableBody = rowToStringArray(tableData).map((arr) => {
232
+ const columns = arr.map((s) => wrap("td", s)).join("");
233
+ return wrapRow(columns);
234
+ }).join("");
235
+ return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
236
+ }
237
+
238
+ // packages/utils/src/lib/text-formats/md/font-style.ts
239
+ var boldWrap = "**";
240
+ function bold2(text) {
241
+ return `${boldWrap}${text}${boldWrap}`;
242
+ }
243
+ var italicWrap = "_";
244
+ function italic2(text) {
245
+ return `${italicWrap}${text}${italicWrap}`;
246
+ }
247
+ var strikeThroughWrap = "~";
248
+ function strikeThrough(text) {
249
+ return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
250
+ }
251
+ var codeWrap = "`";
252
+ function code2(text) {
253
+ return `${codeWrap}${text}${codeWrap}`;
254
+ }
255
+
256
+ // packages/utils/src/lib/text-formats/md/headline.ts
257
+ function headline(text, hierarchy = 1) {
258
+ return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
259
+ }
260
+ function h(text, hierarchy = 1) {
261
+ return headline(text, hierarchy);
262
+ }
263
+ function h1(text) {
264
+ return headline(text, 1);
265
+ }
266
+ function h2(text) {
267
+ return headline(text, 2);
268
+ }
269
+ function h3(text) {
270
+ return headline(text, 3);
271
+ }
272
+ function h4(text) {
273
+ return headline(text, 4);
274
+ }
275
+ function h5(text) {
276
+ return headline(text, 5);
277
+ }
278
+ function h6(text) {
279
+ return headline(text, 6);
280
+ }
281
+
282
+ // packages/utils/src/lib/text-formats/md/image.ts
283
+ function image(src, alt) {
284
+ return `![${alt}](${src})`;
285
+ }
286
+
287
+ // packages/utils/src/lib/text-formats/md/link.ts
288
+ function link2(href, text) {
289
+ return `[${text || href}](${href})`;
290
+ }
291
+
292
+ // packages/utils/src/lib/text-formats/md/list.ts
293
+ function li(text, order = "unordered") {
294
+ const style = order === "unordered" ? "-" : "- [ ]";
295
+ return `${style} ${text}`;
296
+ }
297
+ function indentation(text, level = 1) {
298
+ return `${TAB.repeat(level)}${text}`;
299
+ }
300
+
301
+ // packages/utils/src/lib/text-formats/md/paragraphs.ts
302
+ function paragraphs(...sections) {
303
+ return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
304
+ }
305
+
306
+ // packages/utils/src/lib/text-formats/md/section.ts
307
+ function section(...contents) {
308
+ return `${lines(...contents)}${NEW_LINE}`;
309
+ }
310
+ function lines(...contents) {
311
+ return `${contents.filter(Boolean).join(NEW_LINE)}`;
312
+ }
313
+
314
+ // packages/utils/src/lib/text-formats/md/table.ts
315
+ var alignString = /* @__PURE__ */ new Map([
316
+ ["left", ":--"],
317
+ ["center", ":--:"],
318
+ ["right", "--:"]
319
+ ]);
320
+ function tableRow(rows) {
321
+ return `|${rows.join("|")}|`;
322
+ }
323
+ function table2(data) {
324
+ if (data.rows.length === 0) {
325
+ throw new Error("Data can't be empty");
326
+ }
327
+ const alignmentRow = getColumnAlignments(data).map(
328
+ (s) => alignString.get(s) ?? String(alignString.get("center"))
329
+ );
330
+ return section(
331
+ `${lines(
332
+ tableRow(columnsToStringArray(data)),
333
+ tableRow(alignmentRow),
334
+ ...rowToStringArray(data).map(tableRow)
335
+ )}`
336
+ );
337
+ }
338
+
339
+ // packages/utils/src/lib/text-formats/index.ts
340
+ var md = {
341
+ bold: bold2,
342
+ italic: italic2,
343
+ strikeThrough,
344
+ code: code2,
345
+ link: link2,
346
+ image,
347
+ headline,
348
+ h,
349
+ h1,
350
+ h2,
351
+ h3,
352
+ h4,
353
+ h5,
354
+ h6,
355
+ indentation,
356
+ lines,
357
+ li,
358
+ section,
359
+ paragraphs,
360
+ table: table2
361
+ };
362
+ var html = {
363
+ bold,
364
+ italic,
365
+ code,
366
+ link,
367
+ details,
368
+ table
369
+ };
3
370
 
4
371
  // packages/models/src/lib/implementation/schemas.ts
5
372
  import { MATERIAL_ICONS } from "vscode-material-icons";
@@ -66,6 +433,7 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
66
433
  }
67
434
 
68
435
  // packages/models/src/lib/implementation/schemas.ts
436
+ var primitiveValueSchema = z.union([z.string(), z.number()]);
69
437
  function executionMetaSchema(options = {
70
438
  descriptionDate: "Execution start date and time",
71
439
  descriptionDuration: "Execution duration in ms"
@@ -157,6 +525,7 @@ function hasNonZeroWeightedRef(refs) {
157
525
  }
158
526
 
159
527
  // packages/models/src/lib/audit.ts
528
+ import { z as z2 } from "zod";
160
529
  var auditSchema = z2.object({
161
530
  slug: slugSchema.describe("ID (unique within plugin)")
162
531
  }).merge(
@@ -186,7 +555,7 @@ function getDuplicateSlugsInAudits(audits) {
186
555
  }
187
556
 
188
557
  // packages/models/src/lib/audit-output.ts
189
- import { z as z4 } from "zod";
558
+ import { z as z5 } from "zod";
190
559
 
191
560
  // packages/models/src/lib/issue.ts
192
561
  import { z as z3 } from "zod";
@@ -217,16 +586,61 @@ var issueSchema = z3.object(
217
586
  { description: "Issue information" }
218
587
  );
219
588
 
589
+ // packages/models/src/lib/table.ts
590
+ import { z as z4 } from "zod";
591
+ var tableAlignmentSchema = z4.enum(["left", "center", "right"], {
592
+ description: "Cell alignment"
593
+ });
594
+ var tableColumnObjectSchema = z4.object({
595
+ key: z4.string(),
596
+ label: z4.string().optional(),
597
+ align: tableAlignmentSchema.optional()
598
+ });
599
+ var tableRowObjectSchema = z4.record(primitiveValueSchema, {
600
+ description: "Object row"
601
+ });
602
+ var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
603
+ description: "Primitive row"
604
+ });
605
+ var tableSharedSchema = z4.object({
606
+ title: z4.string().optional().describe("Display title for table")
607
+ });
608
+ var tablePrimitiveSchema = tableSharedSchema.merge(
609
+ z4.object(
610
+ {
611
+ columns: z4.array(tableAlignmentSchema).optional(),
612
+ rows: z4.array(tableRowPrimitiveSchema)
613
+ },
614
+ { description: "Table with primitive rows and optional alignment columns" }
615
+ )
616
+ );
617
+ var tableObjectSchema = tableSharedSchema.merge(
618
+ z4.object(
619
+ {
620
+ columns: z4.union([
621
+ z4.array(tableAlignmentSchema),
622
+ z4.array(tableColumnObjectSchema)
623
+ ]).optional(),
624
+ rows: z4.array(tableRowObjectSchema)
625
+ },
626
+ {
627
+ description: "Table with object rows and optional alignment or object columns"
628
+ }
629
+ )
630
+ );
631
+ var tableSchema = (description = "Table information") => z4.union([tablePrimitiveSchema, tableObjectSchema], { description });
632
+
220
633
  // packages/models/src/lib/audit-output.ts
221
634
  var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
222
- var auditDisplayValueSchema = z4.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
223
- var auditDetailsSchema = z4.object(
635
+ var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
636
+ var auditDetailsSchema = z5.object(
224
637
  {
225
- issues: z4.array(issueSchema, { description: "List of findings" })
638
+ issues: z5.array(issueSchema, { description: "List of findings" }).optional(),
639
+ table: tableSchema("Table of related findings").optional()
226
640
  },
227
641
  { description: "Detailed information" }
228
642
  );
229
- var auditOutputSchema = z4.object(
643
+ var auditOutputSchema = z5.object(
230
644
  {
231
645
  slug: slugSchema.describe("Reference to audit"),
232
646
  displayValue: auditDisplayValueSchema,
@@ -236,7 +650,7 @@ var auditOutputSchema = z4.object(
236
650
  },
237
651
  { description: "Audit information" }
238
652
  );
239
- var auditOutputsSchema = z4.array(auditOutputSchema, {
653
+ var auditOutputsSchema = z5.array(auditOutputSchema, {
240
654
  description: "List of JSON formatted audit output emitted by the runner process of a plugin"
241
655
  }).refine(
242
656
  (audits) => !getDuplicateSlugsInAudits2(audits),
@@ -253,13 +667,13 @@ function getDuplicateSlugsInAudits2(audits) {
253
667
  }
254
668
 
255
669
  // packages/models/src/lib/category-config.ts
256
- import { z as z5 } from "zod";
670
+ import { z as z6 } from "zod";
257
671
  var categoryRefSchema = weightedRefSchema(
258
672
  "Weighted references to audits and/or groups for the category",
259
673
  "Slug of an audit or group (depending on `type`)"
260
674
  ).merge(
261
- z5.object({
262
- type: z5.enum(["audit", "group"], {
675
+ z6.object({
676
+ type: z6.enum(["audit", "group"], {
263
677
  description: "Discriminant for reference kind, affects where `slug` is looked up"
264
678
  }),
265
679
  plugin: slugSchema.describe(
@@ -280,8 +694,8 @@ var categoryConfigSchema = scorableSchema(
280
694
  description: "Meta info for category"
281
695
  })
282
696
  ).merge(
283
- z5.object({
284
- isBinary: z5.boolean({
697
+ z6.object({
698
+ isBinary: z6.boolean({
285
699
  description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
286
700
  }).optional()
287
701
  })
@@ -297,7 +711,7 @@ function getDuplicateRefsInCategoryMetrics(metrics) {
297
711
  metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
298
712
  );
299
713
  }
300
- var categoriesSchema = z5.array(categoryConfigSchema, {
714
+ var categoriesSchema = z6.array(categoryConfigSchema, {
301
715
  description: "Categorization of individual audits"
302
716
  }).refine(
303
717
  (categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
@@ -316,18 +730,18 @@ function getDuplicateSlugCategories(categories) {
316
730
  }
317
731
 
318
732
  // packages/models/src/lib/commit.ts
319
- import { z as z6 } from "zod";
320
- var commitSchema = z6.object(
733
+ import { z as z7 } from "zod";
734
+ var commitSchema = z7.object(
321
735
  {
322
- hash: z6.string({ description: "Commit SHA (full)" }).regex(
736
+ hash: z7.string({ description: "Commit SHA (full)" }).regex(
323
737
  /^[\da-f]{40}$/,
324
738
  "Commit SHA should be a 40-character hexadecimal string"
325
739
  ),
326
- message: z6.string({ description: "Commit message" }),
327
- date: z6.coerce.date({
740
+ message: z7.string({ description: "Commit message" }),
741
+ date: z7.coerce.date({
328
742
  description: "Date and time when commit was authored"
329
743
  }),
330
- author: z6.string({
744
+ author: z7.string({
331
745
  description: "Commit author name"
332
746
  }).trim()
333
747
  },
@@ -335,22 +749,22 @@ var commitSchema = z6.object(
335
749
  );
336
750
 
337
751
  // packages/models/src/lib/core-config.ts
338
- import { z as z12 } from "zod";
752
+ import { z as z13 } from "zod";
339
753
 
340
754
  // packages/models/src/lib/persist-config.ts
341
- import { z as z7 } from "zod";
342
- var formatSchema = z7.enum(["json", "md"]);
343
- var persistConfigSchema = z7.object({
755
+ import { z as z8 } from "zod";
756
+ var formatSchema = z8.enum(["json", "md"]);
757
+ var persistConfigSchema = z8.object({
344
758
  outputDir: filePathSchema.describe("Artifacts folder").optional(),
345
759
  filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
346
- format: z7.array(formatSchema).optional()
760
+ format: z8.array(formatSchema).optional()
347
761
  });
348
762
 
349
763
  // packages/models/src/lib/plugin-config.ts
350
- import { z as z10 } from "zod";
764
+ import { z as z11 } from "zod";
351
765
 
352
766
  // packages/models/src/lib/group.ts
353
- import { z as z8 } from "zod";
767
+ import { z as z9 } from "zod";
354
768
  var groupRefSchema = weightedRefSchema(
355
769
  "Weighted reference to a group",
356
770
  "Reference slug to a group within this plugin (e.g. 'max-lines')"
@@ -367,7 +781,7 @@ var groupSchema = scorableSchema(
367
781
  getDuplicateRefsInGroups,
368
782
  duplicateRefsInGroupsErrorMsg
369
783
  ).merge(groupMetaSchema);
370
- var groupsSchema = z8.array(groupSchema, {
784
+ var groupsSchema = z9.array(groupSchema, {
371
785
  description: "List of groups"
372
786
  }).optional().refine(
373
787
  (groups) => !getDuplicateSlugsInGroups(groups),
@@ -395,14 +809,14 @@ function getDuplicateSlugsInGroups(groups) {
395
809
  }
396
810
 
397
811
  // packages/models/src/lib/runner-config.ts
398
- import { z as z9 } from "zod";
399
- var outputTransformSchema = z9.function().args(z9.unknown()).returns(z9.union([auditOutputsSchema, z9.promise(auditOutputsSchema)]));
400
- var runnerConfigSchema = z9.object(
812
+ import { z as z10 } from "zod";
813
+ var outputTransformSchema = z10.function().args(z10.unknown()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
814
+ var runnerConfigSchema = z10.object(
401
815
  {
402
- command: z9.string({
816
+ command: z10.string({
403
817
  description: "Shell command to execute"
404
818
  }),
405
- args: z9.array(z9.string({ description: "Command arguments" })).optional(),
819
+ args: z10.array(z10.string({ description: "Command arguments" })).optional(),
406
820
  outputFile: filePathSchema.describe("Output path"),
407
821
  outputTransform: outputTransformSchema.optional()
408
822
  },
@@ -410,8 +824,8 @@ var runnerConfigSchema = z9.object(
410
824
  description: "How to execute runner"
411
825
  }
412
826
  );
413
- var onProgressSchema = z9.function().args(z9.unknown()).returns(z9.void());
414
- var runnerFunctionSchema = z9.function().args(onProgressSchema.optional()).returns(z9.union([auditOutputsSchema, z9.promise(auditOutputsSchema)]));
827
+ var onProgressSchema = z10.function().args(z10.unknown()).returns(z10.void());
828
+ var runnerFunctionSchema = z10.function().args(onProgressSchema.optional()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
415
829
 
416
830
  // packages/models/src/lib/plugin-config.ts
417
831
  var pluginMetaSchema = packageVersionSchema().merge(
@@ -422,13 +836,13 @@ var pluginMetaSchema = packageVersionSchema().merge(
422
836
  description: "Plugin metadata"
423
837
  })
424
838
  ).merge(
425
- z10.object({
839
+ z11.object({
426
840
  slug: slugSchema.describe("Unique plugin slug within core config"),
427
841
  icon: materialIconSchema
428
842
  })
429
843
  );
430
- var pluginDataSchema = z10.object({
431
- runner: z10.union([runnerConfigSchema, runnerFunctionSchema]),
844
+ var pluginDataSchema = z11.object({
845
+ runner: z11.union([runnerConfigSchema, runnerFunctionSchema]),
432
846
  audits: pluginAuditsSchema,
433
847
  groups: groupsSchema
434
848
  });
@@ -454,22 +868,22 @@ function getMissingRefsFromGroups(pluginCfg) {
454
868
  }
455
869
 
456
870
  // packages/models/src/lib/upload-config.ts
457
- import { z as z11 } from "zod";
458
- var uploadConfigSchema = z11.object({
871
+ import { z as z12 } from "zod";
872
+ var uploadConfigSchema = z12.object({
459
873
  server: urlSchema.describe("URL of deployed portal API"),
460
- apiKey: z11.string({
874
+ apiKey: z12.string({
461
875
  description: "API key with write access to portal (use `process.env` for security)"
462
876
  }),
463
877
  organization: slugSchema.describe(
464
878
  "Organization slug from Code PushUp portal"
465
879
  ),
466
880
  project: slugSchema.describe("Project slug from Code PushUp portal"),
467
- timeout: z11.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
881
+ timeout: z12.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
468
882
  });
469
883
 
470
884
  // packages/models/src/lib/core-config.ts
471
- var unrefinedCoreConfigSchema = z12.object({
472
- plugins: z12.array(pluginConfigSchema, {
885
+ var unrefinedCoreConfigSchema = z13.object({
886
+ plugins: z13.array(pluginConfigSchema, {
473
887
  description: "List of plugins to be used (official, community-provided, or custom)"
474
888
  }).min(1),
475
889
  /** portal configuration for persisting results */
@@ -492,7 +906,7 @@ function refineCoreConfig(schema) {
492
906
  }
493
907
 
494
908
  // packages/models/src/lib/report.ts
495
- import { z as z13 } from "zod";
909
+ import { z as z14 } from "zod";
496
910
  var auditReportSchema = auditSchema.merge(auditOutputSchema);
497
911
  var pluginReportSchema = pluginMetaSchema.merge(
498
912
  executionMetaSchema({
@@ -500,9 +914,9 @@ var pluginReportSchema = pluginMetaSchema.merge(
500
914
  descriptionDuration: "Duration of the plugin run in ms"
501
915
  })
502
916
  ).merge(
503
- z13.object({
504
- audits: z13.array(auditReportSchema).min(1),
505
- groups: z13.array(groupSchema).optional()
917
+ z14.object({
918
+ audits: z14.array(auditReportSchema).min(1),
919
+ groups: z14.array(groupSchema).optional()
506
920
  })
507
921
  ).refine(
508
922
  (pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
@@ -536,10 +950,10 @@ var reportSchema = packageVersionSchema({
536
950
  descriptionDuration: "Duration of the collect run in ms"
537
951
  })
538
952
  ).merge(
539
- z13.object(
953
+ z14.object(
540
954
  {
541
- categories: z13.array(categoryConfigSchema),
542
- plugins: z13.array(pluginReportSchema).min(1),
955
+ categories: z14.array(categoryConfigSchema),
956
+ plugins: z14.array(pluginReportSchema).min(1),
543
957
  commit: commitSchema.describe("Git commit for which report was collected").nullable()
544
958
  },
545
959
  { description: "Collect output data" }
@@ -555,40 +969,40 @@ var reportSchema = packageVersionSchema({
555
969
  );
556
970
 
557
971
  // packages/models/src/lib/reports-diff.ts
558
- import { z as z14 } from "zod";
972
+ import { z as z15 } from "zod";
559
973
  function makeComparisonSchema(schema) {
560
974
  const sharedDescription = schema.description || "Result";
561
- return z14.object({
975
+ return z15.object({
562
976
  before: schema.describe(`${sharedDescription} (source commit)`),
563
977
  after: schema.describe(`${sharedDescription} (target commit)`)
564
978
  });
565
979
  }
566
980
  function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
567
- return z14.object(
981
+ return z15.object(
568
982
  {
569
- changed: z14.array(diffSchema),
570
- unchanged: z14.array(resultSchema),
571
- added: z14.array(resultSchema),
572
- removed: z14.array(resultSchema)
983
+ changed: z15.array(diffSchema),
984
+ unchanged: z15.array(resultSchema),
985
+ added: z15.array(resultSchema),
986
+ removed: z15.array(resultSchema)
573
987
  },
574
988
  { description }
575
989
  );
576
990
  }
577
- var scorableMetaSchema = z14.object({
991
+ var scorableMetaSchema = z15.object({
578
992
  slug: slugSchema,
579
993
  title: titleSchema,
580
994
  docsUrl: docsUrlSchema
581
995
  });
582
996
  var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
583
- z14.object({
997
+ z15.object({
584
998
  plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
585
999
  })
586
1000
  );
587
1001
  var scorableDiffSchema = scorableMetaSchema.merge(
588
- z14.object({
1002
+ z15.object({
589
1003
  scores: makeComparisonSchema(scoreSchema).merge(
590
- z14.object({
591
- diff: z14.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
1004
+ z15.object({
1005
+ diff: z15.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
592
1006
  })
593
1007
  ).describe("Score comparison")
594
1008
  })
@@ -599,10 +1013,10 @@ var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
599
1013
  var categoryDiffSchema = scorableDiffSchema;
600
1014
  var groupDiffSchema = scorableWithPluginDiffSchema;
601
1015
  var auditDiffSchema = scorableWithPluginDiffSchema.merge(
602
- z14.object({
1016
+ z15.object({
603
1017
  values: makeComparisonSchema(auditValueSchema).merge(
604
- z14.object({
605
- diff: z14.number().int().describe("Value change (`values.after - values.before`)")
1018
+ z15.object({
1019
+ diff: z15.number().int().describe("Value change (`values.after - values.before`)")
606
1020
  })
607
1021
  ).describe("Audit `value` comparison"),
608
1022
  displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
@@ -611,15 +1025,15 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
611
1025
  })
612
1026
  );
613
1027
  var categoryResultSchema = scorableMetaSchema.merge(
614
- z14.object({ score: scoreSchema })
1028
+ z15.object({ score: scoreSchema })
615
1029
  );
616
1030
  var groupResultSchema = scorableWithPluginMetaSchema.merge(
617
- z14.object({ score: scoreSchema })
1031
+ z15.object({ score: scoreSchema })
618
1032
  );
619
1033
  var auditResultSchema = scorableWithPluginMetaSchema.merge(
620
1034
  auditOutputSchema.pick({ score: true, value: true, displayValue: true })
621
1035
  );
622
- var reportsDiffSchema = z14.object({
1036
+ var reportsDiffSchema = z15.object({
623
1037
  commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
624
1038
  categories: makeArraysComparisonSchema(
625
1039
  categoryDiffSchema,
@@ -782,40 +1196,48 @@ import chalk from "chalk";
782
1196
 
783
1197
  // packages/utils/src/lib/reports/constants.ts
784
1198
  var TERMINAL_WIDTH = 80;
785
- var NEW_LINE = "\n";
786
1199
  var SCORE_COLOR_RANGE = {
787
1200
  GREEN_MIN: 0.9,
788
1201
  YELLOW_MIN: 0.5
789
1202
  };
1203
+ var CATEGORIES_TITLE = "\u{1F3F7} Categories";
790
1204
  var FOOTER_PREFIX = "Made with \u2764 by";
791
1205
  var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
792
- var README_LINK = "https://github.com/flowup/quality-metrics-cli#readme";
1206
+ var README_LINK = "https://github.com/code-pushup/cli#readme";
793
1207
  var reportHeadlineText = "Code PushUp Report";
794
1208
  var reportOverviewTableHeaders = [
795
- "\u{1F3F7} Category",
796
- "\u2B50 Score",
797
- "\u{1F6E1} Audits"
1209
+ {
1210
+ key: "category",
1211
+ label: "\u{1F3F7} Category",
1212
+ align: "left"
1213
+ },
1214
+ {
1215
+ key: "score",
1216
+ label: "\u2B50 Score"
1217
+ },
1218
+ {
1219
+ key: "audits",
1220
+ label: "\u{1F6E1} Audits"
1221
+ }
798
1222
  ];
799
1223
  var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
800
- var reportMetaTableHeaders = [
801
- "Commit",
802
- "Version",
803
- "Duration",
804
- "Plugins",
805
- "Categories",
806
- "Audits"
807
- ];
808
- var pluginMetaTableHeaders = [
809
- "Plugin",
810
- "Audits",
811
- "Version",
812
- "Duration"
813
- ];
814
- var detailsTableHeaders = [
815
- "Severity",
816
- "Message",
817
- "Source file",
818
- "Line(s)"
1224
+ var issuesTableHeadings = [
1225
+ {
1226
+ key: "severity",
1227
+ label: "Severity"
1228
+ },
1229
+ {
1230
+ key: "message",
1231
+ label: "Message"
1232
+ },
1233
+ {
1234
+ key: "file",
1235
+ label: "Source file"
1236
+ },
1237
+ {
1238
+ key: "line",
1239
+ label: "Line(s)"
1240
+ }
819
1241
  ];
820
1242
 
821
1243
  // packages/utils/src/lib/logging.ts
@@ -841,7 +1263,7 @@ function logListItem(args) {
841
1263
  singletonisaacUi.rows = [];
842
1264
  singletonUiInstance?.logger.log(content);
843
1265
  }
844
- function link(text) {
1266
+ function link3(text) {
845
1267
  return chalk.underline(chalk.blueBright(text));
846
1268
  }
847
1269
 
@@ -973,127 +1395,40 @@ async function crawlFileSystem(options) {
973
1395
  return resultsNestedArray.flat();
974
1396
  }
975
1397
  function findLineNumberInText(content, pattern) {
976
- const lines = content.split(/\r?\n/);
977
- const lineNumber = lines.findIndex((line) => line.includes(pattern)) + 1;
1398
+ const lines6 = content.split(/\r?\n/);
1399
+ const lineNumber = lines6.findIndex((line) => line.includes(pattern)) + 1;
978
1400
  return lineNumber === 0 ? null : lineNumber;
979
1401
  }
980
1402
 
981
- // packages/utils/src/lib/reports/md/details.ts
982
- function details(title, content, cfg = { open: false }) {
983
- return `<details${cfg.open ? " open" : ""}>
984
- <summary>${title}</summary>
985
-
986
- ${content}
987
-
988
- </details>
989
- `;
990
- }
991
-
992
- // packages/utils/src/lib/reports/md/font-style.ts
993
- var stylesMap = {
994
- i: "_",
995
- // italic
996
- b: "**",
997
- // bold
998
- s: "~",
999
- // strike through
1000
- c: "`"
1001
- // code
1002
- };
1003
- function style(text, styles = ["b"]) {
1004
- return styles.reduce((t, s) => `${stylesMap[s]}${t}${stylesMap[s]}`, text);
1005
- }
1006
-
1007
- // packages/utils/src/lib/reports/md/headline.ts
1008
- function headline(text, hierarchy = 1) {
1009
- return `${"#".repeat(hierarchy)} ${text}`;
1010
- }
1011
- function h1(text) {
1012
- return headline(text, 1);
1013
- }
1014
- function h2(text) {
1015
- return headline(text, 2);
1016
- }
1017
- function h3(text) {
1018
- return headline(text, 3);
1019
- }
1020
-
1021
- // packages/utils/src/lib/reports/md/image.ts
1022
- function image(src, alt) {
1023
- return `![${alt}](${src})`;
1024
- }
1025
-
1026
- // packages/utils/src/lib/reports/md/link.ts
1027
- function link2(href, text) {
1028
- return `[${text || href}](${href})`;
1029
- }
1030
-
1031
- // packages/utils/src/lib/reports/md/list.ts
1032
- function li(text, order = "unordered") {
1033
- const style2 = order === "unordered" ? "-" : "- [ ]";
1034
- return `${style2} ${text}`;
1035
- }
1036
-
1037
- // packages/utils/src/lib/reports/md/paragraphs.ts
1038
- function paragraphs(...sections) {
1039
- return sections.filter(Boolean).join("\n\n");
1040
- }
1041
-
1042
- // packages/utils/src/lib/reports/md/table.ts
1043
- var alignString = /* @__PURE__ */ new Map([
1044
- ["l", ":--"],
1045
- ["c", ":--:"],
1046
- ["r", "--:"]
1047
- ]);
1048
- function tableMd(data, align) {
1049
- if (data.length === 0) {
1050
- throw new Error("Data can't be empty");
1051
- }
1052
- const alignmentSetting = align ?? data[0]?.map(() => "c");
1053
- const tableContent = data.map((arr) => `|${arr.join("|")}|`);
1054
- const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
1055
- return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
1056
- }
1057
- function tableHtml(data) {
1058
- if (data.length === 0) {
1059
- throw new Error("Data can't be empty");
1060
- }
1061
- const tableContent = data.map((arr, index) => {
1062
- if (index === 0) {
1063
- const headerRow = arr.map((s) => `<th>${s}</th>`).join("");
1064
- return `<tr>${headerRow}</tr>`;
1065
- }
1066
- const row = arr.map((s) => `<td>${s}</td>`).join("");
1067
- return `<tr>${row}</tr>`;
1068
- });
1069
- return `<table>${tableContent.join("")}</table>`;
1070
- }
1071
-
1072
- // packages/utils/src/lib/reports/utils.ts
1073
- function formatReportScore(score) {
1074
- return Math.round(score * 100).toString();
1403
+ // packages/utils/src/lib/reports/utils.ts
1404
+ var { image: image2, bold: boldMd } = md;
1405
+ function formatReportScore(score) {
1406
+ return Math.round(score * 100).toString();
1075
1407
  }
1076
1408
  function formatScoreWithColor(score, options) {
1077
- const styledNumber = options?.skipBold ? formatReportScore(score) : style(formatReportScore(score));
1078
- return `${getRoundScoreMarker(score)} ${styledNumber}`;
1079
- }
1080
- function getRoundScoreMarker(score) {
1081
- if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
1082
- return "\u{1F7E2}";
1083
- }
1084
- if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
1085
- return "\u{1F7E1}";
1409
+ const styledNumber = options?.skipBold ? formatReportScore(score) : boldMd(formatReportScore(score));
1410
+ return `${scoreMarker(score)} ${styledNumber}`;
1411
+ }
1412
+ var MARKERS = {
1413
+ circle: {
1414
+ red: "\u{1F534}",
1415
+ yellow: "\u{1F7E1}",
1416
+ green: "\u{1F7E2}"
1417
+ },
1418
+ square: {
1419
+ red: "\u{1F7E5}",
1420
+ yellow: "\u{1F7E8}",
1421
+ green: "\u{1F7E9}"
1086
1422
  }
1087
- return "\u{1F534}";
1088
- }
1089
- function getSquaredScoreMarker(score) {
1423
+ };
1424
+ function scoreMarker(score, markerType = "circle") {
1090
1425
  if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
1091
- return "\u{1F7E9}";
1426
+ return MARKERS[markerType].green;
1092
1427
  }
1093
1428
  if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
1094
- return "\u{1F7E8}";
1429
+ return MARKERS[markerType].yellow;
1095
1430
  }
1096
- return "\u{1F7E5}";
1431
+ return MARKERS[markerType].red;
1097
1432
  }
1098
1433
  function getDiffMarker(diff) {
1099
1434
  if (diff > 0) {
@@ -1109,7 +1444,7 @@ function colorByScoreDiff(text, diff) {
1109
1444
  return shieldsBadge(text, color);
1110
1445
  }
1111
1446
  function shieldsBadge(text, color) {
1112
- return image(
1447
+ return image2(
1113
1448
  `https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
1114
1449
  text
1115
1450
  );
@@ -1119,7 +1454,7 @@ function formatDiffNumber(diff) {
1119
1454
  const sign = diff < 0 ? "\u2212" : "+";
1120
1455
  return `${sign}${number}`;
1121
1456
  }
1122
- function getSeverityIcon(severity) {
1457
+ function severityMarker(severity) {
1123
1458
  if (severity === "error") {
1124
1459
  return "\u{1F6A8}";
1125
1460
  }
@@ -1310,13 +1645,13 @@ function executeProcess(cfg) {
1310
1645
  process2.on("error", (err) => {
1311
1646
  stderr += err.toString();
1312
1647
  });
1313
- process2.on("close", (code) => {
1648
+ process2.on("close", (code3) => {
1314
1649
  const timings = { date, duration: calcDuration(start) };
1315
- if (code === 0 || ignoreExitCode) {
1650
+ if (code3 === 0 || ignoreExitCode) {
1316
1651
  onComplete?.();
1317
- resolve({ code, stdout, stderr, ...timings });
1652
+ resolve({ code: code3, stdout, stderr, ...timings });
1318
1653
  } else {
1319
- const errorMsg = new ProcessError({ code, stdout, stderr, ...timings });
1654
+ const errorMsg = new ProcessError({ code: code3, stdout, stderr, ...timings });
1320
1655
  onError?.(errorMsg);
1321
1656
  reject(errorMsg);
1322
1657
  }
@@ -1332,123 +1667,9 @@ function filterItemRefsBy(items, refFilterFn) {
1332
1667
  })).filter((item) => item.refs.length);
1333
1668
  }
1334
1669
 
1335
- // packages/utils/src/lib/git.ts
1670
+ // packages/utils/src/lib/git/git.ts
1336
1671
  import { isAbsolute, join as join3, relative } from "node:path";
1337
1672
  import { simpleGit } from "simple-git";
1338
-
1339
- // packages/utils/src/lib/transform.ts
1340
- import { platform } from "node:os";
1341
- function toArray(val) {
1342
- return Array.isArray(val) ? val : [val];
1343
- }
1344
- function objectToKeys(obj) {
1345
- return Object.keys(obj);
1346
- }
1347
- function objectToEntries(obj) {
1348
- return Object.entries(obj);
1349
- }
1350
- function objectFromEntries(entries) {
1351
- return Object.fromEntries(entries);
1352
- }
1353
- function countOccurrences(values) {
1354
- return values.reduce(
1355
- (acc, value) => ({ ...acc, [value]: (acc[value] ?? 0) + 1 }),
1356
- {}
1357
- );
1358
- }
1359
- function distinct(array) {
1360
- return [...new Set(array)];
1361
- }
1362
- function deepClone(obj) {
1363
- return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
1364
- }
1365
- function factorOf(items, filterFn) {
1366
- const itemCount = items.length;
1367
- if (!itemCount) {
1368
- return 1;
1369
- }
1370
- const filterCount = items.filter(filterFn).length;
1371
- return filterCount === 0 ? 1 : (itemCount - filterCount) / itemCount;
1372
- }
1373
- function objectToCliArgs(params) {
1374
- if (!params) {
1375
- return [];
1376
- }
1377
- return Object.entries(params).flatMap(([key, value]) => {
1378
- if (key === "_") {
1379
- return Array.isArray(value) ? value : [`${value}`];
1380
- }
1381
- const prefix = key.length === 1 ? "-" : "--";
1382
- if (Array.isArray(value)) {
1383
- return value.map((v) => `${prefix}${key}="${v}"`);
1384
- }
1385
- if (Array.isArray(value)) {
1386
- return value.map((v) => `${prefix}${key}="${v}"`);
1387
- }
1388
- if (typeof value === "string") {
1389
- return [`${prefix}${key}="${value}"`];
1390
- }
1391
- if (typeof value === "number") {
1392
- return [`${prefix}${key}=${value}`];
1393
- }
1394
- if (typeof value === "boolean") {
1395
- return [`${prefix}${value ? "" : "no-"}${key}`];
1396
- }
1397
- throw new Error(`Unsupported type ${typeof value} for key ${key}`);
1398
- });
1399
- }
1400
- function toUnixPath(path) {
1401
- return path.replace(/\\/g, "/");
1402
- }
1403
- function toUnixNewlines(text) {
1404
- return platform() === "win32" ? text.replace(/\r\n/g, "\n") : text;
1405
- }
1406
- function fromJsonLines(jsonLines) {
1407
- const unifiedNewLines = toUnixNewlines(jsonLines).trim();
1408
- return JSON.parse(`[${unifiedNewLines.split("\n").join(",")}]`);
1409
- }
1410
- function toJsonLines(json) {
1411
- return json.map((item) => JSON.stringify(item)).join("\n");
1412
- }
1413
- function capitalize(text) {
1414
- return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
1415
- 1
1416
- )}`;
1417
- }
1418
- function apostrophize(text, upperCase) {
1419
- const lastCharMatch = text.match(/(\w)\W*$/);
1420
- const lastChar = lastCharMatch?.[1] ?? "";
1421
- return `${text}'${lastChar.toLocaleLowerCase() === "s" ? "" : upperCase ? "S" : "s"}`;
1422
- }
1423
- function toNumberPrecision(value, decimalPlaces) {
1424
- return Number(
1425
- `${Math.round(
1426
- Number.parseFloat(`${value}e${decimalPlaces}`)
1427
- )}e-${decimalPlaces}`
1428
- );
1429
- }
1430
- function toOrdinal(value) {
1431
- if (value % 10 === 1 && value % 100 !== 11) {
1432
- return `${value}st`;
1433
- }
1434
- if (value % 10 === 2 && value % 100 !== 12) {
1435
- return `${value}nd`;
1436
- }
1437
- if (value % 10 === 3 && value % 100 !== 13) {
1438
- return `${value}rd`;
1439
- }
1440
- return `${value}th`;
1441
- }
1442
-
1443
- // packages/utils/src/lib/git.ts
1444
- async function getLatestCommit(git = simpleGit()) {
1445
- const log2 = await git.log({
1446
- maxCount: 1,
1447
- // git log -1 --pretty=format:"%H %s %an %aI" - See: https://git-scm.com/docs/pretty-formats
1448
- format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
1449
- });
1450
- return commitSchema.parse(log2.latest);
1451
- }
1452
1673
  function getGitRoot(git = simpleGit()) {
1453
1674
  return git.revparse("--show-toplevel");
1454
1675
  }
@@ -1499,11 +1720,6 @@ async function guardAgainstLocalChanges(git = simpleGit()) {
1499
1720
  throw new GitStatusError(status);
1500
1721
  }
1501
1722
  }
1502
- async function getCurrentBranchOrTag(git = simpleGit()) {
1503
- return await git.branch().then((r) => r.current) || // If no current branch, try to get the tag
1504
- // @TODO use simple git
1505
- await git.raw(["describe", "--tags", "--exact-match"]).then((out) => out.trim());
1506
- }
1507
1723
  async function safeCheckout(branchOrHash, forceCleanStatus = false, git = simpleGit()) {
1508
1724
  if (forceCleanStatus) {
1509
1725
  await git.raw(["reset", "--hard"]);
@@ -1514,6 +1730,120 @@ async function safeCheckout(branchOrHash, forceCleanStatus = false, git = simple
1514
1730
  await git.checkout(branchOrHash);
1515
1731
  }
1516
1732
 
1733
+ // packages/utils/src/lib/git/git.commits-and-tags.ts
1734
+ import { simpleGit as simpleGit2 } from "simple-git";
1735
+
1736
+ // packages/utils/src/lib/semver.ts
1737
+ import { rcompare, valid } from "semver";
1738
+ function normalizeSemver(semverString) {
1739
+ if (semverString.startsWith("v") || semverString.startsWith("V")) {
1740
+ return semverString.slice(1);
1741
+ }
1742
+ if (semverString.includes("@")) {
1743
+ return semverString.split("@").at(-1) ?? "";
1744
+ }
1745
+ return semverString;
1746
+ }
1747
+ function isSemver(semverString = "") {
1748
+ return valid(normalizeSemver(semverString)) != null;
1749
+ }
1750
+ function sortSemvers(semverStrings) {
1751
+ return semverStrings.map(normalizeSemver).filter(isSemver).sort(rcompare);
1752
+ }
1753
+
1754
+ // packages/utils/src/lib/git/git.commits-and-tags.ts
1755
+ async function getLatestCommit(git = simpleGit2()) {
1756
+ const log2 = await git.log({
1757
+ maxCount: 1,
1758
+ // git log -1 --pretty=format:"%H %s %an %aI" - See: https://git-scm.com/docs/pretty-formats
1759
+ format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
1760
+ });
1761
+ return commitSchema.parse(log2.latest);
1762
+ }
1763
+ async function getCurrentBranchOrTag(git = simpleGit2()) {
1764
+ return await git.branch().then((r) => r.current) || // If no current branch, try to get the tag
1765
+ // @TODO use simple git
1766
+ await git.raw(["describe", "--tags", "--exact-match"]).then((out) => out.trim());
1767
+ }
1768
+ function validateFilter({ from, to }) {
1769
+ if (to && !from) {
1770
+ throw new Error(
1771
+ `filter needs the "from" option defined to accept the "to" option.
1772
+ `
1773
+ );
1774
+ }
1775
+ }
1776
+ function filterLogs(allTags, opt) {
1777
+ if (!opt) {
1778
+ return allTags;
1779
+ }
1780
+ validateFilter(opt);
1781
+ const { from, to, maxCount } = opt;
1782
+ const finIndex = (tagName, fallback) => {
1783
+ const idx = allTags.indexOf(tagName ?? "");
1784
+ if (idx > -1) {
1785
+ return idx;
1786
+ }
1787
+ return fallback;
1788
+ };
1789
+ const fromIndex = finIndex(from, 0);
1790
+ const toIndex = finIndex(to, void 0);
1791
+ return allTags.slice(fromIndex, toIndex ? toIndex + 1 : toIndex).slice(0, maxCount ?? void 0);
1792
+ }
1793
+ async function getHashFromTag(tag, git = simpleGit2()) {
1794
+ const tagDetails = await git.show(["--no-patch", "--format=%H", tag]);
1795
+ const hash = tagDetails.trim();
1796
+ return {
1797
+ hash: hash.split("\n").at(-1) ?? "",
1798
+ message: tag
1799
+ };
1800
+ }
1801
+ async function getSemverTags(opt = {}, git = simpleGit2()) {
1802
+ validateFilter(opt);
1803
+ const { targetBranch, ...options } = opt;
1804
+ let currentBranch;
1805
+ if (targetBranch) {
1806
+ currentBranch = await getCurrentBranchOrTag(git);
1807
+ await git.checkout(targetBranch);
1808
+ }
1809
+ const tagsRaw = await git.tag([
1810
+ "--merged",
1811
+ targetBranch ?? await getCurrentBranchOrTag(git)
1812
+ ]);
1813
+ const allTags = tagsRaw.split(/\n/).map((tag) => tag.trim()).filter(Boolean).filter(isSemver);
1814
+ const relevantTags = filterLogs(allTags, options);
1815
+ const tagsWithHashes = await Promise.all(
1816
+ relevantTags.map((tag) => getHashFromTag(tag, git))
1817
+ );
1818
+ if (currentBranch) {
1819
+ await git.checkout(currentBranch);
1820
+ }
1821
+ return tagsWithHashes;
1822
+ }
1823
+ async function getHashes(options = {}, git = simpleGit2()) {
1824
+ const { targetBranch, from, to, maxCount, ...opt } = options;
1825
+ validateFilter({ from, to });
1826
+ let currentBranch;
1827
+ if (targetBranch) {
1828
+ currentBranch = await getCurrentBranchOrTag(git);
1829
+ await git.checkout(targetBranch);
1830
+ }
1831
+ const logs = await git.log({
1832
+ ...opt,
1833
+ format: {
1834
+ hash: "%H",
1835
+ message: "%s"
1836
+ },
1837
+ from,
1838
+ to,
1839
+ maxCount
1840
+ });
1841
+ if (targetBranch) {
1842
+ await git.checkout(currentBranch);
1843
+ }
1844
+ return [...logs.all];
1845
+ }
1846
+
1517
1847
  // packages/utils/src/lib/group-by-status.ts
1518
1848
  function groupByStatus(results) {
1519
1849
  return results.reduce(
@@ -1589,44 +1919,65 @@ function listAuditsFromAllPlugins(report) {
1589
1919
  );
1590
1920
  }
1591
1921
 
1592
- // packages/utils/src/lib/reports/generate-md-report.ts
1593
- function generateMdReport(report) {
1594
- const printCategories = report.categories.length > 0;
1595
- return (
1596
- // header section
1597
- // eslint-disable-next-line prefer-template
1598
- reportToHeaderSection() + NEW_LINE + // categories overview section
1599
- (printCategories ? reportToOverviewSection(report) + NEW_LINE + NEW_LINE : "") + // categories section
1600
- (printCategories ? reportToCategoriesSection(report) + NEW_LINE + NEW_LINE : "") + // audits section
1601
- reportToAuditsSection(report) + NEW_LINE + NEW_LINE + // about section
1602
- reportToAboutSection(report) + NEW_LINE + NEW_LINE + // footer section
1603
- `${FOOTER_PREFIX} ${link2(README_LINK, "Code PushUp")}`
1922
+ // packages/utils/src/lib/reports/formatting.ts
1923
+ var { headline: headline2, lines: lines2, link: link4, section: section2, table: table3 } = md;
1924
+ function tableSection(tableData, options) {
1925
+ if (tableData.rows.length === 0) {
1926
+ return "";
1927
+ }
1928
+ const { level = 4 } = options ?? {};
1929
+ const render = (h7, l) => l === 0 ? h7 : headline2(h7, l);
1930
+ return lines2(
1931
+ tableData.title && render(tableData.title, level),
1932
+ table3(tableData)
1604
1933
  );
1605
1934
  }
1606
- function reportToHeaderSection() {
1607
- return headline(reportHeadlineText) + NEW_LINE;
1935
+ function metaDescription({
1936
+ docsUrl,
1937
+ description
1938
+ }) {
1939
+ if (docsUrl) {
1940
+ const docsLink = link4(docsUrl, "\u{1F4D6} Docs");
1941
+ if (!description) {
1942
+ return section2(docsLink);
1943
+ }
1944
+ const parsedDescription = description.toString().endsWith("```") ? `${description}${NEW_LINE + NEW_LINE}` : `${description}${SPACE}`;
1945
+ return section2(`${parsedDescription}${docsLink}`);
1946
+ }
1947
+ if (description && description.trim().length > 0) {
1948
+ return section2(description);
1949
+ }
1950
+ return "";
1608
1951
  }
1609
- function reportToOverviewSection(report) {
1952
+
1953
+ // packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
1954
+ var { link: link5, section: section3, h2: h22, lines: lines3, li: li2, bold: boldMd2, h3: h32, indentation: indentation2 } = md;
1955
+ function categoriesOverviewSection(report) {
1610
1956
  const { categories, plugins } = report;
1611
- const tableContent = [
1612
- reportOverviewTableHeaders,
1613
- ...categories.map(({ title, refs, score }) => [
1614
- link2(`#${slugify(title)}`, title),
1615
- `${getRoundScoreMarker(score)} ${style(formatReportScore(score))}`,
1616
- countCategoryAudits(refs, plugins).toString()
1617
- ])
1618
- ];
1619
- return tableMd(tableContent, ["l", "c", "c"]);
1957
+ if (categories.length > 0 && plugins.length > 0) {
1958
+ const tableContent = {
1959
+ columns: reportOverviewTableHeaders,
1960
+ rows: categories.map(({ title, refs, score }) => ({
1961
+ // The heading "ID" is inferred from the heading text in Markdown.
1962
+ category: link5(`#${slugify(title)}`, title),
1963
+ score: `${scoreMarker(score)}${SPACE}${boldMd2(
1964
+ formatReportScore(score)
1965
+ )}`,
1966
+ audits: countCategoryAudits(refs, plugins).toString()
1967
+ }))
1968
+ };
1969
+ return tableSection(tableContent);
1970
+ }
1971
+ return "";
1620
1972
  }
1621
- function reportToCategoriesSection(report) {
1973
+ function categoriesDetailsSection(report) {
1622
1974
  const { categories, plugins } = report;
1623
- const categoryDetails = categories.reduce((acc, category) => {
1624
- const categoryTitle = h3(category.title);
1625
- const categoryScore = `${getRoundScoreMarker(
1975
+ const categoryDetails = categories.flatMap((category) => {
1976
+ const categoryTitle = h32(category.title);
1977
+ const categoryScore = `${scoreMarker(
1626
1978
  category.score
1627
- )} Score: ${style(formatReportScore(category.score))}`;
1628
- const categoryDocs = getDocsAndDescription(category);
1629
- const categoryMDItems = category.refs.reduce((refAcc, ref) => {
1979
+ )}${SPACE}Score: ${boldMd2(formatReportScore(category.score))}`;
1980
+ const categoryMDItems = category.refs.map((ref) => {
1630
1981
  if (ref.type === "group") {
1631
1982
  const group = getSortableGroupByRef(ref, plugins);
1632
1983
  const groupAudits = group.refs.map(
@@ -1636,151 +1987,240 @@ function reportToCategoriesSection(report) {
1636
1987
  )
1637
1988
  );
1638
1989
  const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
1639
- const mdGroupItem = groupItemToCategorySection(
1640
- group,
1641
- groupAudits,
1642
- pluginTitle
1643
- );
1644
- return refAcc + mdGroupItem + NEW_LINE;
1990
+ return categoryGroupItem(group, groupAudits, pluginTitle);
1645
1991
  } else {
1646
1992
  const audit = getSortableAuditByRef(ref, plugins);
1647
1993
  const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
1648
- const mdAuditItem = auditItemToCategorySection(audit, pluginTitle);
1649
- return refAcc + mdAuditItem + NEW_LINE;
1994
+ return categoryRef(audit, pluginTitle);
1650
1995
  }
1651
- }, "");
1652
- return acc + NEW_LINE + categoryTitle + NEW_LINE + NEW_LINE + categoryDocs + categoryScore + NEW_LINE + categoryMDItems;
1653
- }, "");
1654
- return h2("\u{1F3F7} Categories") + NEW_LINE + categoryDetails;
1655
- }
1656
- function auditItemToCategorySection(audit, pluginTitle) {
1657
- const auditTitle = link2(
1658
- `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1659
- audit.title
1996
+ });
1997
+ return section3(
1998
+ categoryTitle,
1999
+ metaDescription(category),
2000
+ categoryScore,
2001
+ ...categoryMDItems
2002
+ );
2003
+ });
2004
+ return lines3(h22(CATEGORIES_TITLE), ...categoryDetails);
2005
+ }
2006
+ function categoryRef({ title, score, value, displayValue }, pluginTitle) {
2007
+ const auditTitleAsLink = link5(
2008
+ `#${slugify(title)}-${slugify(pluginTitle)}`,
2009
+ title
1660
2010
  );
1661
- return li(
1662
- `${getSquaredScoreMarker(
1663
- audit.score
1664
- )} ${auditTitle} (_${pluginTitle}_) - ${getAuditResult(audit)}`
2011
+ const marker = scoreMarker(score, "square");
2012
+ return li2(
2013
+ `${marker}${SPACE}${auditTitleAsLink}${SPACE}(_${pluginTitle}_) - ${boldMd2(
2014
+ (displayValue || value).toString()
2015
+ )}`
1665
2016
  );
1666
2017
  }
1667
- function groupItemToCategorySection(group, groupAudits, pluginTitle) {
1668
- const groupScore = Number(formatReportScore(group.score || 0));
1669
- const groupTitle = li(
1670
- `${getRoundScoreMarker(groupScore)} ${group.title} (_${pluginTitle}_)`
2018
+ function categoryGroupItem({ score = 0, title }, groupAudits, pluginTitle) {
2019
+ const groupTitle = li2(
2020
+ `${scoreMarker(score)}${SPACE}${title}${SPACE}(_${pluginTitle}_)`
1671
2021
  );
1672
- const auditTitles = groupAudits.reduce((acc, audit) => {
1673
- const auditTitle = link2(
1674
- `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1675
- audit.title
1676
- );
1677
- return `${acc} ${li(
1678
- `${getSquaredScoreMarker(audit.score)} ${auditTitle} - ${getAuditResult(
1679
- audit
1680
- )}`
1681
- )}${NEW_LINE}`;
1682
- }, "");
1683
- return groupTitle + NEW_LINE + auditTitles;
1684
- }
1685
- function reportToAuditsSection(report) {
1686
- const auditsSection = report.plugins.reduce((pluginAcc, plugin) => {
1687
- const auditsData = plugin.audits.reduce((auditAcc, audit) => {
1688
- const auditTitle = `${audit.title} (${getPluginNameFromSlug(
1689
- plugin.slug,
1690
- report.plugins
1691
- )})`;
1692
- return auditAcc + h3(auditTitle) + NEW_LINE + NEW_LINE + reportToDetailsSection(audit) + NEW_LINE + NEW_LINE + getDocsAndDescription(audit);
1693
- }, "");
1694
- return pluginAcc + auditsData;
1695
- }, "");
1696
- return h2("\u{1F6E1}\uFE0F Audits") + NEW_LINE + NEW_LINE + auditsSection;
1697
- }
1698
- function reportToDetailsSection(audit) {
1699
- const detailsTitle = `${getSquaredScoreMarker(audit.score)} ${getAuditResult(
1700
- audit,
1701
- true
1702
- )} (score: ${formatReportScore(audit.score)})`;
1703
- if (!audit.details?.issues.length) {
1704
- return detailsTitle;
1705
- }
1706
- const detailsTableData = [
1707
- detailsTableHeaders,
1708
- ...audit.details.issues.map((issue) => {
1709
- const severity = `${getSeverityIcon(issue.severity)} <i>${issue.severity}</i>`;
1710
- const message = issue.message;
1711
- if (!issue.source) {
1712
- return [severity, message, "", ""];
1713
- }
1714
- const file = `<code>${issue.source.file}</code>`;
1715
- if (!issue.source.position) {
1716
- return [severity, message, file, ""];
1717
- }
1718
- const { startLine, endLine } = issue.source.position;
1719
- const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
1720
- return [severity, message, file, line];
1721
- })
1722
- ];
1723
- const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
1724
- return details(detailsTitle, detailsTable);
1725
- }
1726
- function reportToAboutSection(report) {
1727
- const date = formatDate(/* @__PURE__ */ new Date());
1728
- const { duration, version, commit, plugins, categories } = report;
1729
- const commitInfo = commit ? `${commit.message} (${commit.hash})` : "N/A";
1730
- const reportMetaTable = [
1731
- reportMetaTableHeaders,
1732
- [
1733
- commitInfo,
1734
- style(version || "", ["c"]),
1735
- formatDuration(duration),
1736
- plugins.length.toString(),
1737
- categories.length.toString(),
1738
- plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
1739
- ]
1740
- ];
1741
- const pluginMetaTable = [
1742
- pluginMetaTableHeaders,
1743
- ...plugins.map((plugin) => [
1744
- plugin.title,
1745
- plugin.audits.length.toString(),
1746
- style(plugin.version || "", ["c"]),
1747
- formatDuration(plugin.duration)
1748
- ])
1749
- ];
1750
- return (
1751
- // eslint-disable-next-line prefer-template
1752
- h2("About") + NEW_LINE + NEW_LINE + `Report was created by [Code PushUp](${README_LINK}) on ${date}.` + NEW_LINE + NEW_LINE + tableMd(reportMetaTable, ["l", "c", "c", "c", "c", "c"]) + NEW_LINE + NEW_LINE + "The following plugins were run:" + NEW_LINE + NEW_LINE + tableMd(pluginMetaTable, ["l", "c", "c", "c"])
2022
+ const auditTitles = groupAudits.map(
2023
+ ({ title: auditTitle, score: auditScore, value, displayValue }) => {
2024
+ const auditTitleLink = link5(
2025
+ `#${slugify(auditTitle)}-${slugify(pluginTitle)}`,
2026
+ auditTitle
2027
+ );
2028
+ const marker = scoreMarker(auditScore, "square");
2029
+ return indentation2(
2030
+ li2(
2031
+ `${marker}${SPACE}${auditTitleLink} - ${boldMd2(
2032
+ String(displayValue ?? value)
2033
+ )}`
2034
+ )
2035
+ );
2036
+ }
1753
2037
  );
2038
+ return lines3(groupTitle, ...auditTitles);
1754
2039
  }
1755
- function getDocsAndDescription({
1756
- docsUrl,
1757
- description
2040
+
2041
+ // packages/utils/src/lib/reports/generate-md-report.ts
2042
+ var { h1: h12, h2: h23, h3: h33, lines: lines4, link: link6, section: section4, code: codeMd } = md;
2043
+ var { bold: boldHtml, details: details2 } = html;
2044
+ function auditDetailsAuditValue({
2045
+ score,
2046
+ value,
2047
+ displayValue
1758
2048
  }) {
1759
- if (docsUrl) {
1760
- const docsLink = link2(docsUrl, "\u{1F4D6} Docs");
1761
- if (!description) {
1762
- return docsLink + NEW_LINE + NEW_LINE;
1763
- }
1764
- if (description.endsWith("```")) {
1765
- return description + NEW_LINE + NEW_LINE + docsLink + NEW_LINE + NEW_LINE;
1766
- }
1767
- return `${description} ${docsLink}${NEW_LINE}${NEW_LINE}`;
1768
- }
1769
- if (description) {
1770
- return description + NEW_LINE + NEW_LINE;
2049
+ return `${scoreMarker(score, "square")} ${boldHtml(
2050
+ String(displayValue ?? value)
2051
+ )} (score: ${formatReportScore(score)})`;
2052
+ }
2053
+ function generateMdReport(report) {
2054
+ const printCategories = report.categories.length > 0;
2055
+ return lines4(
2056
+ h12(reportHeadlineText),
2057
+ printCategories ? categoriesOverviewSection(report) : "",
2058
+ printCategories ? categoriesDetailsSection(report) : "",
2059
+ auditsSection(report),
2060
+ aboutSection(report),
2061
+ `${FOOTER_PREFIX}${SPACE}${link6(README_LINK, "Code PushUp")}`
2062
+ );
2063
+ }
2064
+ function auditDetailsIssues(issues = []) {
2065
+ if (issues.length === 0) {
2066
+ return "";
1771
2067
  }
1772
- return "";
2068
+ const detailsTableData = {
2069
+ title: "Issues",
2070
+ columns: issuesTableHeadings,
2071
+ rows: issues.map(
2072
+ ({ severity: severityVal, message, source: sourceVal }) => {
2073
+ const severity = `${severityMarker(severityVal)} <i>${severityVal}</i>`;
2074
+ if (!sourceVal) {
2075
+ return { severity, message, file: "", line: "" };
2076
+ }
2077
+ const file = `<code>${sourceVal.file}</code>`;
2078
+ if (!sourceVal.position) {
2079
+ return { severity, message, file, line: "" };
2080
+ }
2081
+ const { startLine, endLine } = sourceVal.position;
2082
+ const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
2083
+ return { severity, message, file, line };
2084
+ }
2085
+ )
2086
+ };
2087
+ return tableSection(detailsTableData);
2088
+ }
2089
+ function auditDetails(audit) {
2090
+ const { table: table5, issues = [] } = audit.details ?? {};
2091
+ const detailsValue = auditDetailsAuditValue(audit);
2092
+ if (issues.length === 0 && table5 == null) {
2093
+ return section4(detailsValue);
2094
+ }
2095
+ const tableSectionContent = table5 == null ? "" : tableSection(table5);
2096
+ const issuesSectionContent = issues.length > 0 ? auditDetailsIssues(issues) : "";
2097
+ return details2(
2098
+ detailsValue,
2099
+ lines4(tableSectionContent, issuesSectionContent)
2100
+ );
1773
2101
  }
1774
- function getAuditResult(audit, isHtml = false) {
1775
- const { displayValue, value } = audit;
1776
- return isHtml ? `<b>${displayValue || value}</b>` : style(String(displayValue || value));
2102
+ function auditsSection({
2103
+ plugins
2104
+ }) {
2105
+ const content = plugins.flatMap(
2106
+ ({ slug, audits }) => audits.flatMap((audit) => {
2107
+ const auditTitle = `${audit.title}${SPACE}(${getPluginNameFromSlug(
2108
+ slug,
2109
+ plugins
2110
+ )})`;
2111
+ const detailsContent = auditDetails(audit);
2112
+ const descriptionContent = metaDescription(audit);
2113
+ return [h33(auditTitle), detailsContent, descriptionContent];
2114
+ })
2115
+ );
2116
+ return section4(h23("\u{1F6E1}\uFE0F Audits"), ...content);
2117
+ }
2118
+ function aboutSection(report) {
2119
+ const { date, plugins } = report;
2120
+ const reportMetaTable = reportMetaData(report);
2121
+ const pluginMetaTable = reportPluginMeta({ plugins });
2122
+ return lines4(
2123
+ h23("About"),
2124
+ section4(
2125
+ `Report was created by [Code PushUp](${README_LINK}) on ${formatDate(
2126
+ new Date(date)
2127
+ )}.`
2128
+ ),
2129
+ tableSection(pluginMetaTable),
2130
+ tableSection(reportMetaTable)
2131
+ );
2132
+ }
2133
+ function reportPluginMeta({ plugins }) {
2134
+ return {
2135
+ columns: [
2136
+ {
2137
+ key: "plugin",
2138
+ align: "left"
2139
+ },
2140
+ {
2141
+ key: "audits"
2142
+ },
2143
+ {
2144
+ key: "version"
2145
+ },
2146
+ {
2147
+ key: "duration"
2148
+ }
2149
+ ],
2150
+ rows: plugins.map(
2151
+ ({
2152
+ title: pluginTitle,
2153
+ audits,
2154
+ version: pluginVersion,
2155
+ duration: pluginDuration
2156
+ }) => ({
2157
+ plugin: pluginTitle,
2158
+ audits: audits.length.toString(),
2159
+ version: codeMd(pluginVersion || ""),
2160
+ duration: formatDuration(pluginDuration)
2161
+ })
2162
+ )
2163
+ };
2164
+ }
2165
+ function reportMetaData({
2166
+ commit,
2167
+ version,
2168
+ duration,
2169
+ plugins,
2170
+ categories
2171
+ }) {
2172
+ const commitInfo = commit ? `${commit.message}${SPACE}(${commit.hash})` : "N/A";
2173
+ return {
2174
+ columns: [
2175
+ {
2176
+ key: "commit",
2177
+ align: "left"
2178
+ },
2179
+ {
2180
+ key: "version"
2181
+ },
2182
+ {
2183
+ key: "duration"
2184
+ },
2185
+ {
2186
+ key: "plugins"
2187
+ },
2188
+ {
2189
+ key: "categories"
2190
+ },
2191
+ {
2192
+ key: "audits"
2193
+ }
2194
+ ],
2195
+ rows: [
2196
+ {
2197
+ commit: commitInfo,
2198
+ version: codeMd(version || ""),
2199
+ duration: formatDuration(duration),
2200
+ plugins: plugins.length,
2201
+ categories: categories.length,
2202
+ audits: plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
2203
+ }
2204
+ ]
2205
+ };
1777
2206
  }
1778
2207
 
1779
2208
  // packages/utils/src/lib/reports/generate-md-reports-diff.ts
2209
+ var {
2210
+ h1: h13,
2211
+ h2: h24,
2212
+ lines: lines5,
2213
+ link: link7,
2214
+ bold: boldMd3,
2215
+ italic: italicMd,
2216
+ table: table4,
2217
+ section: section5
2218
+ } = md;
2219
+ var { details: details3 } = html;
1780
2220
  var MAX_ROWS = 100;
1781
2221
  function generateMdReportsDiff(diff) {
1782
- return paragraphs(
1783
- formatDiffHeaderSection(diff),
2222
+ return lines5(
2223
+ section5(formatDiffHeaderSection(diff)),
1784
2224
  formatDiffCategoriesSection(diff),
1785
2225
  formatDiffGroupsSection(diff),
1786
2226
  formatDiffAuditsSection(diff)
@@ -1788,12 +2228,12 @@ function generateMdReportsDiff(diff) {
1788
2228
  }
1789
2229
  function formatDiffHeaderSection(diff) {
1790
2230
  const outcomeTexts = {
1791
- positive: `\u{1F973} Code PushUp report has ${style("improved")}`,
1792
- negative: `\u{1F61F} Code PushUp report has ${style("regressed")}`,
1793
- mixed: `\u{1F928} Code PushUp report has both ${style(
2231
+ positive: `\u{1F973} Code PushUp report has ${boldMd3("improved")}`,
2232
+ negative: `\u{1F61F} Code PushUp report has ${boldMd3("regressed")}`,
2233
+ mixed: `\u{1F928} Code PushUp report has both ${boldMd3(
1794
2234
  "improvements and regressions"
1795
2235
  )}`,
1796
- unchanged: `\u{1F610} Code PushUp report is ${style("unchanged")}`
2236
+ unchanged: `\u{1F610} Code PushUp report is ${boldMd3("unchanged")}`
1797
2237
  };
1798
2238
  const outcome = mergeDiffOutcomes(
1799
2239
  changesToDiffOutcomes([
@@ -1803,8 +2243,8 @@ function formatDiffHeaderSection(diff) {
1803
2243
  ])
1804
2244
  );
1805
2245
  const styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
1806
- return paragraphs(
1807
- h1("Code PushUp"),
2246
+ return lines5(
2247
+ h13("Code PushUp"),
1808
2248
  diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
1809
2249
  );
1810
2250
  }
@@ -1815,102 +2255,104 @@ function formatDiffCategoriesSection(diff) {
1815
2255
  if (categoriesCount === 0) {
1816
2256
  return "";
1817
2257
  }
1818
- return paragraphs(
1819
- h2("\u{1F3F7}\uFE0F Categories"),
1820
- categoriesCount > 0 && tableMd(
1821
- [
1822
- [
1823
- "\u{1F3F7}\uFE0F Category",
1824
- hasChanges ? "\u2B50 Current score" : "\u2B50 Score",
1825
- "\u2B50 Previous score",
1826
- "\u{1F504} Score change"
1827
- ],
1828
- ...sortChanges(changed).map((category) => [
1829
- formatTitle(category),
1830
- formatScoreWithColor(category.scores.after),
1831
- formatScoreWithColor(category.scores.before, { skipBold: true }),
1832
- formatScoreChange(category.scores.diff)
1833
- ]),
1834
- ...added.map((category) => [
1835
- formatTitle(category),
1836
- formatScoreWithColor(category.score),
1837
- style("n/a (\\*)", ["i"]),
1838
- style("n/a (\\*)", ["i"])
1839
- ]),
1840
- ...unchanged.map((category) => [
1841
- formatTitle(category),
1842
- formatScoreWithColor(category.score),
1843
- formatScoreWithColor(category.score, { skipBold: true }),
1844
- "\u2013"
1845
- ])
1846
- ].map((row) => hasChanges ? row : row.slice(0, 2)),
1847
- hasChanges ? ["l", "c", "c", "c"] : ["l", "c"]
1848
- ),
1849
- added.length > 0 && style("(\\*) New category.", ["i"])
2258
+ const columns = [
2259
+ { key: "category", label: "\u{1F3F7}\uFE0F Category", align: "left" },
2260
+ { key: "after", label: hasChanges ? "\u2B50 Current score" : "\u2B50 Score" },
2261
+ { key: "before", label: "\u2B50 Previous score" },
2262
+ { key: "change", label: "\u{1F504} Score change" }
2263
+ ];
2264
+ return lines5(
2265
+ h24("\u{1F3F7}\uFE0F Categories"),
2266
+ categoriesCount > 0 && table4({
2267
+ columns: hasChanges ? columns : columns.slice(0, 2),
2268
+ rows: [
2269
+ ...sortChanges(changed).map((category) => ({
2270
+ category: formatTitle(category),
2271
+ after: formatScoreWithColor(category.scores.after),
2272
+ before: formatScoreWithColor(category.scores.before, {
2273
+ skipBold: true
2274
+ }),
2275
+ change: formatScoreChange(category.scores.diff)
2276
+ })),
2277
+ ...added.map((category) => ({
2278
+ category: formatTitle(category),
2279
+ after: formatScoreWithColor(category.score),
2280
+ before: italicMd("n/a (\\*)"),
2281
+ change: italicMd("n/a (\\*)")
2282
+ })),
2283
+ ...unchanged.map((category) => ({
2284
+ category: formatTitle(category),
2285
+ after: formatScoreWithColor(category.score),
2286
+ before: formatScoreWithColor(category.score, { skipBold: true }),
2287
+ change: "\u2013"
2288
+ }))
2289
+ ].map(
2290
+ (row) => hasChanges ? row : { category: row.category, after: row.after }
2291
+ )
2292
+ }),
2293
+ added.length > 0 && section5(italicMd("(\\*) New category."))
1850
2294
  );
1851
2295
  }
1852
2296
  function formatDiffGroupsSection(diff) {
1853
2297
  if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
1854
2298
  return "";
1855
2299
  }
1856
- return paragraphs(
1857
- h2("\u{1F5C3}\uFE0F Groups"),
2300
+ return lines5(
2301
+ h24("\u{1F5C3}\uFE0F Groups"),
1858
2302
  formatGroupsOrAuditsDetails("group", diff.groups, {
1859
- headings: [
1860
- "\u{1F50C} Plugin",
1861
- "\u{1F5C3}\uFE0F Group",
1862
- "\u2B50 Current score",
1863
- "\u2B50 Previous score",
1864
- "\u{1F504} Score change"
2303
+ columns: [
2304
+ { key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
2305
+ { key: "group", label: "\u{1F5C3}\uFE0F Group", align: "left" },
2306
+ { key: "after", label: "\u2B50 Current score" },
2307
+ { key: "before", label: "\u2B50 Previous score" },
2308
+ { key: "change", label: "\u{1F504} Score change" }
1865
2309
  ],
1866
- rows: sortChanges(diff.groups.changed).map((group) => [
1867
- formatTitle(group.plugin),
1868
- formatTitle(group),
1869
- formatScoreWithColor(group.scores.after),
1870
- formatScoreWithColor(group.scores.before, { skipBold: true }),
1871
- formatScoreChange(group.scores.diff)
1872
- ]),
1873
- align: ["l", "l", "c", "c", "c"]
2310
+ rows: sortChanges(diff.groups.changed).map((group) => ({
2311
+ plugin: formatTitle(group.plugin),
2312
+ group: formatTitle(group),
2313
+ after: formatScoreWithColor(group.scores.after),
2314
+ before: formatScoreWithColor(group.scores.before, { skipBold: true }),
2315
+ change: formatScoreChange(group.scores.diff)
2316
+ }))
1874
2317
  })
1875
2318
  );
1876
2319
  }
1877
2320
  function formatDiffAuditsSection(diff) {
1878
- return paragraphs(
1879
- h2("\u{1F6E1}\uFE0F Audits"),
2321
+ return lines5(
2322
+ h24("\u{1F6E1}\uFE0F Audits"),
1880
2323
  formatGroupsOrAuditsDetails("audit", diff.audits, {
1881
- headings: [
1882
- "\u{1F50C} Plugin",
1883
- "\u{1F6E1}\uFE0F Audit",
1884
- "\u{1F4CF} Current value",
1885
- "\u{1F4CF} Previous value",
1886
- "\u{1F504} Value change"
2324
+ columns: [
2325
+ { key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
2326
+ { key: "audit", label: "\u{1F6E1}\uFE0F Audit", align: "left" },
2327
+ { key: "after", label: "\u{1F4CF} Current value" },
2328
+ { key: "before", label: "\u{1F4CF} Previous value" },
2329
+ { key: "change", label: "\u{1F504} Value change" }
1887
2330
  ],
1888
- rows: sortChanges(diff.audits.changed).map((audit) => [
1889
- formatTitle(audit.plugin),
1890
- formatTitle(audit),
1891
- `${getSquaredScoreMarker(audit.scores.after)} ${style(
2331
+ rows: sortChanges(diff.audits.changed).map((audit) => ({
2332
+ plugin: formatTitle(audit.plugin),
2333
+ audit: formatTitle(audit),
2334
+ after: `${scoreMarker(audit.scores.after, "square")} ${boldMd3(
1892
2335
  audit.displayValues.after || audit.values.after.toString()
1893
2336
  )}`,
1894
- `${getSquaredScoreMarker(audit.scores.before)} ${audit.displayValues.before || audit.values.before.toString()}`,
1895
- formatValueChange(audit)
1896
- ]),
1897
- align: ["l", "l", "c", "c", "c"]
2337
+ before: `${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
2338
+ change: formatValueChange(audit)
2339
+ }))
1898
2340
  })
1899
2341
  );
1900
2342
  }
1901
- function formatGroupsOrAuditsDetails(token, { changed, unchanged }, table) {
1902
- return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details(
2343
+ function formatGroupsOrAuditsDetails(token, { changed, unchanged }, tableData) {
2344
+ return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details3(
1903
2345
  summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
1904
- paragraphs(
1905
- tableMd(
1906
- [table.headings, ...table.rows.slice(0, MAX_ROWS)],
1907
- table.align
1908
- ),
1909
- changed.length > MAX_ROWS && style(
2346
+ lines5(
2347
+ table4({
2348
+ ...tableData,
2349
+ rows: tableData.rows.slice(0, MAX_ROWS)
2350
+ // use never to avoid typing problem
2351
+ }),
2352
+ changed.length > MAX_ROWS && italicMd(
1910
2353
  `Only the ${MAX_ROWS} most affected ${pluralize(
1911
2354
  token
1912
- )} are listed above for brevity.`,
1913
- ["i"]
2355
+ )} are listed above for brevity.`
1914
2356
  ),
1915
2357
  unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
1916
2358
  )
@@ -1931,11 +2373,13 @@ function formatValueChange({
1931
2373
  return colorByScoreDiff(`${marker} ${text}`, scores.diff);
1932
2374
  }
1933
2375
  function summarizeUnchanged(token, { changed, unchanged }) {
1934
- return [
1935
- changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
1936
- unchanged.length === 1 ? "is" : "are",
1937
- "unchanged."
1938
- ].join(" ");
2376
+ return section5(
2377
+ [
2378
+ changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
2379
+ unchanged.length === 1 ? "is" : "are",
2380
+ "unchanged."
2381
+ ].join(" ")
2382
+ );
1939
2383
  }
1940
2384
  function summarizeDiffOutcomes(outcomes, token) {
1941
2385
  return objectToEntries(countDiffOutcomes(outcomes)).filter(
@@ -1960,7 +2404,7 @@ function formatTitle({
1960
2404
  docsUrl
1961
2405
  }) {
1962
2406
  if (docsUrl) {
1963
- return link2(docsUrl, title);
2407
+ return link7(docsUrl, title);
1964
2408
  }
1965
2409
  return title;
1966
2410
  }
@@ -2011,7 +2455,7 @@ function log(msg = "") {
2011
2455
  }
2012
2456
  function logStdoutSummary(report) {
2013
2457
  const printCategories = report.categories.length > 0;
2014
- log(reportToHeaderSection2(report));
2458
+ log(reportToHeaderSection(report));
2015
2459
  log();
2016
2460
  logPlugins(report);
2017
2461
  if (printCategories) {
@@ -2020,7 +2464,7 @@ function logStdoutSummary(report) {
2020
2464
  log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
2021
2465
  log();
2022
2466
  }
2023
- function reportToHeaderSection2(report) {
2467
+ function reportToHeaderSection(report) {
2024
2468
  const { packageName, version } = report;
2025
2469
  return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version}`;
2026
2470
  }
@@ -2060,16 +2504,16 @@ function logCategories({ categories, plugins }) {
2060
2504
  applyScoreColor({ score }),
2061
2505
  countCategoryAudits(refs, plugins)
2062
2506
  ]);
2063
- const table = ui().table();
2064
- table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
2065
- table.head(
2507
+ const table5 = ui().table();
2508
+ table5.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
2509
+ table5.head(
2066
2510
  reportRawOverviewTableHeaders.map((heading, idx) => ({
2067
2511
  content: chalk4.cyan(heading),
2068
2512
  hAlign: hAlign(idx)
2069
2513
  }))
2070
2514
  );
2071
2515
  rows.forEach(
2072
- (row) => table.row(
2516
+ (row) => table5.row(
2073
2517
  row.map((content, idx) => ({
2074
2518
  content: content.toString(),
2075
2519
  hAlign: hAlign(idx)
@@ -2078,19 +2522,19 @@ function logCategories({ categories, plugins }) {
2078
2522
  );
2079
2523
  log(chalk4.magentaBright.bold("Categories"));
2080
2524
  log();
2081
- table.render();
2525
+ table5.render();
2082
2526
  log();
2083
2527
  }
2084
2528
  function applyScoreColor({ score, text }) {
2085
2529
  const formattedScore = text ?? formatReportScore(score);
2086
- const style2 = text ? chalk4 : chalk4.bold;
2530
+ const style = text ? chalk4 : chalk4.bold;
2087
2531
  if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
2088
- return style2.green(formattedScore);
2532
+ return style.green(formattedScore);
2089
2533
  }
2090
2534
  if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
2091
- return style2.yellow(formattedScore);
2535
+ return style.yellow(formattedScore);
2092
2536
  }
2093
- return style2.red(formattedScore);
2537
+ return style.red(formattedScore);
2094
2538
  }
2095
2539
 
2096
2540
  // packages/utils/src/lib/reports/scoring.ts
@@ -2252,8 +2696,11 @@ var verboseUtils = (verbose = false) => ({
2252
2696
  export {
2253
2697
  CODE_PUSHUP_DOMAIN,
2254
2698
  FOOTER_PREFIX,
2699
+ NEW_LINE,
2255
2700
  ProcessError,
2256
2701
  README_LINK,
2702
+ SPACE,
2703
+ TAB,
2257
2704
  TERMINAL_WIDTH,
2258
2705
  apostrophize,
2259
2706
  calcDuration,
@@ -2279,14 +2726,19 @@ export {
2279
2726
  generateMdReportsDiff,
2280
2727
  getCurrentBranchOrTag,
2281
2728
  getGitRoot,
2729
+ getHashFromTag,
2730
+ getHashes,
2282
2731
  getLatestCommit,
2283
2732
  getProgressBar,
2733
+ getSemverTags,
2284
2734
  groupByStatus,
2285
2735
  guardAgainstLocalChanges,
2736
+ html,
2286
2737
  importEsmModule,
2287
2738
  isPromiseFulfilledResult,
2288
2739
  isPromiseRejectedResult,
2289
- link,
2740
+ isSemver,
2741
+ link3 as link,
2290
2742
  listAuditsFromAllPlugins,
2291
2743
  listGroupsFromAllPlugins,
2292
2744
  loadReport,
@@ -2294,6 +2746,8 @@ export {
2294
2746
  logMultipleResults,
2295
2747
  logStdoutSummary,
2296
2748
  matchArrayItemsByKey,
2749
+ md,
2750
+ normalizeSemver,
2297
2751
  objectFromEntries,
2298
2752
  objectToCliArgs,
2299
2753
  objectToEntries,
@@ -2308,6 +2762,7 @@ export {
2308
2762
  scoreReport,
2309
2763
  slugify,
2310
2764
  sortReport,
2765
+ sortSemvers,
2311
2766
  toArray,
2312
2767
  toGitPath,
2313
2768
  toJsonLines,