@code-pushup/utils 0.18.1 → 0.20.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/index.js CHANGED
@@ -75,25 +75,15 @@ function executionMetaSchema(options = {
75
75
  duration: z.number({ description: options.descriptionDuration })
76
76
  });
77
77
  }
78
- function slugSchema(description = "Unique ID (human-readable, URL-safe)") {
79
- return z.string({ description }).regex(slugRegex, {
80
- message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
81
- }).max(MAX_SLUG_LENGTH, {
82
- message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
83
- });
84
- }
85
- function descriptionSchema(description = "Description (markdown)") {
86
- return z.string({ description }).max(MAX_DESCRIPTION_LENGTH).optional();
87
- }
88
- function docsUrlSchema(description = "Documentation site") {
89
- return urlSchema(description).optional().or(z.string().max(0));
90
- }
91
- function urlSchema(description) {
92
- return z.string({ description }).url();
93
- }
94
- function titleSchema(description = "Descriptive name") {
95
- return z.string({ description }).max(MAX_TITLE_LENGTH);
96
- }
78
+ var slugSchema = z.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
79
+ message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
80
+ }).max(MAX_SLUG_LENGTH, {
81
+ message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
82
+ });
83
+ var descriptionSchema = z.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
84
+ var urlSchema = z.string().url();
85
+ var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
86
+ var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
97
87
  function metaSchema(options) {
98
88
  const {
99
89
  descriptionDescription,
@@ -103,24 +93,19 @@ function metaSchema(options) {
103
93
  } = options ?? {};
104
94
  return z.object(
105
95
  {
106
- title: titleSchema(titleDescription),
107
- description: descriptionSchema(descriptionDescription),
108
- docsUrl: docsUrlSchema(docsUrlDescription)
96
+ title: titleDescription ? titleSchema.describe(titleDescription) : titleSchema,
97
+ description: descriptionDescription ? descriptionSchema.describe(descriptionDescription) : descriptionSchema,
98
+ docsUrl: docsUrlDescription ? docsUrlSchema.describe(docsUrlDescription) : docsUrlSchema
109
99
  },
110
100
  { description }
111
101
  );
112
102
  }
113
- function filePathSchema(description) {
114
- return z.string({ description }).trim().min(1, { message: "path is invalid" });
115
- }
116
- function fileNameSchema(description) {
117
- return z.string({ description }).trim().regex(filenameRegex, {
118
- message: `The filename has to be valid`
119
- }).min(1, { message: "file name is invalid" });
120
- }
121
- function positiveIntSchema(description) {
122
- return z.number({ description }).int().nonnegative();
123
- }
103
+ var filePathSchema = z.string().trim().min(1, { message: "path is invalid" });
104
+ var fileNameSchema = z.string().trim().regex(filenameRegex, {
105
+ message: `The filename has to be valid`
106
+ }).min(1, { message: "file name is invalid" });
107
+ var positiveIntSchema = z.number().int().positive();
108
+ var nonnegativeIntSchema = z.number().int().nonnegative();
124
109
  function packageVersionSchema(options) {
125
110
  const { versionDescription = "NPM version of the package", required } = options ?? {};
126
111
  const packageSchema = z.string({ description: "NPM package name" });
@@ -133,14 +118,14 @@ function packageVersionSchema(options) {
133
118
  { description: "NPM package name and version of a published package" }
134
119
  );
135
120
  }
136
- function weightSchema(description = "Coefficient for the given score (use weight 0 if only for display)") {
137
- return positiveIntSchema(description);
138
- }
121
+ var weightSchema = nonnegativeIntSchema.describe(
122
+ "Coefficient for the given score (use weight 0 if only for display)"
123
+ );
139
124
  function weightedRefSchema(description, slugDescription) {
140
125
  return z.object(
141
126
  {
142
- slug: slugSchema(slugDescription),
143
- weight: weightSchema("Weight used to calculate score")
127
+ slug: slugSchema.describe(slugDescription),
128
+ weight: weightSchema.describe("Weight used to calculate score")
144
129
  },
145
130
  { description }
146
131
  );
@@ -148,14 +133,14 @@ function weightedRefSchema(description, slugDescription) {
148
133
  function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
149
134
  return z.object(
150
135
  {
151
- slug: slugSchema('Human-readable unique ID, e.g. "performance"'),
136
+ slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
152
137
  refs: z.array(refSchema).min(1).refine(
153
138
  (refs) => !duplicateCheckFn(refs),
154
139
  (refs) => ({
155
140
  message: duplicateMessageFn(refs)
156
141
  })
157
- ).refine(hasWeightedRefsInCategories, () => ({
158
- message: `In a category there has to be at least one ref with weight > 0`
142
+ ).refine(hasNonZeroWeightedRef, () => ({
143
+ message: "In a category there has to be at least one ref with weight > 0"
159
144
  }))
160
145
  },
161
146
  { description }
@@ -164,13 +149,13 @@ function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessa
164
149
  var materialIconSchema = z.enum(MATERIAL_ICONS, {
165
150
  description: "Icon from VSCode Material Icons extension"
166
151
  });
167
- function hasWeightedRefsInCategories(categoryRefs) {
168
- return categoryRefs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
152
+ function hasNonZeroWeightedRef(refs) {
153
+ return refs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
169
154
  }
170
155
 
171
156
  // packages/models/src/lib/audit.ts
172
157
  var auditSchema = z2.object({
173
- slug: slugSchema("ID (unique within plugin)")
158
+ slug: slugSchema.describe("ID (unique within plugin)")
174
159
  }).merge(
175
160
  metaSchema({
176
161
  titleDescription: "Descriptive name",
@@ -197,17 +182,20 @@ function getDuplicateSlugsInAudits(audits) {
197
182
  return hasDuplicateStrings(audits.map(({ slug }) => slug));
198
183
  }
199
184
 
200
- // packages/models/src/lib/audit-issue.ts
185
+ // packages/models/src/lib/audit-output.ts
186
+ import { z as z4 } from "zod";
187
+
188
+ // packages/models/src/lib/issue.ts
201
189
  import { z as z3 } from "zod";
202
190
  var sourceFileLocationSchema = z3.object(
203
191
  {
204
- file: filePathSchema("Relative path to source file in Git repo"),
192
+ file: filePathSchema.describe("Relative path to source file in Git repo"),
205
193
  position: z3.object(
206
194
  {
207
- startLine: positiveIntSchema("Start line"),
208
- startColumn: positiveIntSchema("Start column").optional(),
209
- endLine: positiveIntSchema("End line").optional(),
210
- endColumn: positiveIntSchema("End column").optional()
195
+ startLine: positiveIntSchema.describe("Start line"),
196
+ startColumn: positiveIntSchema.describe("Start column").optional(),
197
+ endLine: positiveIntSchema.describe("End line").optional(),
198
+ endColumn: positiveIntSchema.describe("End column").optional()
211
199
  },
212
200
  { description: "Location in file" }
213
201
  ).optional()
@@ -227,21 +215,21 @@ var issueSchema = z3.object(
227
215
  );
228
216
 
229
217
  // packages/models/src/lib/audit-output.ts
230
- import { z as z4 } from "zod";
218
+ var auditDetailsSchema = z4.object(
219
+ {
220
+ issues: z4.array(issueSchema, { description: "List of findings" })
221
+ },
222
+ { description: "Detailed information" }
223
+ );
231
224
  var auditOutputSchema = z4.object(
232
225
  {
233
- slug: slugSchema("Reference to audit"),
226
+ slug: slugSchema.describe("Reference to audit"),
234
227
  displayValue: z4.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional(),
235
- value: positiveIntSchema("Raw numeric value"),
228
+ value: nonnegativeIntSchema.describe("Raw numeric value"),
236
229
  score: z4.number({
237
230
  description: "Value between 0 and 1"
238
231
  }).min(0).max(1),
239
- details: z4.object(
240
- {
241
- issues: z4.array(issueSchema, { description: "List of findings" })
242
- },
243
- { description: "Detailed information" }
244
- ).optional()
232
+ details: auditDetailsSchema.optional()
245
233
  },
246
234
  { description: "Audit information" }
247
235
  );
@@ -271,7 +259,7 @@ var categoryRefSchema = weightedRefSchema(
271
259
  type: z5.enum(["audit", "group"], {
272
260
  description: "Discriminant for reference kind, affects where `slug` is looked up"
273
261
  }),
274
- plugin: slugSchema(
262
+ plugin: slugSchema.describe(
275
263
  "Plugin slug (plugin should contain referenced audit or group)"
276
264
  )
277
265
  })
@@ -331,10 +319,8 @@ import { z as z11 } from "zod";
331
319
  import { z as z6 } from "zod";
332
320
  var formatSchema = z6.enum(["json", "md"]);
333
321
  var persistConfigSchema = z6.object({
334
- outputDir: filePathSchema("Artifacts folder").optional(),
335
- filename: fileNameSchema(
336
- "Artifacts file name (without extension)"
337
- ).optional(),
322
+ outputDir: filePathSchema.describe("Artifacts folder").optional(),
323
+ filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
338
324
  format: z6.array(formatSchema).optional()
339
325
  });
340
326
 
@@ -395,7 +381,7 @@ var runnerConfigSchema = z8.object(
395
381
  description: "Shell command to execute"
396
382
  }),
397
383
  args: z8.array(z8.string({ description: "Command arguments" })).optional(),
398
- outputFile: filePathSchema("Output path"),
384
+ outputFile: filePathSchema.describe("Output path"),
399
385
  outputTransform: outputTransformSchema.optional()
400
386
  },
401
387
  {
@@ -415,7 +401,7 @@ var pluginMetaSchema = packageVersionSchema().merge(
415
401
  })
416
402
  ).merge(
417
403
  z9.object({
418
- slug: slugSchema("Unique plugin slug within core config"),
404
+ slug: slugSchema.describe("Unique plugin slug within core config"),
419
405
  icon: materialIconSchema
420
406
  })
421
407
  );
@@ -448,12 +434,14 @@ function getMissingRefsFromGroups(pluginCfg) {
448
434
  // packages/models/src/lib/upload-config.ts
449
435
  import { z as z10 } from "zod";
450
436
  var uploadConfigSchema = z10.object({
451
- server: urlSchema("URL of deployed portal API"),
437
+ server: urlSchema.describe("URL of deployed portal API"),
452
438
  apiKey: z10.string({
453
439
  description: "API key with write access to portal (use `process.env` for security)"
454
440
  }),
455
- organization: slugSchema("Organization slug from Code PushUp portal"),
456
- project: slugSchema("Project slug from Code PushUp portal"),
441
+ organization: slugSchema.describe(
442
+ "Organization slug from Code PushUp portal"
443
+ ),
444
+ project: slugSchema.describe("Project slug from Code PushUp portal"),
457
445
  timeout: z10.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
458
446
  });
459
447
 
@@ -1017,16 +1005,140 @@ function executeProcess(cfg) {
1017
1005
  });
1018
1006
  }
1019
1007
 
1008
+ // packages/utils/src/lib/transform.ts
1009
+ import { platform } from "node:os";
1010
+ function toArray(val) {
1011
+ return Array.isArray(val) ? val : [val];
1012
+ }
1013
+ function objectToKeys(obj) {
1014
+ return Object.keys(obj);
1015
+ }
1016
+ function objectToEntries(obj) {
1017
+ return Object.entries(obj);
1018
+ }
1019
+ function countOccurrences(values) {
1020
+ return values.reduce(
1021
+ (acc, value) => ({ ...acc, [value]: (acc[value] ?? 0) + 1 }),
1022
+ {}
1023
+ );
1024
+ }
1025
+ function distinct(array) {
1026
+ return [...new Set(array)];
1027
+ }
1028
+ function deepClone(obj) {
1029
+ return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
1030
+ }
1031
+ function factorOf(items, filterFn) {
1032
+ const itemCount = items.length;
1033
+ if (!itemCount) {
1034
+ return 1;
1035
+ }
1036
+ const filterCount = items.filter(filterFn).length;
1037
+ return filterCount === 0 ? 1 : (itemCount - filterCount) / itemCount;
1038
+ }
1039
+ function objectToCliArgs(params) {
1040
+ if (!params) {
1041
+ return [];
1042
+ }
1043
+ return Object.entries(params).flatMap(([key, value]) => {
1044
+ if (key === "_") {
1045
+ return Array.isArray(value) ? value : [`${value}`];
1046
+ }
1047
+ const prefix = key.length === 1 ? "-" : "--";
1048
+ if (Array.isArray(value)) {
1049
+ return value.map((v) => `${prefix}${key}="${v}"`);
1050
+ }
1051
+ if (Array.isArray(value)) {
1052
+ return value.map((v) => `${prefix}${key}="${v}"`);
1053
+ }
1054
+ if (typeof value === "string") {
1055
+ return [`${prefix}${key}="${value}"`];
1056
+ }
1057
+ if (typeof value === "number") {
1058
+ return [`${prefix}${key}=${value}`];
1059
+ }
1060
+ if (typeof value === "boolean") {
1061
+ return [`${prefix}${value ? "" : "no-"}${key}`];
1062
+ }
1063
+ throw new Error(`Unsupported type ${typeof value} for key ${key}`);
1064
+ });
1065
+ }
1066
+ function toUnixPath(path) {
1067
+ return path.replace(/\\/g, "/");
1068
+ }
1069
+ function toUnixNewlines(text) {
1070
+ return platform() === "win32" ? text.replace(/\r\n/g, "\n") : text;
1071
+ }
1072
+ function capitalize(text) {
1073
+ return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
1074
+ 1
1075
+ )}`;
1076
+ }
1077
+ function toNumberPrecision(value, decimalPlaces) {
1078
+ return Number(
1079
+ `${Math.round(
1080
+ Number.parseFloat(`${value}e${decimalPlaces}`)
1081
+ )}e-${decimalPlaces}`
1082
+ );
1083
+ }
1084
+ function toOrdinal(value) {
1085
+ if (value % 10 === 1 && value % 100 !== 11) {
1086
+ return `${value}st`;
1087
+ }
1088
+ if (value % 10 === 2 && value % 100 !== 12) {
1089
+ return `${value}nd`;
1090
+ }
1091
+ if (value % 10 === 3 && value % 100 !== 13) {
1092
+ return `${value}rd`;
1093
+ }
1094
+ return `${value}th`;
1095
+ }
1096
+
1097
+ // packages/utils/src/lib/filter-by-slug.ts
1098
+ function filterGroupsByAuditSlug(groups, auditSlugs) {
1099
+ const slugs = toArray(auditSlugs);
1100
+ if (slugs.length === 0) {
1101
+ return groups;
1102
+ }
1103
+ return groups.map((group) => ({
1104
+ ...group,
1105
+ refs: filterSlug(group.refs, slugs)
1106
+ })).filter((group) => group.refs.length);
1107
+ }
1108
+ function filterAuditsBySlug(list, auditSlugs) {
1109
+ const slugs = toArray(auditSlugs);
1110
+ if (slugs.length === 0) {
1111
+ return list;
1112
+ }
1113
+ return filterSlug(list, slugs);
1114
+ }
1115
+ function filterSlug(refs, slugOrSlugs) {
1116
+ const slugs = toArray(slugOrSlugs);
1117
+ return refs.filter(({ slug }) => slugs.includes(slug));
1118
+ }
1119
+
1020
1120
  // packages/utils/src/lib/git.ts
1121
+ import { isAbsolute, join as join3, relative } from "node:path";
1021
1122
  import { simpleGit } from "simple-git";
1022
- var git = simpleGit();
1023
- async function getLatestCommit() {
1123
+ async function getLatestCommit(git = simpleGit()) {
1024
1124
  const log = await git.log({
1025
1125
  maxCount: 1,
1026
1126
  format: { hash: "%H", message: "%s", author: "%an", date: "%ad" }
1027
1127
  });
1028
1128
  return log.latest;
1029
1129
  }
1130
+ function getGitRoot(git = simpleGit()) {
1131
+ return git.revparse("--show-toplevel");
1132
+ }
1133
+ function formatGitPath(path, gitRoot) {
1134
+ const absolutePath = isAbsolute(path) ? path : join3(process.cwd(), path);
1135
+ const relativePath = relative(gitRoot, absolutePath);
1136
+ return toUnixPath(relativePath);
1137
+ }
1138
+ async function toGitPath(path, git = simpleGit()) {
1139
+ const gitRoot = await getGitRoot(git);
1140
+ return formatGitPath(path, gitRoot);
1141
+ }
1030
1142
  function validateCommitData(commitData, options = {}) {
1031
1143
  const { throwError = false } = options;
1032
1144
  if (!commitData) {
@@ -1049,18 +1161,24 @@ function groupByStatus(results) {
1049
1161
  );
1050
1162
  }
1051
1163
 
1052
- // packages/utils/src/lib/progress.ts
1164
+ // packages/utils/src/lib/logging.ts
1053
1165
  import chalk2 from "chalk";
1166
+ function link(text) {
1167
+ return chalk2.underline(chalk2.blueBright(text));
1168
+ }
1169
+
1170
+ // packages/utils/src/lib/progress.ts
1171
+ import chalk3 from "chalk";
1054
1172
  import { MultiProgressBars } from "multi-progress-bars";
1055
1173
  var barStyles = {
1056
- active: (s) => chalk2.green(s),
1057
- done: (s) => chalk2.gray(s),
1058
- idle: (s) => chalk2.gray(s)
1174
+ active: (s) => chalk3.green(s),
1175
+ done: (s) => chalk3.gray(s),
1176
+ idle: (s) => chalk3.gray(s)
1059
1177
  };
1060
1178
  var messageStyles = {
1061
- active: (s) => chalk2.black(s),
1062
- done: (s) => chalk2.green(chalk2.bold(s)),
1063
- idle: (s) => chalk2.gray(s)
1179
+ active: (s) => chalk3.black(s),
1180
+ done: (s) => chalk3.green(chalk3.bold(s)),
1181
+ idle: (s) => chalk3.gray(s)
1064
1182
  };
1065
1183
  var mpb;
1066
1184
  function getSingletonProgressBars(options) {
@@ -1140,7 +1258,7 @@ function h3(text) {
1140
1258
  }
1141
1259
 
1142
1260
  // packages/utils/src/lib/reports/md/link.ts
1143
- function link(href, text) {
1261
+ function link2(href, text) {
1144
1262
  return `[${text || href}](${href})`;
1145
1263
  }
1146
1264
 
@@ -1191,7 +1309,7 @@ function generateMdReport(report, commitData) {
1191
1309
  (printCategories ? reportToCategoriesSection(report) + NEW_LINE + NEW_LINE : "") + // audits section
1192
1310
  reportToAuditsSection(report) + NEW_LINE + NEW_LINE + // about section
1193
1311
  reportToAboutSection(report, commitData) + NEW_LINE + NEW_LINE + // footer section
1194
- `${FOOTER_PREFIX} ${link(README_LINK, "Code PushUp")}`
1312
+ `${FOOTER_PREFIX} ${link2(README_LINK, "Code PushUp")}`
1195
1313
  );
1196
1314
  }
1197
1315
  function reportToHeaderSection() {
@@ -1202,7 +1320,7 @@ function reportToOverviewSection(report) {
1202
1320
  const tableContent = [
1203
1321
  reportOverviewTableHeaders,
1204
1322
  ...categories.map(({ title, refs, score }) => [
1205
- link(`#${slugify(title)}`, title),
1323
+ link2(`#${slugify(title)}`, title),
1206
1324
  `${getRoundScoreMarker(score)} ${style(formatReportScore(score))}`,
1207
1325
  countCategoryAudits(refs, plugins).toString()
1208
1326
  ])
@@ -1234,7 +1352,7 @@ function reportToCategoriesSection(report) {
1234
1352
  }
1235
1353
  function auditItemToCategorySection(audit, plugins) {
1236
1354
  const pluginTitle = getPluginNameFromSlug(audit.plugin, plugins);
1237
- const auditTitle = link(
1355
+ const auditTitle = link2(
1238
1356
  `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1239
1357
  audit.title
1240
1358
  );
@@ -1251,7 +1369,7 @@ function groupItemToCategorySection(group, plugins) {
1251
1369
  `${getRoundScoreMarker(groupScore)} ${group.title} (_${pluginTitle}_)`
1252
1370
  );
1253
1371
  const groupAudits = group.audits.reduce((acc, audit) => {
1254
- const auditTitle = link(
1372
+ const auditTitle = link2(
1255
1373
  `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1256
1374
  audit.title
1257
1375
  );
@@ -1338,7 +1456,7 @@ function getDocsAndDescription({
1338
1456
  description
1339
1457
  }) {
1340
1458
  if (docsUrl) {
1341
- const docsLink = link(docsUrl, "\u{1F4D6} Docs");
1459
+ const docsLink = link2(docsUrl, "\u{1F4D6} Docs");
1342
1460
  if (!description) {
1343
1461
  return docsLink + NEW_LINE + NEW_LINE;
1344
1462
  }
@@ -1359,7 +1477,7 @@ function getAuditResult(audit, isHtml = false) {
1359
1477
 
1360
1478
  // packages/utils/src/lib/reports/generate-stdout-summary.ts
1361
1479
  import cliui from "@isaacs/cliui";
1362
- import chalk3 from "chalk";
1480
+ import chalk4 from "chalk";
1363
1481
  import CliTable3 from "cli-table3";
1364
1482
  function addLine(line = "") {
1365
1483
  return line + NEW_LINE;
@@ -1370,7 +1488,7 @@ function generateStdoutSummary(report) {
1370
1488
  }
1371
1489
  function reportToHeaderSection2(report) {
1372
1490
  const { packageName, version } = report;
1373
- return `${chalk3.bold(reportHeadlineText)} - ${packageName}@${version}`;
1491
+ return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version}`;
1374
1492
  }
1375
1493
  function reportToDetailSection(report) {
1376
1494
  const { plugins } = report;
@@ -1390,13 +1508,13 @@ function reportToDetailSection(report) {
1390
1508
  padding: [0, 3, 0, 0]
1391
1509
  },
1392
1510
  {
1393
- text: chalk3.cyanBright(audit.displayValue || `${audit.value}`),
1511
+ text: chalk4.cyanBright(audit.displayValue || `${audit.value}`),
1394
1512
  width: 10,
1395
1513
  padding: [0, 0, 0, 0]
1396
1514
  }
1397
1515
  );
1398
1516
  });
1399
- return acc + addLine() + addLine(chalk3.magentaBright.bold(`${title} audits`)) + addLine() + addLine(ui.toString()) + addLine();
1517
+ return acc + addLine() + addLine(chalk4.magentaBright.bold(`${title} audits`)) + addLine() + addLine(ui.toString()) + addLine();
1400
1518
  }, "");
1401
1519
  }
1402
1520
  function reportToOverviewSection2({
@@ -1419,11 +1537,11 @@ function reportToOverviewSection2({
1419
1537
  countCategoryAudits(refs, plugins)
1420
1538
  ])
1421
1539
  );
1422
- return addLine(chalk3.magentaBright.bold("Categories")) + addLine() + addLine(table.toString());
1540
+ return addLine(chalk4.magentaBright.bold("Categories")) + addLine() + addLine(table.toString());
1423
1541
  }
1424
1542
  function withColor({ score, text }) {
1425
1543
  const formattedScore = text ?? formatReportScore(score);
1426
- const style2 = text ? chalk3 : chalk3.bold;
1544
+ const style2 = text ? chalk4 : chalk4.bold;
1427
1545
  if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
1428
1546
  return style2.green(formattedScore);
1429
1547
  }
@@ -1433,99 +1551,6 @@ function withColor({ score, text }) {
1433
1551
  return style2.red(formattedScore);
1434
1552
  }
1435
1553
 
1436
- // packages/utils/src/lib/transform.ts
1437
- import { platform } from "node:os";
1438
- function toArray(val) {
1439
- return Array.isArray(val) ? val : [val];
1440
- }
1441
- function objectToKeys(obj) {
1442
- return Object.keys(obj);
1443
- }
1444
- function objectToEntries(obj) {
1445
- return Object.entries(obj);
1446
- }
1447
- function countOccurrences(values) {
1448
- return values.reduce(
1449
- (acc, value) => ({ ...acc, [value]: (acc[value] ?? 0) + 1 }),
1450
- {}
1451
- );
1452
- }
1453
- function distinct(array) {
1454
- return [...new Set(array)];
1455
- }
1456
- function deepClone(obj) {
1457
- return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
1458
- }
1459
- function factorOf(items, filterFn) {
1460
- const itemCount = items.length;
1461
- if (!itemCount) {
1462
- return 1;
1463
- }
1464
- const filterCount = items.filter(filterFn).length;
1465
- return filterCount === 0 ? 1 : (itemCount - filterCount) / itemCount;
1466
- }
1467
- function objectToCliArgs(params) {
1468
- if (!params) {
1469
- return [];
1470
- }
1471
- return Object.entries(params).flatMap(([key, value]) => {
1472
- if (key === "_") {
1473
- return Array.isArray(value) ? value : [`${value}`];
1474
- }
1475
- const prefix = key.length === 1 ? "-" : "--";
1476
- if (Array.isArray(value)) {
1477
- return value.map((v) => `${prefix}${key}="${v}"`);
1478
- }
1479
- if (Array.isArray(value)) {
1480
- return value.map((v) => `${prefix}${key}="${v}"`);
1481
- }
1482
- if (typeof value === "string") {
1483
- return [`${prefix}${key}="${value}"`];
1484
- }
1485
- if (typeof value === "number") {
1486
- return [`${prefix}${key}=${value}`];
1487
- }
1488
- if (typeof value === "boolean") {
1489
- return [`${prefix}${value ? "" : "no-"}${key}`];
1490
- }
1491
- throw new Error(`Unsupported type ${typeof value} for key ${key}`);
1492
- });
1493
- }
1494
- function toUnixPath(path, options) {
1495
- const unixPath = path.replace(/\\/g, "/");
1496
- if (options?.toRelative) {
1497
- return unixPath.replace(`${process.cwd().replace(/\\/g, "/")}/`, "");
1498
- }
1499
- return unixPath;
1500
- }
1501
- function toUnixNewlines(text) {
1502
- return platform() === "win32" ? text.replace(/\r\n/g, "\n") : text;
1503
- }
1504
- function capitalize(text) {
1505
- return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
1506
- 1
1507
- )}`;
1508
- }
1509
- function toNumberPrecision(value, decimalPlaces) {
1510
- return Number(
1511
- `${Math.round(
1512
- Number.parseFloat(`${value}e${decimalPlaces}`)
1513
- )}e-${decimalPlaces}`
1514
- );
1515
- }
1516
- function toOrdinal(value) {
1517
- if (value % 10 === 1 && value % 100 !== 11) {
1518
- return `${value}st`;
1519
- }
1520
- if (value % 10 === 2 && value % 100 !== 12) {
1521
- return `${value}nd`;
1522
- }
1523
- if (value % 10 === 3 && value % 100 !== 13) {
1524
- return `${value}rd`;
1525
- }
1526
- return `${value}th`;
1527
- }
1528
-
1529
1554
  // packages/utils/src/lib/reports/scoring.ts
1530
1555
  var GroupRefInvalidError = class extends Error {
1531
1556
  constructor(auditSlug, pluginSlug) {
@@ -1685,35 +1710,6 @@ var verboseUtils = (verbose = false) => ({
1685
1710
  log: getLogVerbose(verbose),
1686
1711
  exec: getExecVerbose(verbose)
1687
1712
  });
1688
-
1689
- // packages/utils/src/lib/logging.ts
1690
- import chalk4 from "chalk";
1691
- function link2(text) {
1692
- return chalk4.underline(chalk4.blueBright(text));
1693
- }
1694
-
1695
- // packages/utils/src/lib/filter-by-slug.ts
1696
- function filterGroupsByAuditSlug(groups, auditSlugs) {
1697
- const slugs = toArray(auditSlugs);
1698
- if (slugs.length === 0) {
1699
- return groups;
1700
- }
1701
- return groups.map((group) => ({
1702
- ...group,
1703
- refs: filterSlug(group.refs, slugs)
1704
- })).filter((group) => group.refs.length);
1705
- }
1706
- function filterAuditsBySlug(list, auditSlugs) {
1707
- const slugs = toArray(auditSlugs);
1708
- if (slugs.length === 0) {
1709
- return list;
1710
- }
1711
- return filterSlug(list, slugs);
1712
- }
1713
- function filterSlug(refs, slugOrSlugs) {
1714
- const slugs = toArray(slugOrSlugs);
1715
- return refs.filter(({ slug }) => slugs.includes(slug));
1716
- }
1717
1713
  export {
1718
1714
  CODE_PUSHUP_DOMAIN,
1719
1715
  FOOTER_PREFIX,
@@ -1737,16 +1733,17 @@ export {
1737
1733
  findLineNumberInText,
1738
1734
  formatBytes,
1739
1735
  formatDuration,
1736
+ formatGitPath,
1740
1737
  generateMdReport,
1741
1738
  generateStdoutSummary,
1739
+ getGitRoot,
1742
1740
  getLatestCommit,
1743
1741
  getProgressBar,
1744
- git,
1745
1742
  groupByStatus,
1746
1743
  importEsmModule,
1747
1744
  isPromiseFulfilledResult,
1748
1745
  isPromiseRejectedResult,
1749
- link2 as link,
1746
+ link,
1750
1747
  loadReport,
1751
1748
  logMultipleFileResults,
1752
1749
  logMultipleResults,
@@ -1764,6 +1761,7 @@ export {
1764
1761
  slugify,
1765
1762
  sortReport,
1766
1763
  toArray,
1764
+ toGitPath,
1767
1765
  toNumberPrecision,
1768
1766
  toOrdinal,
1769
1767
  toUnixNewlines,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-pushup/utils",
3
- "version": "0.18.1",
3
+ "version": "0.20.0",
4
4
  "dependencies": {
5
5
  "@code-pushup/models": "*",
6
6
  "bundle-require": "^4.0.1",
package/src/index.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  export { exists } from '@code-pushup/models';
2
2
  export { ProcessConfig, ProcessError, ProcessObserver, ProcessResult, executeProcess, } from './lib/execute-process';
3
3
  export { CrawlFileSystemOptions, FileResult, MultipleFileResults, crawlFileSystem, directoryExists, ensureDirectoryExists, fileExists, findLineNumberInText, importEsmModule, logMultipleFileResults, pluginWorkDir, readJsonFile, readTextFile, removeDirectoryIfExists, } from './lib/file-system';
4
+ export { filterAuditsBySlug, filterGroupsByAuditSlug, } from './lib/filter-by-slug';
4
5
  export { formatBytes, formatDuration, pluralize, pluralizeToken, slugify, truncateDescription, truncateIssueMessage, truncateText, truncateTitle, } from './lib/formatting';
5
- export { getLatestCommit, git, validateCommitData } from './lib/git';
6
+ export { formatGitPath, getGitRoot, getLatestCommit, toGitPath, validateCommitData, } from './lib/git';
6
7
  export { groupByStatus } from './lib/group-by-status';
7
8
  export { isPromiseFulfilledResult, isPromiseRejectedResult, } from './lib/guards';
8
9
  export { logMultipleResults } from './lib/log-results';
10
+ export { link } from './lib/logging';
9
11
  export { ProgressBar, getProgressBar } from './lib/progress';
10
12
  export { TERMINAL_WIDTH } from './lib/reports/constants';
11
13
  export { generateMdReport } from './lib/reports/generate-md-report';
@@ -15,5 +17,3 @@ export { sortReport } from './lib/reports/sorting';
15
17
  export { CODE_PUSHUP_DOMAIN, FOOTER_PREFIX, README_LINK, calcDuration, compareIssueSeverity, loadReport, portalCommitDashboardLink, } from './lib/reports/utils';
16
18
  export { CliArgsObject, capitalize, countOccurrences, distinct, factorOf, objectToCliArgs, objectToEntries, objectToKeys, toArray, toNumberPrecision, toOrdinal, toUnixNewlines, toUnixPath, } from './lib/transform';
17
19
  export { verboseUtils } from './lib/verbose-utils';
18
- export { link } from './lib/logging';
19
- export { filterAuditsBySlug, filterGroupsByAuditSlug, } from './lib/filter-by-slug';
package/src/lib/git.d.ts CHANGED
@@ -4,13 +4,15 @@ export type CommitData = {
4
4
  author: string;
5
5
  date: string;
6
6
  };
7
- export declare const git: import("simple-git").SimpleGit;
8
- export declare function getLatestCommit(): Promise<({
7
+ export declare function getLatestCommit(git?: import("simple-git").SimpleGit): Promise<({
9
8
  hash: string;
10
9
  message: string;
11
10
  author: string;
12
11
  date: string;
13
12
  } & import("simple-git").ListLogLine) | null>;
14
- export declare function validateCommitData(commitData?: unknown, options?: {
13
+ export declare function getGitRoot(git?: import("simple-git").SimpleGit): Promise<string>;
14
+ export declare function formatGitPath(path: string, gitRoot: string): string;
15
+ export declare function toGitPath(path: string, git?: import("simple-git").SimpleGit): Promise<string>;
16
+ export declare function validateCommitData(commitData: CommitData | null, options?: {
15
17
  throwError?: boolean;
16
18
  }): commitData is CommitData;
@@ -22,9 +22,7 @@ Record<string, ArgumentValue | undefined> | {
22
22
  * });
23
23
  */
24
24
  export declare function objectToCliArgs<T extends object = Record<string, ArgumentValue>>(params?: CliArgsObject<T>): string[];
25
- export declare function toUnixPath(path: string, options?: {
26
- toRelative?: boolean;
27
- }): string;
25
+ export declare function toUnixPath(path: string): string;
28
26
  export declare function toUnixNewlines(text: string): string;
29
27
  export declare function capitalize<T extends string>(text: T): Capitalize<T>;
30
28
  export declare function toNumberPrecision(value: number, decimalPlaces: number): number;