@code-pushup/utils 0.8.24 → 0.9.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.
Files changed (2) hide show
  1. package/index.js +108 -136
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -457,7 +457,8 @@ var uploadConfigSchema = z10.object({
457
457
  description: "API key with write access to portal (use `process.env` for security)"
458
458
  }),
459
459
  organization: slugSchema("Organization slug from Code PushUp portal"),
460
- project: slugSchema("Project slug from Code PushUp portal")
460
+ project: slugSchema("Project slug from Code PushUp portal"),
461
+ timeout: z10.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
461
462
  });
462
463
 
463
464
  // packages/models/src/lib/core-config.ts
@@ -566,15 +567,15 @@ function pluralize(text) {
566
567
  return `${text}s`;
567
568
  }
568
569
  function formatBytes(bytes, decimals = 2) {
569
- bytes = Math.max(bytes, 0);
570
- if (!bytes) {
570
+ const positiveBytes = Math.max(bytes, 0);
571
+ if (positiveBytes === 0) {
571
572
  return "0 B";
572
573
  }
573
574
  const k = 1024;
574
575
  const dm = decimals < 0 ? 0 : decimals;
575
576
  const sizes = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
576
- const i = Math.floor(Math.log(bytes) / Math.log(k));
577
- return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
577
+ const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
578
+ return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
578
579
  }
579
580
  function pluralizeToken(token, times = 0) {
580
581
  return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
@@ -807,8 +808,7 @@ function getSeverityIcon(severity) {
807
808
  return "\u2139\uFE0F";
808
809
  }
809
810
  function calcDuration(start, stop) {
810
- stop ??= performance.now();
811
- return Math.floor(stop - start);
811
+ return Math.floor((stop ?? performance.now()) - start);
812
812
  }
813
813
  function countCategoryAudits(refs, plugins) {
814
814
  const groupLookup = plugins.reduce(
@@ -834,15 +834,15 @@ function countCategoryAudits(refs, plugins) {
834
834
  }, 0);
835
835
  }
836
836
  function getAuditByRef({ slug, weight, plugin }, plugins) {
837
- const auditPlugin = plugins.find(({ slug: slug2 }) => slug2 === plugin);
837
+ const auditPlugin = plugins.find((p) => p.slug === plugin);
838
838
  if (!auditPlugin) {
839
839
  throwIsNotPresentError(`Plugin ${plugin}`, "report");
840
840
  }
841
- const audit = auditPlugin?.audits.find(
841
+ const audit = auditPlugin.audits.find(
842
842
  ({ slug: auditSlug }) => auditSlug === slug
843
843
  );
844
844
  if (!audit) {
845
- throwIsNotPresentError(`Audit ${slug}`, auditPlugin?.slug);
845
+ throwIsNotPresentError(`Audit ${slug}`, auditPlugin.slug);
846
846
  }
847
847
  return {
848
848
  ...audit,
@@ -855,24 +855,21 @@ function getGroupWithAudits(refSlug, refPlugin, plugins) {
855
855
  if (!plugin) {
856
856
  throwIsNotPresentError(`Plugin ${refPlugin}`, "report");
857
857
  }
858
- const groupWithAudits = plugin?.groups?.find(({ slug }) => slug === refSlug);
858
+ const groupWithAudits = plugin.groups?.find(({ slug }) => slug === refSlug);
859
859
  if (!groupWithAudits) {
860
- throwIsNotPresentError(`Group ${refSlug}`, plugin?.slug);
860
+ throwIsNotPresentError(`Group ${refSlug}`, plugin.slug);
861
861
  }
862
862
  const groupAudits = groupWithAudits.refs.reduce(
863
863
  (acc, ref) => {
864
864
  const audit = getAuditByRef(
865
- { ...ref, plugin: refPlugin },
865
+ { ...ref, plugin: refPlugin, type: "audit" },
866
866
  plugins
867
867
  );
868
- if (audit) {
869
- return [...acc, audit];
870
- }
871
- return [...acc];
868
+ return [...acc, audit];
872
869
  },
873
870
  []
874
871
  );
875
- const audits = groupAudits.sort(compareCategoryAudits);
872
+ const audits = [...groupAudits].sort(compareCategoryAudits);
876
873
  return {
877
874
  ...groupWithAudits,
878
875
  audits
@@ -915,7 +912,8 @@ async function loadReport(options) {
915
912
  const content = await readJsonFile(filePath);
916
913
  return reportSchema.parse(content);
917
914
  }
918
- return readTextFile(filePath);
915
+ const text = await readTextFile(filePath);
916
+ return text;
919
917
  }
920
918
  function throwIsNotPresentError(itemName, presentPlace) {
921
919
  throw new Error(`${itemName} is not present in ${presentPlace}`);
@@ -970,11 +968,11 @@ function executeProcess(cfg) {
970
968
  let stdout = "";
971
969
  let stderr = "";
972
970
  process2.stdout.on("data", (data) => {
973
- stdout += data.toString();
974
- onStdout?.(data);
971
+ stdout += String(data);
972
+ onStdout?.(String(data));
975
973
  });
976
974
  process2.stderr.on("data", (data) => {
977
- stderr += data.toString();
975
+ stderr += String(data);
978
976
  });
979
977
  process2.on("error", (err) => {
980
978
  stderr += err.toString();
@@ -994,28 +992,20 @@ function executeProcess(cfg) {
994
992
  }
995
993
 
996
994
  // packages/utils/src/lib/git.ts
997
- import simpleGit from "simple-git";
995
+ import { simpleGit } from "simple-git";
998
996
  var git = simpleGit();
999
997
  async function getLatestCommit() {
1000
998
  const log = await git.log({
1001
999
  maxCount: 1,
1002
1000
  format: { hash: "%H", message: "%s", author: "%an", date: "%ad" }
1003
1001
  });
1004
- return log?.latest;
1002
+ return log.latest;
1005
1003
  }
1006
1004
 
1007
1005
  // packages/utils/src/lib/group-by-status.ts
1008
1006
  function groupByStatus(results) {
1009
1007
  return results.reduce(
1010
- (acc, result) => {
1011
- if (result.status === "fulfilled") {
1012
- return { ...acc, fulfilled: [...acc.fulfilled, result] };
1013
- }
1014
- if (result.status === "rejected") {
1015
- return { ...acc, rejected: [...acc.rejected, result] };
1016
- }
1017
- return acc;
1018
- },
1008
+ (acc, result) => result.status === "fulfilled" ? { ...acc, fulfilled: [...acc.fulfilled, result] } : { ...acc, rejected: [...acc.rejected, result] },
1019
1009
  { fulfilled: [], rejected: [] }
1020
1010
  );
1021
1011
  }
@@ -1130,10 +1120,10 @@ function tableMd(data, align) {
1130
1120
  if (data.length === 0) {
1131
1121
  throw new Error("Data can't be empty");
1132
1122
  }
1133
- align ??= data[0]?.map(() => "c");
1123
+ const alignmentSetting = align ?? data[0]?.map(() => "c");
1134
1124
  const tableContent = data.map((arr) => `|${arr.join("|")}|`);
1135
- const secondRow = `|${align?.map((s) => alignString.get(s)).join("|")}|`;
1136
- return tableContent.shift() + NEW_LINE + secondRow + NEW_LINE + tableContent.join(NEW_LINE);
1125
+ const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
1126
+ return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
1137
1127
  }
1138
1128
  function tableHtml(data) {
1139
1129
  if (data.length === 0) {
@@ -1186,15 +1176,15 @@ function reportToCategoriesSection(report) {
1186
1176
  category.score
1187
1177
  )} Score: ${style(formatReportScore(category.score))}`;
1188
1178
  const categoryDocs = getDocsAndDescription(category);
1189
- const categoryMDItems = category.refs.reduce((acc2, ref) => {
1179
+ const categoryMDItems = category.refs.reduce((refAcc, ref) => {
1190
1180
  if (ref.type === "group") {
1191
1181
  const group = getGroupWithAudits(ref.slug, ref.plugin, plugins);
1192
1182
  const mdGroupItem = groupItemToCategorySection(group, plugins);
1193
- return acc2 + mdGroupItem + NEW_LINE;
1183
+ return refAcc + mdGroupItem + NEW_LINE;
1194
1184
  } else {
1195
1185
  const audit = getAuditByRef(ref, plugins);
1196
1186
  const mdAuditItem = auditItemToCategorySection(audit, plugins);
1197
- return acc2 + mdAuditItem + NEW_LINE;
1187
+ return refAcc + mdAuditItem + NEW_LINE;
1198
1188
  }
1199
1189
  }, "");
1200
1190
  return acc + NEW_LINE + categoryTitle + NEW_LINE + NEW_LINE + categoryDocs + categoryScore + NEW_LINE + categoryMDItems;
@@ -1205,7 +1195,7 @@ function auditItemToCategorySection(audit, plugins) {
1205
1195
  const pluginTitle = getPluginNameFromSlug(audit.plugin, plugins);
1206
1196
  const auditTitle = link(
1207
1197
  `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1208
- audit?.title
1198
+ audit.title
1209
1199
  );
1210
1200
  return li(
1211
1201
  `${getSquaredScoreMarker(
@@ -1222,69 +1212,57 @@ function groupItemToCategorySection(group, plugins) {
1222
1212
  const groupAudits = group.audits.reduce((acc, audit) => {
1223
1213
  const auditTitle = link(
1224
1214
  `#${slugify(audit.title)}-${slugify(pluginTitle)}`,
1225
- audit?.title
1215
+ audit.title
1226
1216
  );
1227
- acc += ` ${li(
1217
+ return `${acc} ${li(
1228
1218
  `${getSquaredScoreMarker(audit.score)} ${auditTitle} - ${getAuditResult(
1229
1219
  audit
1230
1220
  )}`
1231
- )}`;
1232
- acc += NEW_LINE;
1233
- return acc;
1221
+ )}${NEW_LINE}`;
1234
1222
  }, "");
1235
1223
  return groupTitle + NEW_LINE + groupAudits;
1236
1224
  }
1237
1225
  function reportToAuditsSection(report) {
1238
- const auditsSection = report.plugins.reduce((acc, plugin) => {
1239
- const auditsData = plugin.audits.reduce((acc2, audit) => {
1226
+ const auditsSection = report.plugins.reduce((pluginAcc, plugin) => {
1227
+ const auditsData = plugin.audits.reduce((auditAcc, audit) => {
1240
1228
  const auditTitle = `${audit.title} (${getPluginNameFromSlug(
1241
1229
  audit.plugin,
1242
1230
  report.plugins
1243
1231
  )})`;
1244
- const detailsTitle = `${getSquaredScoreMarker(
1245
- audit.score
1246
- )} ${getAuditResult(audit, true)} (score: ${formatReportScore(
1247
- audit.score
1248
- )})`;
1249
- const docsItem = getDocsAndDescription(audit);
1250
- acc2 += h3(auditTitle);
1251
- acc2 += NEW_LINE;
1252
- acc2 += NEW_LINE;
1253
- if (!audit.details?.issues?.length) {
1254
- acc2 += detailsTitle;
1255
- acc2 += NEW_LINE;
1256
- acc2 += NEW_LINE;
1257
- acc2 += docsItem;
1258
- return acc2;
1259
- }
1260
- const detailsTableData = [
1261
- detailsTableHeaders,
1262
- ...audit.details.issues.map((issue) => {
1263
- const severity = `${getSeverityIcon(issue.severity)} <i>${issue.severity}</i>`;
1264
- const message = issue.message;
1265
- if (!issue.source) {
1266
- return [severity, message, "", ""];
1267
- }
1268
- const file = `<code>${issue.source?.file}</code>`;
1269
- if (!issue.source.position) {
1270
- return [severity, message, file, ""];
1271
- }
1272
- const { startLine, endLine } = issue.source.position;
1273
- const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
1274
- return [severity, message, file, line];
1275
- })
1276
- ];
1277
- const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
1278
- acc2 += details(detailsTitle, detailsTable);
1279
- acc2 += NEW_LINE;
1280
- acc2 += NEW_LINE;
1281
- acc2 += docsItem;
1282
- return acc2;
1232
+ return auditAcc + h3(auditTitle) + NEW_LINE + NEW_LINE + reportToDetailsSection(audit) + NEW_LINE + NEW_LINE + getDocsAndDescription(audit);
1283
1233
  }, "");
1284
- return acc + auditsData;
1234
+ return pluginAcc + auditsData;
1285
1235
  }, "");
1286
1236
  return h2("\u{1F6E1}\uFE0F Audits") + NEW_LINE + NEW_LINE + auditsSection;
1287
1237
  }
1238
+ function reportToDetailsSection(audit) {
1239
+ const detailsTitle = `${getSquaredScoreMarker(audit.score)} ${getAuditResult(
1240
+ audit,
1241
+ true
1242
+ )} (score: ${formatReportScore(audit.score)})`;
1243
+ if (!audit.details?.issues.length) {
1244
+ return detailsTitle;
1245
+ }
1246
+ const detailsTableData = [
1247
+ detailsTableHeaders,
1248
+ ...audit.details.issues.map((issue) => {
1249
+ const severity = `${getSeverityIcon(issue.severity)} <i>${issue.severity}</i>`;
1250
+ const message = issue.message;
1251
+ if (!issue.source) {
1252
+ return [severity, message, "", ""];
1253
+ }
1254
+ const file = `<code>${issue.source.file}</code>`;
1255
+ if (!issue.source.position) {
1256
+ return [severity, message, file, ""];
1257
+ }
1258
+ const { startLine, endLine } = issue.source.position;
1259
+ const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
1260
+ return [severity, message, file, line];
1261
+ })
1262
+ ];
1263
+ const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
1264
+ return details(detailsTitle, detailsTable);
1265
+ }
1288
1266
  function reportToAboutSection(report, commitData) {
1289
1267
  const date = (/* @__PURE__ */ new Date()).toString();
1290
1268
  const { duration, version, plugins, categories } = report;
@@ -1302,11 +1280,11 @@ function reportToAboutSection(report, commitData) {
1302
1280
  ];
1303
1281
  const pluginMetaTable = [
1304
1282
  pluginMetaTableHeaders,
1305
- ...plugins.map(({ title, version: version2, duration: duration2, audits }) => [
1306
- title,
1307
- audits.length.toString(),
1308
- style(version2 || "", ["c"]),
1309
- formatDuration(duration2)
1283
+ ...plugins.map((plugin) => [
1284
+ plugin.title,
1285
+ plugin.audits.length.toString(),
1286
+ style(plugin.version || "", ["c"]),
1287
+ formatDuration(plugin.duration)
1310
1288
  ])
1311
1289
  ];
1312
1290
  return (
@@ -1341,7 +1319,7 @@ function getAuditResult(audit, isHtml = false) {
1341
1319
  // packages/utils/src/lib/reports/generate-stdout-summary.ts
1342
1320
  import cliui from "@isaacs/cliui";
1343
1321
  import chalk3 from "chalk";
1344
- import Table from "cli-table3";
1322
+ import CliTable3 from "cli-table3";
1345
1323
  function addLine(line = "") {
1346
1324
  return line + NEW_LINE;
1347
1325
  }
@@ -1357,20 +1335,20 @@ function reportToDetailSection(report) {
1357
1335
  return plugins.reduce((acc, plugin) => {
1358
1336
  const { title, audits } = plugin;
1359
1337
  const ui = cliui({ width: TERMINAL_WIDTH });
1360
- audits.forEach(({ score, title: title2, displayValue, value }) => {
1338
+ audits.forEach((audit) => {
1361
1339
  ui.div(
1362
1340
  {
1363
- text: withColor({ score, text: "\u25CF" }),
1341
+ text: withColor({ score: audit.score, text: "\u25CF" }),
1364
1342
  width: 2,
1365
1343
  padding: [0, 1, 0, 0]
1366
1344
  },
1367
1345
  {
1368
- text: title2,
1346
+ text: audit.title,
1369
1347
  // eslint-disable-next-line no-magic-numbers
1370
1348
  padding: [0, 3, 0, 0]
1371
1349
  },
1372
1350
  {
1373
- text: chalk3.cyanBright(displayValue || `${value}`),
1351
+ text: chalk3.cyanBright(audit.displayValue || `${audit.value}`),
1374
1352
  width: 10,
1375
1353
  padding: [0, 0, 0, 0]
1376
1354
  }
@@ -1383,7 +1361,7 @@ function reportToOverviewSection2({
1383
1361
  categories,
1384
1362
  plugins
1385
1363
  }) {
1386
- const table = new Table({
1364
+ const table = new CliTable3({
1387
1365
  head: reportRawOverviewTableHeaders,
1388
1366
  colAligns: ["left", "right", "right"],
1389
1367
  style: {
@@ -1431,16 +1409,7 @@ function distinct(array) {
1431
1409
  return [...new Set(array)];
1432
1410
  }
1433
1411
  function deepClone(obj) {
1434
- if (obj == null || typeof obj !== "object") {
1435
- return obj;
1436
- }
1437
- const cloned = Array.isArray(obj) ? [] : {};
1438
- for (const key in obj) {
1439
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1440
- cloned[key] = deepClone(obj[key]);
1441
- }
1442
- }
1443
- return cloned;
1412
+ return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
1444
1413
  }
1445
1414
  function factorOf(items, filterFn) {
1446
1415
  const itemCount = items.length;
@@ -1505,15 +1474,12 @@ function calculateScore(refs, scoreFn) {
1505
1474
  return numerator / denominator;
1506
1475
  }
1507
1476
  function scoreReport(report) {
1508
- const scoredReport = deepClone(report);
1509
1477
  const allScoredAuditsAndGroups = /* @__PURE__ */ new Map();
1510
- scoredReport.plugins?.forEach((plugin) => {
1511
- const { slug, audits } = plugin;
1512
- const groups = plugin.groups || [];
1513
- audits.forEach((audit) => {
1514
- const key = `${slug}-${audit.slug}-audit`;
1515
- audit.plugin = slug;
1516
- allScoredAuditsAndGroups.set(key, audit);
1478
+ const scoredPlugins = report.plugins.map((plugin) => {
1479
+ const { slug, audits, groups } = plugin;
1480
+ const updatedAudits = audits.map((audit) => ({ ...audit, plugin: slug }));
1481
+ updatedAudits.forEach((audit) => {
1482
+ allScoredAuditsAndGroups.set(`${slug}-${audit.slug}-audit`, audit);
1517
1483
  });
1518
1484
  function groupScoreFn(ref) {
1519
1485
  const score = allScoredAuditsAndGroups.get(
@@ -1526,13 +1492,15 @@ function scoreReport(report) {
1526
1492
  }
1527
1493
  return score;
1528
1494
  }
1529
- groups.forEach((group) => {
1530
- const key = `${slug}-${group.slug}-group`;
1531
- group.score = calculateScore(group.refs, groupScoreFn);
1532
- group.plugin = slug;
1533
- allScoredAuditsAndGroups.set(key, group);
1495
+ const scoredGroups = groups?.map((group) => ({
1496
+ ...group,
1497
+ score: calculateScore(group.refs, groupScoreFn),
1498
+ plugin: slug
1499
+ })) ?? [];
1500
+ scoredGroups.forEach((group) => {
1501
+ allScoredAuditsAndGroups.set(`${slug}-${group.slug}-group`, group);
1534
1502
  });
1535
- plugin.groups = groups;
1503
+ return { ...plugin, audits: updatedAudits, groups: scoredGroups };
1536
1504
  });
1537
1505
  function catScoreFn(ref) {
1538
1506
  const key = `${ref.plugin}-${ref.slug}-${ref.type}`;
@@ -1544,13 +1512,15 @@ function scoreReport(report) {
1544
1512
  }
1545
1513
  return item.score;
1546
1514
  }
1547
- const scoredCategoriesMap = /* @__PURE__ */ new Map();
1548
- for (const category of scoredReport.categories) {
1549
- category.score = calculateScore(category.refs, catScoreFn);
1550
- scoredCategoriesMap.set(category.slug, category);
1551
- }
1552
- scoredReport.categories = [...scoredCategoriesMap.values()];
1553
- return scoredReport;
1515
+ const scoredCategories = report.categories.map((category) => ({
1516
+ ...category,
1517
+ score: calculateScore(category.refs, catScoreFn)
1518
+ }));
1519
+ return {
1520
+ ...deepClone(report),
1521
+ plugins: scoredPlugins,
1522
+ categories: scoredCategories
1523
+ };
1554
1524
  }
1555
1525
 
1556
1526
  // packages/utils/src/lib/reports/sorting.ts
@@ -1573,7 +1543,7 @@ function sortReport(report) {
1573
1543
  );
1574
1544
  const sortedAuditsAndGroups = [
1575
1545
  ...groups,
1576
- ...audits.sort(compareCategoryAudits)
1546
+ ...[...audits].sort(compareCategoryAudits)
1577
1547
  ];
1578
1548
  const sortedRefs = [...category.refs].sort((a, b) => {
1579
1549
  const aIndex = sortedAuditsAndGroups.findIndex(
@@ -1586,7 +1556,14 @@ function sortReport(report) {
1586
1556
  });
1587
1557
  return { ...category, refs: sortedRefs };
1588
1558
  });
1589
- const sortedPlugins = plugins.map((plugin) => ({
1559
+ return {
1560
+ ...report,
1561
+ categories: sortedCategories,
1562
+ plugins: sortPlugins(plugins)
1563
+ };
1564
+ }
1565
+ function sortPlugins(plugins) {
1566
+ return plugins.map((plugin) => ({
1590
1567
  ...plugin,
1591
1568
  audits: [...plugin.audits].sort(compareAudits).map(
1592
1569
  (audit) => audit.details?.issues ? {
@@ -1598,11 +1575,6 @@ function sortReport(report) {
1598
1575
  } : audit
1599
1576
  )
1600
1577
  }));
1601
- return {
1602
- ...report,
1603
- categories: sortedCategories,
1604
- plugins: sortedPlugins
1605
- };
1606
1578
  }
1607
1579
 
1608
1580
  // packages/utils/src/lib/verbose-utils.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-pushup/utils",
3
- "version": "0.8.24",
3
+ "version": "0.9.0",
4
4
  "dependencies": {
5
5
  "@code-pushup/models": "*",
6
6
  "bundle-require": "^4.0.1",