@code-pushup/core 0.27.1 → 0.29.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 +394 -98
- package/package.json +1 -1
- package/src/lib/compare.d.ts +2 -2
package/index.js
CHANGED
|
@@ -705,6 +705,18 @@ import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
|
705
705
|
function slugify(text) {
|
|
706
706
|
return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
|
|
707
707
|
}
|
|
708
|
+
function pluralize(text, amount) {
|
|
709
|
+
if (amount != null && Math.abs(amount) === 1) {
|
|
710
|
+
return text;
|
|
711
|
+
}
|
|
712
|
+
if (text.endsWith("y")) {
|
|
713
|
+
return `${text.slice(0, -1)}ies`;
|
|
714
|
+
}
|
|
715
|
+
if (text.endsWith("s")) {
|
|
716
|
+
return `${text}es`;
|
|
717
|
+
}
|
|
718
|
+
return `${text}s`;
|
|
719
|
+
}
|
|
708
720
|
function formatBytes(bytes, decimals = 2) {
|
|
709
721
|
const positiveBytes = Math.max(bytes, 0);
|
|
710
722
|
if (positiveBytes === 0) {
|
|
@@ -716,6 +728,9 @@ function formatBytes(bytes, decimals = 2) {
|
|
|
716
728
|
const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
|
|
717
729
|
return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
718
730
|
}
|
|
731
|
+
function pluralizeToken(token, times) {
|
|
732
|
+
return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
|
|
733
|
+
}
|
|
719
734
|
function formatDuration(duration) {
|
|
720
735
|
if (duration < 1e3) {
|
|
721
736
|
return `${duration} ms`;
|
|
@@ -939,6 +954,9 @@ function style(text, styles = ["b"]) {
|
|
|
939
954
|
function headline(text, hierarchy = 1) {
|
|
940
955
|
return `${"#".repeat(hierarchy)} ${text}`;
|
|
941
956
|
}
|
|
957
|
+
function h1(text) {
|
|
958
|
+
return headline(text, 1);
|
|
959
|
+
}
|
|
942
960
|
function h2(text) {
|
|
943
961
|
return headline(text, 2);
|
|
944
962
|
}
|
|
@@ -946,6 +964,11 @@ function h3(text) {
|
|
|
946
964
|
return headline(text, 3);
|
|
947
965
|
}
|
|
948
966
|
|
|
967
|
+
// packages/utils/src/lib/reports/md/image.ts
|
|
968
|
+
function image(src, alt) {
|
|
969
|
+
return ``;
|
|
970
|
+
}
|
|
971
|
+
|
|
949
972
|
// packages/utils/src/lib/reports/md/link.ts
|
|
950
973
|
function link(href, text) {
|
|
951
974
|
return `[${text || href}](${href})`;
|
|
@@ -957,6 +980,11 @@ function li(text, order = "unordered") {
|
|
|
957
980
|
return `${style2} ${text}`;
|
|
958
981
|
}
|
|
959
982
|
|
|
983
|
+
// packages/utils/src/lib/reports/md/paragraphs.ts
|
|
984
|
+
function paragraphs(...sections) {
|
|
985
|
+
return sections.filter(Boolean).join("\n\n");
|
|
986
|
+
}
|
|
987
|
+
|
|
960
988
|
// packages/utils/src/lib/reports/md/table.ts
|
|
961
989
|
var alignString = /* @__PURE__ */ new Map([
|
|
962
990
|
["l", ":--"],
|
|
@@ -991,6 +1019,10 @@ function tableHtml(data) {
|
|
|
991
1019
|
function formatReportScore(score) {
|
|
992
1020
|
return Math.round(score * 100).toString();
|
|
993
1021
|
}
|
|
1022
|
+
function formatScoreWithColor(score, options) {
|
|
1023
|
+
const styledNumber = options?.skipBold ? formatReportScore(score) : style(formatReportScore(score));
|
|
1024
|
+
return `${getRoundScoreMarker(score)} ${styledNumber}`;
|
|
1025
|
+
}
|
|
994
1026
|
function getRoundScoreMarker(score) {
|
|
995
1027
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
996
1028
|
return "\u{1F7E2}";
|
|
@@ -1009,6 +1041,30 @@ function getSquaredScoreMarker(score) {
|
|
|
1009
1041
|
}
|
|
1010
1042
|
return "\u{1F7E5}";
|
|
1011
1043
|
}
|
|
1044
|
+
function getDiffMarker(diff) {
|
|
1045
|
+
if (diff > 0) {
|
|
1046
|
+
return "\u2191";
|
|
1047
|
+
}
|
|
1048
|
+
if (diff < 0) {
|
|
1049
|
+
return "\u2193";
|
|
1050
|
+
}
|
|
1051
|
+
return "";
|
|
1052
|
+
}
|
|
1053
|
+
function colorByScoreDiff(text, diff) {
|
|
1054
|
+
const color = diff > 0 ? "green" : diff < 0 ? "red" : "gray";
|
|
1055
|
+
return shieldsBadge(text, color);
|
|
1056
|
+
}
|
|
1057
|
+
function shieldsBadge(text, color) {
|
|
1058
|
+
return image(
|
|
1059
|
+
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1060
|
+
text
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
function formatDiffNumber(diff) {
|
|
1064
|
+
const number = Math.abs(diff) === Number.POSITIVE_INFINITY ? "\u221E" : `${Math.abs(diff)}`;
|
|
1065
|
+
const sign = diff < 0 ? "\u2212" : "+";
|
|
1066
|
+
return `${sign}${number}`;
|
|
1067
|
+
}
|
|
1012
1068
|
function getSeverityIcon(severity) {
|
|
1013
1069
|
if (severity === "error") {
|
|
1014
1070
|
return "\u{1F6A8}";
|
|
@@ -1019,7 +1075,7 @@ function getSeverityIcon(severity) {
|
|
|
1019
1075
|
return "\u2139\uFE0F";
|
|
1020
1076
|
}
|
|
1021
1077
|
function calcDuration(start, stop) {
|
|
1022
|
-
return Math.
|
|
1078
|
+
return Math.round((stop ?? performance.now()) - start);
|
|
1023
1079
|
}
|
|
1024
1080
|
function countCategoryAudits(refs, plugins) {
|
|
1025
1081
|
const groupLookup = plugins.reduce(
|
|
@@ -1219,6 +1275,9 @@ import { isAbsolute, join as join2, relative } from "node:path";
|
|
|
1219
1275
|
import { simpleGit } from "simple-git";
|
|
1220
1276
|
|
|
1221
1277
|
// packages/utils/src/lib/transform.ts
|
|
1278
|
+
function objectToEntries(obj) {
|
|
1279
|
+
return Object.entries(obj);
|
|
1280
|
+
}
|
|
1222
1281
|
function deepClone(obj) {
|
|
1223
1282
|
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
1224
1283
|
}
|
|
@@ -1309,95 +1368,6 @@ function getProgressBar(taskName) {
|
|
|
1309
1368
|
};
|
|
1310
1369
|
}
|
|
1311
1370
|
|
|
1312
|
-
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1313
|
-
import chalk4 from "chalk";
|
|
1314
|
-
function log(msg = "") {
|
|
1315
|
-
ui().logger.log(msg);
|
|
1316
|
-
}
|
|
1317
|
-
function logStdoutSummary(report) {
|
|
1318
|
-
const printCategories = report.categories.length > 0;
|
|
1319
|
-
log(reportToHeaderSection(report));
|
|
1320
|
-
log();
|
|
1321
|
-
logPlugins(report);
|
|
1322
|
-
if (printCategories) {
|
|
1323
|
-
logCategories(report);
|
|
1324
|
-
}
|
|
1325
|
-
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1326
|
-
log();
|
|
1327
|
-
}
|
|
1328
|
-
function reportToHeaderSection(report) {
|
|
1329
|
-
const { packageName, version: version2 } = report;
|
|
1330
|
-
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1331
|
-
}
|
|
1332
|
-
function logPlugins(report) {
|
|
1333
|
-
const { plugins } = report;
|
|
1334
|
-
plugins.forEach((plugin) => {
|
|
1335
|
-
const { title, audits } = plugin;
|
|
1336
|
-
log();
|
|
1337
|
-
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1338
|
-
log();
|
|
1339
|
-
audits.forEach((audit) => {
|
|
1340
|
-
ui().row([
|
|
1341
|
-
{
|
|
1342
|
-
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1343
|
-
width: 2,
|
|
1344
|
-
padding: [0, 1, 0, 0]
|
|
1345
|
-
},
|
|
1346
|
-
{
|
|
1347
|
-
text: audit.title,
|
|
1348
|
-
// eslint-disable-next-line no-magic-numbers
|
|
1349
|
-
padding: [0, 3, 0, 0]
|
|
1350
|
-
},
|
|
1351
|
-
{
|
|
1352
|
-
text: chalk4.cyanBright(audit.displayValue || `${audit.value}`),
|
|
1353
|
-
width: 10,
|
|
1354
|
-
padding: [0, 0, 0, 0]
|
|
1355
|
-
}
|
|
1356
|
-
]);
|
|
1357
|
-
});
|
|
1358
|
-
log();
|
|
1359
|
-
});
|
|
1360
|
-
}
|
|
1361
|
-
function logCategories({ categories, plugins }) {
|
|
1362
|
-
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1363
|
-
const rows = categories.map(({ title, score, refs }) => [
|
|
1364
|
-
title,
|
|
1365
|
-
applyScoreColor({ score }),
|
|
1366
|
-
countCategoryAudits(refs, plugins)
|
|
1367
|
-
]);
|
|
1368
|
-
const table = ui().table();
|
|
1369
|
-
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1370
|
-
table.head(
|
|
1371
|
-
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1372
|
-
content: chalk4.cyan(heading),
|
|
1373
|
-
hAlign: hAlign(idx)
|
|
1374
|
-
}))
|
|
1375
|
-
);
|
|
1376
|
-
rows.forEach(
|
|
1377
|
-
(row) => table.row(
|
|
1378
|
-
row.map((content, idx) => ({
|
|
1379
|
-
content: content.toString(),
|
|
1380
|
-
hAlign: hAlign(idx)
|
|
1381
|
-
}))
|
|
1382
|
-
)
|
|
1383
|
-
);
|
|
1384
|
-
log(chalk4.magentaBright.bold("Categories"));
|
|
1385
|
-
log();
|
|
1386
|
-
table.render();
|
|
1387
|
-
log();
|
|
1388
|
-
}
|
|
1389
|
-
function applyScoreColor({ score, text }) {
|
|
1390
|
-
const formattedScore = text ?? formatReportScore(score);
|
|
1391
|
-
const style2 = text ? chalk4 : chalk4.bold;
|
|
1392
|
-
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1393
|
-
return style2.green(formattedScore);
|
|
1394
|
-
}
|
|
1395
|
-
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1396
|
-
return style2.yellow(formattedScore);
|
|
1397
|
-
}
|
|
1398
|
-
return style2.red(formattedScore);
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
1371
|
// packages/utils/src/lib/reports/flatten-plugins.ts
|
|
1402
1372
|
function listGroupsFromAllPlugins(report) {
|
|
1403
1373
|
return report.plugins.flatMap(
|
|
@@ -1416,7 +1386,7 @@ function generateMdReport(report) {
|
|
|
1416
1386
|
return (
|
|
1417
1387
|
// header section
|
|
1418
1388
|
// eslint-disable-next-line prefer-template
|
|
1419
|
-
|
|
1389
|
+
reportToHeaderSection() + NEW_LINE + // categories overview section
|
|
1420
1390
|
(printCategories ? reportToOverviewSection(report) + NEW_LINE + NEW_LINE : "") + // categories section
|
|
1421
1391
|
(printCategories ? reportToCategoriesSection(report) + NEW_LINE + NEW_LINE : "") + // audits section
|
|
1422
1392
|
reportToAuditsSection(report) + NEW_LINE + NEW_LINE + // about section
|
|
@@ -1424,7 +1394,7 @@ function generateMdReport(report) {
|
|
|
1424
1394
|
`${FOOTER_PREFIX} ${link(README_LINK, "Code PushUp")}`
|
|
1425
1395
|
);
|
|
1426
1396
|
}
|
|
1427
|
-
function
|
|
1397
|
+
function reportToHeaderSection() {
|
|
1428
1398
|
return headline(reportHeadlineText) + NEW_LINE;
|
|
1429
1399
|
}
|
|
1430
1400
|
function reportToOverviewSection(report) {
|
|
@@ -1547,7 +1517,7 @@ function reportToDetailsSection(audit) {
|
|
|
1547
1517
|
function reportToAboutSection(report) {
|
|
1548
1518
|
const date = formatDate(/* @__PURE__ */ new Date());
|
|
1549
1519
|
const { duration, version: version2, commit, plugins, categories } = report;
|
|
1550
|
-
const commitInfo = commit ? `${commit.message} (${commit.hash
|
|
1520
|
+
const commitInfo = commit ? `${commit.message} (${commit.hash})` : "N/A";
|
|
1551
1521
|
const reportMetaTable = [
|
|
1552
1522
|
reportMetaTableHeaders,
|
|
1553
1523
|
[
|
|
@@ -1597,6 +1567,314 @@ function getAuditResult(audit, isHtml = false) {
|
|
|
1597
1567
|
return isHtml ? `<b>${displayValue || value}</b>` : style(String(displayValue || value));
|
|
1598
1568
|
}
|
|
1599
1569
|
|
|
1570
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1571
|
+
var MAX_ROWS = 100;
|
|
1572
|
+
function generateMdReportsDiff(diff) {
|
|
1573
|
+
return paragraphs(
|
|
1574
|
+
formatDiffHeaderSection(diff),
|
|
1575
|
+
formatDiffCategoriesSection(diff),
|
|
1576
|
+
formatDiffGroupsSection(diff),
|
|
1577
|
+
formatDiffAuditsSection(diff)
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
function formatDiffHeaderSection(diff) {
|
|
1581
|
+
const outcomeTexts = {
|
|
1582
|
+
positive: `\u{1F973} Code PushUp report has ${style("improved")}`,
|
|
1583
|
+
negative: `\u{1F61F} Code PushUp report has ${style("regressed")}`,
|
|
1584
|
+
mixed: `\u{1F928} Code PushUp report has both ${style(
|
|
1585
|
+
"improvements and regressions"
|
|
1586
|
+
)}`,
|
|
1587
|
+
unchanged: `\u{1F610} Code PushUp report is ${style("unchanged")}`
|
|
1588
|
+
};
|
|
1589
|
+
const outcome = mergeDiffOutcomes(
|
|
1590
|
+
changesToDiffOutcomes([
|
|
1591
|
+
...diff.categories.changed,
|
|
1592
|
+
...diff.groups.changed,
|
|
1593
|
+
...diff.audits.changed
|
|
1594
|
+
])
|
|
1595
|
+
);
|
|
1596
|
+
const styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
1597
|
+
return paragraphs(
|
|
1598
|
+
h1("Code PushUp"),
|
|
1599
|
+
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
function formatDiffCategoriesSection(diff) {
|
|
1603
|
+
const { changed, unchanged, added } = diff.categories;
|
|
1604
|
+
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
1605
|
+
const hasChanges = unchanged.length < categoriesCount;
|
|
1606
|
+
if (categoriesCount === 0) {
|
|
1607
|
+
return "";
|
|
1608
|
+
}
|
|
1609
|
+
return paragraphs(
|
|
1610
|
+
h2("\u{1F3F7}\uFE0F Categories"),
|
|
1611
|
+
categoriesCount > 0 && tableMd(
|
|
1612
|
+
[
|
|
1613
|
+
[
|
|
1614
|
+
"\u{1F3F7}\uFE0F Category",
|
|
1615
|
+
hasChanges ? "\u2B50 Current score" : "\u2B50 Score",
|
|
1616
|
+
"\u2B50 Previous score",
|
|
1617
|
+
"\u{1F504} Score change"
|
|
1618
|
+
],
|
|
1619
|
+
...sortChanges(changed).map((category) => [
|
|
1620
|
+
category.title,
|
|
1621
|
+
formatScoreWithColor(category.scores.after),
|
|
1622
|
+
formatScoreWithColor(category.scores.before, { skipBold: true }),
|
|
1623
|
+
formatScoreChange(category.scores.diff)
|
|
1624
|
+
]),
|
|
1625
|
+
...added.map((category) => [
|
|
1626
|
+
category.title,
|
|
1627
|
+
formatScoreWithColor(category.score),
|
|
1628
|
+
style("n/a (\\*)", ["i"]),
|
|
1629
|
+
style("n/a (\\*)", ["i"])
|
|
1630
|
+
]),
|
|
1631
|
+
...unchanged.map((category) => [
|
|
1632
|
+
category.title,
|
|
1633
|
+
formatScoreWithColor(category.score),
|
|
1634
|
+
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1635
|
+
"\u2013"
|
|
1636
|
+
])
|
|
1637
|
+
].map((row) => hasChanges ? row : row.slice(0, 2)),
|
|
1638
|
+
hasChanges ? ["l", "c", "c", "c"] : ["l", "c"]
|
|
1639
|
+
),
|
|
1640
|
+
added.length > 0 && style("(\\*) New category.", ["i"])
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
function formatDiffGroupsSection(diff) {
|
|
1644
|
+
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1645
|
+
return "";
|
|
1646
|
+
}
|
|
1647
|
+
return paragraphs(
|
|
1648
|
+
h2("\u{1F397}\uFE0F Groups"),
|
|
1649
|
+
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1650
|
+
headings: [
|
|
1651
|
+
"\u{1F50C} Plugin",
|
|
1652
|
+
"\u{1F5C3}\uFE0F Group",
|
|
1653
|
+
"\u2B50 Current score",
|
|
1654
|
+
"\u2B50 Previous score",
|
|
1655
|
+
"\u{1F504} Score change"
|
|
1656
|
+
],
|
|
1657
|
+
rows: sortChanges(diff.groups.changed).map((group) => [
|
|
1658
|
+
group.plugin.title,
|
|
1659
|
+
group.title,
|
|
1660
|
+
formatScoreWithColor(group.scores.after),
|
|
1661
|
+
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1662
|
+
formatScoreChange(group.scores.diff)
|
|
1663
|
+
]),
|
|
1664
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1665
|
+
})
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
function formatDiffAuditsSection(diff) {
|
|
1669
|
+
return paragraphs(
|
|
1670
|
+
h2("\u{1F6E1}\uFE0F Audits"),
|
|
1671
|
+
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1672
|
+
headings: [
|
|
1673
|
+
"\u{1F50C} Plugin",
|
|
1674
|
+
"\u{1F6E1}\uFE0F Audit",
|
|
1675
|
+
"\u{1F4CF} Current value",
|
|
1676
|
+
"\u{1F4CF} Previous value",
|
|
1677
|
+
"\u{1F504} Value change"
|
|
1678
|
+
],
|
|
1679
|
+
rows: sortChanges(diff.audits.changed).map((audit) => [
|
|
1680
|
+
audit.plugin.title,
|
|
1681
|
+
audit.title,
|
|
1682
|
+
`${getSquaredScoreMarker(audit.scores.after)} ${style(
|
|
1683
|
+
audit.displayValues.after || audit.values.after.toString()
|
|
1684
|
+
)}`,
|
|
1685
|
+
`${getSquaredScoreMarker(audit.scores.before)} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
1686
|
+
formatValueChange(audit)
|
|
1687
|
+
]),
|
|
1688
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1689
|
+
})
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, table) {
|
|
1693
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details(
|
|
1694
|
+
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1695
|
+
paragraphs(
|
|
1696
|
+
tableMd(
|
|
1697
|
+
[table.headings, ...table.rows.slice(0, MAX_ROWS)],
|
|
1698
|
+
table.align
|
|
1699
|
+
),
|
|
1700
|
+
changed.length > MAX_ROWS && style(
|
|
1701
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1702
|
+
token
|
|
1703
|
+
)} are listed above for brevity.`,
|
|
1704
|
+
["i"]
|
|
1705
|
+
),
|
|
1706
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1707
|
+
)
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
function formatScoreChange(diff) {
|
|
1711
|
+
const marker = getDiffMarker(diff);
|
|
1712
|
+
const text = formatDiffNumber(Math.round(diff * 100));
|
|
1713
|
+
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
1714
|
+
}
|
|
1715
|
+
function formatValueChange({
|
|
1716
|
+
values,
|
|
1717
|
+
scores
|
|
1718
|
+
}) {
|
|
1719
|
+
const marker = getDiffMarker(values.diff);
|
|
1720
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
1721
|
+
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
1722
|
+
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1723
|
+
}
|
|
1724
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1725
|
+
return [
|
|
1726
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
1727
|
+
unchanged.length === 1 ? "is" : "are",
|
|
1728
|
+
"unchanged."
|
|
1729
|
+
].join(" ");
|
|
1730
|
+
}
|
|
1731
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
1732
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1733
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1734
|
+
).map(([outcome, count]) => {
|
|
1735
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1736
|
+
token,
|
|
1737
|
+
count
|
|
1738
|
+
)}`;
|
|
1739
|
+
switch (outcome) {
|
|
1740
|
+
case "positive":
|
|
1741
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
1742
|
+
case "negative":
|
|
1743
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1744
|
+
case "mixed":
|
|
1745
|
+
return `${formattedCount} changed without impacting score`;
|
|
1746
|
+
}
|
|
1747
|
+
}).join(", ");
|
|
1748
|
+
}
|
|
1749
|
+
function sortChanges(changes) {
|
|
1750
|
+
return [...changes].sort(
|
|
1751
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
function changesToDiffOutcomes(changes) {
|
|
1755
|
+
return changes.map((change) => {
|
|
1756
|
+
if (change.scores.diff > 0) {
|
|
1757
|
+
return "positive";
|
|
1758
|
+
}
|
|
1759
|
+
if (change.scores.diff < 0) {
|
|
1760
|
+
return "negative";
|
|
1761
|
+
}
|
|
1762
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
1763
|
+
return "mixed";
|
|
1764
|
+
}
|
|
1765
|
+
return "unchanged";
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
function mergeDiffOutcomes(outcomes) {
|
|
1769
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
1770
|
+
return "unchanged";
|
|
1771
|
+
}
|
|
1772
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
1773
|
+
return "positive";
|
|
1774
|
+
}
|
|
1775
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
1776
|
+
return "negative";
|
|
1777
|
+
}
|
|
1778
|
+
return "mixed";
|
|
1779
|
+
}
|
|
1780
|
+
function countDiffOutcomes(outcomes) {
|
|
1781
|
+
return {
|
|
1782
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
1783
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
1784
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
1785
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1790
|
+
import chalk4 from "chalk";
|
|
1791
|
+
function log(msg = "") {
|
|
1792
|
+
ui().logger.log(msg);
|
|
1793
|
+
}
|
|
1794
|
+
function logStdoutSummary(report) {
|
|
1795
|
+
const printCategories = report.categories.length > 0;
|
|
1796
|
+
log(reportToHeaderSection2(report));
|
|
1797
|
+
log();
|
|
1798
|
+
logPlugins(report);
|
|
1799
|
+
if (printCategories) {
|
|
1800
|
+
logCategories(report);
|
|
1801
|
+
}
|
|
1802
|
+
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1803
|
+
log();
|
|
1804
|
+
}
|
|
1805
|
+
function reportToHeaderSection2(report) {
|
|
1806
|
+
const { packageName, version: version2 } = report;
|
|
1807
|
+
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1808
|
+
}
|
|
1809
|
+
function logPlugins(report) {
|
|
1810
|
+
const { plugins } = report;
|
|
1811
|
+
plugins.forEach((plugin) => {
|
|
1812
|
+
const { title, audits } = plugin;
|
|
1813
|
+
log();
|
|
1814
|
+
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1815
|
+
log();
|
|
1816
|
+
audits.forEach((audit) => {
|
|
1817
|
+
ui().row([
|
|
1818
|
+
{
|
|
1819
|
+
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1820
|
+
width: 2,
|
|
1821
|
+
padding: [0, 1, 0, 0]
|
|
1822
|
+
},
|
|
1823
|
+
{
|
|
1824
|
+
text: audit.title,
|
|
1825
|
+
// eslint-disable-next-line no-magic-numbers
|
|
1826
|
+
padding: [0, 3, 0, 0]
|
|
1827
|
+
},
|
|
1828
|
+
{
|
|
1829
|
+
text: chalk4.cyanBright(audit.displayValue || `${audit.value}`),
|
|
1830
|
+
width: 10,
|
|
1831
|
+
padding: [0, 0, 0, 0]
|
|
1832
|
+
}
|
|
1833
|
+
]);
|
|
1834
|
+
});
|
|
1835
|
+
log();
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
function logCategories({ categories, plugins }) {
|
|
1839
|
+
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1840
|
+
const rows = categories.map(({ title, score, refs }) => [
|
|
1841
|
+
title,
|
|
1842
|
+
applyScoreColor({ score }),
|
|
1843
|
+
countCategoryAudits(refs, plugins)
|
|
1844
|
+
]);
|
|
1845
|
+
const table = ui().table();
|
|
1846
|
+
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1847
|
+
table.head(
|
|
1848
|
+
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1849
|
+
content: chalk4.cyan(heading),
|
|
1850
|
+
hAlign: hAlign(idx)
|
|
1851
|
+
}))
|
|
1852
|
+
);
|
|
1853
|
+
rows.forEach(
|
|
1854
|
+
(row) => table.row(
|
|
1855
|
+
row.map((content, idx) => ({
|
|
1856
|
+
content: content.toString(),
|
|
1857
|
+
hAlign: hAlign(idx)
|
|
1858
|
+
}))
|
|
1859
|
+
)
|
|
1860
|
+
);
|
|
1861
|
+
log(chalk4.magentaBright.bold("Categories"));
|
|
1862
|
+
log();
|
|
1863
|
+
table.render();
|
|
1864
|
+
log();
|
|
1865
|
+
}
|
|
1866
|
+
function applyScoreColor({ score, text }) {
|
|
1867
|
+
const formattedScore = text ?? formatReportScore(score);
|
|
1868
|
+
const style2 = text ? chalk4 : chalk4.bold;
|
|
1869
|
+
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1870
|
+
return style2.green(formattedScore);
|
|
1871
|
+
}
|
|
1872
|
+
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1873
|
+
return style2.yellow(formattedScore);
|
|
1874
|
+
}
|
|
1875
|
+
return style2.red(formattedScore);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1600
1878
|
// packages/utils/src/lib/reports/scoring.ts
|
|
1601
1879
|
var GroupRefInvalidError = class extends Error {
|
|
1602
1880
|
constructor(auditSlug, pluginSlug) {
|
|
@@ -1756,7 +2034,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
1756
2034
|
|
|
1757
2035
|
// packages/core/package.json
|
|
1758
2036
|
var name = "@code-pushup/core";
|
|
1759
|
-
var version = "0.
|
|
2037
|
+
var version = "0.29.0";
|
|
1760
2038
|
|
|
1761
2039
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
1762
2040
|
import chalk5 from "chalk";
|
|
@@ -1983,6 +2261,7 @@ async function collectAndPersistReports(options) {
|
|
|
1983
2261
|
|
|
1984
2262
|
// packages/core/src/lib/compare.ts
|
|
1985
2263
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
2264
|
+
import { join as join5 } from "node:path";
|
|
1986
2265
|
|
|
1987
2266
|
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
1988
2267
|
function compareCategories(reports) {
|
|
@@ -2128,7 +2407,8 @@ function pluginAuditPairToDiff({
|
|
|
2128
2407
|
}
|
|
2129
2408
|
|
|
2130
2409
|
// packages/core/src/lib/compare.ts
|
|
2131
|
-
async function compareReportFiles(inputPaths,
|
|
2410
|
+
async function compareReportFiles(inputPaths, persistConfig) {
|
|
2411
|
+
const { outputDir, filename, format } = persistConfig;
|
|
2132
2412
|
const [reportBefore, reportAfter] = await Promise.all([
|
|
2133
2413
|
readJsonFile(inputPaths.before),
|
|
2134
2414
|
readJsonFile(inputPaths.after)
|
|
@@ -2138,7 +2418,15 @@ async function compareReportFiles(inputPaths, outputPath) {
|
|
|
2138
2418
|
after: reportSchema.parse(reportAfter)
|
|
2139
2419
|
};
|
|
2140
2420
|
const reportsDiff = compareReports(reports);
|
|
2141
|
-
|
|
2421
|
+
return Promise.all(
|
|
2422
|
+
format.map(async (fmt) => {
|
|
2423
|
+
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2424
|
+
const content = reportsDiffToFileContent(reportsDiff, fmt);
|
|
2425
|
+
await ensureDirectoryExists(outputDir);
|
|
2426
|
+
await writeFile2(outputPath, content);
|
|
2427
|
+
return outputPath;
|
|
2428
|
+
})
|
|
2429
|
+
);
|
|
2142
2430
|
}
|
|
2143
2431
|
function compareReports(reports) {
|
|
2144
2432
|
const start = performance.now();
|
|
@@ -2163,9 +2451,17 @@ function compareReports(reports) {
|
|
|
2163
2451
|
duration
|
|
2164
2452
|
};
|
|
2165
2453
|
}
|
|
2454
|
+
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2455
|
+
switch (format) {
|
|
2456
|
+
case "json":
|
|
2457
|
+
return JSON.stringify(reportsDiff, null, 2);
|
|
2458
|
+
case "md":
|
|
2459
|
+
return generateMdReportsDiff(reportsDiff);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2166
2462
|
|
|
2167
2463
|
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2168
|
-
import { join as
|
|
2464
|
+
import { join as join6 } from "node:path";
|
|
2169
2465
|
var ConfigPathError = class extends Error {
|
|
2170
2466
|
constructor(configPath) {
|
|
2171
2467
|
super(`Provided path '${configPath}' is not valid.`);
|
|
@@ -2199,7 +2495,7 @@ async function autoloadRc(tsconfig) {
|
|
|
2199
2495
|
);
|
|
2200
2496
|
}
|
|
2201
2497
|
return readRcByPath(
|
|
2202
|
-
|
|
2498
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2203
2499
|
tsconfig
|
|
2204
2500
|
);
|
|
2205
2501
|
}
|
package/package.json
CHANGED
package/src/lib/compare.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Report, ReportsDiff } from '@code-pushup/models';
|
|
1
|
+
import { type PersistConfig, Report, ReportsDiff } from '@code-pushup/models';
|
|
2
2
|
import { Diff } from '@code-pushup/utils';
|
|
3
|
-
export declare function compareReportFiles(inputPaths: Diff<string>,
|
|
3
|
+
export declare function compareReportFiles(inputPaths: Diff<string>, persistConfig: Required<PersistConfig>): Promise<string[]>;
|
|
4
4
|
export declare function compareReports(reports: Diff<Report>): ReportsDiff;
|