@code-pushup/cli 0.27.1 → 0.28.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 +407 -109
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -504,9 +504,9 @@ var CONFIG_FILE_NAME = "code-pushup.config";
|
|
|
504
504
|
var SUPPORTED_CONFIG_FILE_FORMATS = ["ts", "mjs", "js"];
|
|
505
505
|
|
|
506
506
|
// packages/models/src/lib/implementation/constants.ts
|
|
507
|
-
var
|
|
508
|
-
var
|
|
509
|
-
var
|
|
507
|
+
var DEFAULT_PERSIST_OUTPUT_DIR = ".code-pushup";
|
|
508
|
+
var DEFAULT_PERSIST_FILENAME = "report";
|
|
509
|
+
var DEFAULT_PERSIST_FORMAT = ["json", "md"];
|
|
510
510
|
|
|
511
511
|
// packages/models/src/lib/report.ts
|
|
512
512
|
import { z as z13 } from "zod";
|
|
@@ -718,6 +718,18 @@ import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
|
718
718
|
function slugify(text) {
|
|
719
719
|
return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
|
|
720
720
|
}
|
|
721
|
+
function pluralize(text, amount) {
|
|
722
|
+
if (amount != null && Math.abs(amount) === 1) {
|
|
723
|
+
return text;
|
|
724
|
+
}
|
|
725
|
+
if (text.endsWith("y")) {
|
|
726
|
+
return `${text.slice(0, -1)}ies`;
|
|
727
|
+
}
|
|
728
|
+
if (text.endsWith("s")) {
|
|
729
|
+
return `${text}es`;
|
|
730
|
+
}
|
|
731
|
+
return `${text}s`;
|
|
732
|
+
}
|
|
721
733
|
function formatBytes(bytes, decimals = 2) {
|
|
722
734
|
const positiveBytes = Math.max(bytes, 0);
|
|
723
735
|
if (positiveBytes === 0) {
|
|
@@ -729,6 +741,9 @@ function formatBytes(bytes, decimals = 2) {
|
|
|
729
741
|
const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
|
|
730
742
|
return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
731
743
|
}
|
|
744
|
+
function pluralizeToken(token, times) {
|
|
745
|
+
return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
|
|
746
|
+
}
|
|
732
747
|
function formatDuration(duration) {
|
|
733
748
|
if (duration < 1e3) {
|
|
734
749
|
return `${duration} ms`;
|
|
@@ -955,6 +970,9 @@ function style(text, styles = ["b"]) {
|
|
|
955
970
|
function headline(text, hierarchy = 1) {
|
|
956
971
|
return `${"#".repeat(hierarchy)} ${text}`;
|
|
957
972
|
}
|
|
973
|
+
function h1(text) {
|
|
974
|
+
return headline(text, 1);
|
|
975
|
+
}
|
|
958
976
|
function h2(text) {
|
|
959
977
|
return headline(text, 2);
|
|
960
978
|
}
|
|
@@ -962,6 +980,11 @@ function h3(text) {
|
|
|
962
980
|
return headline(text, 3);
|
|
963
981
|
}
|
|
964
982
|
|
|
983
|
+
// packages/utils/src/lib/reports/md/image.ts
|
|
984
|
+
function image(src, alt) {
|
|
985
|
+
return ``;
|
|
986
|
+
}
|
|
987
|
+
|
|
965
988
|
// packages/utils/src/lib/reports/md/link.ts
|
|
966
989
|
function link2(href, text) {
|
|
967
990
|
return `[${text || href}](${href})`;
|
|
@@ -973,6 +996,11 @@ function li(text, order = "unordered") {
|
|
|
973
996
|
return `${style2} ${text}`;
|
|
974
997
|
}
|
|
975
998
|
|
|
999
|
+
// packages/utils/src/lib/reports/md/paragraphs.ts
|
|
1000
|
+
function paragraphs(...sections) {
|
|
1001
|
+
return sections.filter(Boolean).join("\n\n");
|
|
1002
|
+
}
|
|
1003
|
+
|
|
976
1004
|
// packages/utils/src/lib/reports/md/table.ts
|
|
977
1005
|
var alignString = /* @__PURE__ */ new Map([
|
|
978
1006
|
["l", ":--"],
|
|
@@ -1007,6 +1035,10 @@ function tableHtml(data) {
|
|
|
1007
1035
|
function formatReportScore(score) {
|
|
1008
1036
|
return Math.round(score * 100).toString();
|
|
1009
1037
|
}
|
|
1038
|
+
function formatScoreWithColor(score, options2) {
|
|
1039
|
+
const styledNumber = options2?.skipBold ? formatReportScore(score) : style(formatReportScore(score));
|
|
1040
|
+
return `${getRoundScoreMarker(score)} ${styledNumber}`;
|
|
1041
|
+
}
|
|
1010
1042
|
function getRoundScoreMarker(score) {
|
|
1011
1043
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1012
1044
|
return "\u{1F7E2}";
|
|
@@ -1025,6 +1057,30 @@ function getSquaredScoreMarker(score) {
|
|
|
1025
1057
|
}
|
|
1026
1058
|
return "\u{1F7E5}";
|
|
1027
1059
|
}
|
|
1060
|
+
function getDiffMarker(diff) {
|
|
1061
|
+
if (diff > 0) {
|
|
1062
|
+
return "\u2191";
|
|
1063
|
+
}
|
|
1064
|
+
if (diff < 0) {
|
|
1065
|
+
return "\u2193";
|
|
1066
|
+
}
|
|
1067
|
+
return "";
|
|
1068
|
+
}
|
|
1069
|
+
function colorByScoreDiff(text, diff) {
|
|
1070
|
+
const color = diff > 0 ? "green" : diff < 0 ? "red" : "gray";
|
|
1071
|
+
return shieldsBadge(text, color);
|
|
1072
|
+
}
|
|
1073
|
+
function shieldsBadge(text, color) {
|
|
1074
|
+
return image(
|
|
1075
|
+
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1076
|
+
text
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
function formatDiffNumber(diff) {
|
|
1080
|
+
const number = Math.abs(diff) === Number.POSITIVE_INFINITY ? "\u221E" : `${Math.abs(diff)}`;
|
|
1081
|
+
const sign = diff < 0 ? "\u2212" : "+";
|
|
1082
|
+
return `${sign}${number}`;
|
|
1083
|
+
}
|
|
1028
1084
|
function getSeverityIcon(severity) {
|
|
1029
1085
|
if (severity === "error") {
|
|
1030
1086
|
return "\u{1F6A8}";
|
|
@@ -1035,7 +1091,7 @@ function getSeverityIcon(severity) {
|
|
|
1035
1091
|
return "\u2139\uFE0F";
|
|
1036
1092
|
}
|
|
1037
1093
|
function calcDuration(start, stop) {
|
|
1038
|
-
return Math.
|
|
1094
|
+
return Math.round((stop ?? performance.now()) - start);
|
|
1039
1095
|
}
|
|
1040
1096
|
function countCategoryAudits(refs, plugins) {
|
|
1041
1097
|
const groupLookup = plugins.reduce(
|
|
@@ -1246,6 +1302,9 @@ import { simpleGit } from "simple-git";
|
|
|
1246
1302
|
function toArray(val) {
|
|
1247
1303
|
return Array.isArray(val) ? val : [val];
|
|
1248
1304
|
}
|
|
1305
|
+
function objectToEntries(obj) {
|
|
1306
|
+
return Object.entries(obj);
|
|
1307
|
+
}
|
|
1249
1308
|
function deepClone(obj) {
|
|
1250
1309
|
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
1251
1310
|
}
|
|
@@ -1336,95 +1395,6 @@ function getProgressBar(taskName) {
|
|
|
1336
1395
|
};
|
|
1337
1396
|
}
|
|
1338
1397
|
|
|
1339
|
-
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1340
|
-
import chalk4 from "chalk";
|
|
1341
|
-
function log(msg = "") {
|
|
1342
|
-
ui().logger.log(msg);
|
|
1343
|
-
}
|
|
1344
|
-
function logStdoutSummary(report) {
|
|
1345
|
-
const printCategories = report.categories.length > 0;
|
|
1346
|
-
log(reportToHeaderSection(report));
|
|
1347
|
-
log();
|
|
1348
|
-
logPlugins(report);
|
|
1349
|
-
if (printCategories) {
|
|
1350
|
-
logCategories(report);
|
|
1351
|
-
}
|
|
1352
|
-
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1353
|
-
log();
|
|
1354
|
-
}
|
|
1355
|
-
function reportToHeaderSection(report) {
|
|
1356
|
-
const { packageName, version: version2 } = report;
|
|
1357
|
-
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1358
|
-
}
|
|
1359
|
-
function logPlugins(report) {
|
|
1360
|
-
const { plugins } = report;
|
|
1361
|
-
plugins.forEach((plugin) => {
|
|
1362
|
-
const { title, audits } = plugin;
|
|
1363
|
-
log();
|
|
1364
|
-
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1365
|
-
log();
|
|
1366
|
-
audits.forEach((audit) => {
|
|
1367
|
-
ui().row([
|
|
1368
|
-
{
|
|
1369
|
-
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1370
|
-
width: 2,
|
|
1371
|
-
padding: [0, 1, 0, 0]
|
|
1372
|
-
},
|
|
1373
|
-
{
|
|
1374
|
-
text: audit.title,
|
|
1375
|
-
// eslint-disable-next-line no-magic-numbers
|
|
1376
|
-
padding: [0, 3, 0, 0]
|
|
1377
|
-
},
|
|
1378
|
-
{
|
|
1379
|
-
text: chalk4.cyanBright(audit.displayValue || `${audit.value}`),
|
|
1380
|
-
width: 10,
|
|
1381
|
-
padding: [0, 0, 0, 0]
|
|
1382
|
-
}
|
|
1383
|
-
]);
|
|
1384
|
-
});
|
|
1385
|
-
log();
|
|
1386
|
-
});
|
|
1387
|
-
}
|
|
1388
|
-
function logCategories({ categories, plugins }) {
|
|
1389
|
-
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1390
|
-
const rows = categories.map(({ title, score, refs }) => [
|
|
1391
|
-
title,
|
|
1392
|
-
applyScoreColor({ score }),
|
|
1393
|
-
countCategoryAudits(refs, plugins)
|
|
1394
|
-
]);
|
|
1395
|
-
const table = ui().table();
|
|
1396
|
-
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1397
|
-
table.head(
|
|
1398
|
-
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1399
|
-
content: chalk4.cyan(heading),
|
|
1400
|
-
hAlign: hAlign(idx)
|
|
1401
|
-
}))
|
|
1402
|
-
);
|
|
1403
|
-
rows.forEach(
|
|
1404
|
-
(row) => table.row(
|
|
1405
|
-
row.map((content, idx) => ({
|
|
1406
|
-
content: content.toString(),
|
|
1407
|
-
hAlign: hAlign(idx)
|
|
1408
|
-
}))
|
|
1409
|
-
)
|
|
1410
|
-
);
|
|
1411
|
-
log(chalk4.magentaBright.bold("Categories"));
|
|
1412
|
-
log();
|
|
1413
|
-
table.render();
|
|
1414
|
-
log();
|
|
1415
|
-
}
|
|
1416
|
-
function applyScoreColor({ score, text }) {
|
|
1417
|
-
const formattedScore = text ?? formatReportScore(score);
|
|
1418
|
-
const style2 = text ? chalk4 : chalk4.bold;
|
|
1419
|
-
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1420
|
-
return style2.green(formattedScore);
|
|
1421
|
-
}
|
|
1422
|
-
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1423
|
-
return style2.yellow(formattedScore);
|
|
1424
|
-
}
|
|
1425
|
-
return style2.red(formattedScore);
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
1398
|
// packages/utils/src/lib/reports/flatten-plugins.ts
|
|
1429
1399
|
function listGroupsFromAllPlugins(report) {
|
|
1430
1400
|
return report.plugins.flatMap(
|
|
@@ -1443,7 +1413,7 @@ function generateMdReport(report) {
|
|
|
1443
1413
|
return (
|
|
1444
1414
|
// header section
|
|
1445
1415
|
// eslint-disable-next-line prefer-template
|
|
1446
|
-
|
|
1416
|
+
reportToHeaderSection() + NEW_LINE + // categories overview section
|
|
1447
1417
|
(printCategories ? reportToOverviewSection(report) + NEW_LINE + NEW_LINE : "") + // categories section
|
|
1448
1418
|
(printCategories ? reportToCategoriesSection(report) + NEW_LINE + NEW_LINE : "") + // audits section
|
|
1449
1419
|
reportToAuditsSection(report) + NEW_LINE + NEW_LINE + // about section
|
|
@@ -1451,7 +1421,7 @@ function generateMdReport(report) {
|
|
|
1451
1421
|
`${FOOTER_PREFIX} ${link2(README_LINK, "Code PushUp")}`
|
|
1452
1422
|
);
|
|
1453
1423
|
}
|
|
1454
|
-
function
|
|
1424
|
+
function reportToHeaderSection() {
|
|
1455
1425
|
return headline(reportHeadlineText) + NEW_LINE;
|
|
1456
1426
|
}
|
|
1457
1427
|
function reportToOverviewSection(report) {
|
|
@@ -1624,6 +1594,319 @@ function getAuditResult(audit, isHtml = false) {
|
|
|
1624
1594
|
return isHtml ? `<b>${displayValue || value}</b>` : style(String(displayValue || value));
|
|
1625
1595
|
}
|
|
1626
1596
|
|
|
1597
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1598
|
+
var MAX_ROWS = 100;
|
|
1599
|
+
function generateMdReportsDiff(diff) {
|
|
1600
|
+
return paragraphs(
|
|
1601
|
+
formatDiffHeaderSection(diff),
|
|
1602
|
+
formatDiffCategoriesSection(diff),
|
|
1603
|
+
formatDiffGroupsSection(diff),
|
|
1604
|
+
formatDiffAuditsSection(diff)
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
function formatDiffHeaderSection(diff) {
|
|
1608
|
+
const outcomeTexts = {
|
|
1609
|
+
positive: `\u{1F973} Code PushUp report has ${style("improved")}`,
|
|
1610
|
+
negative: `\u{1F61F} Code PushUp report has ${style("regressed")}`,
|
|
1611
|
+
mixed: `\u{1F928} Code PushUp report has both ${style(
|
|
1612
|
+
"improvements and regressions"
|
|
1613
|
+
)}`,
|
|
1614
|
+
unchanged: `\u{1F610} Code PushUp report is ${style("unchanged")}`
|
|
1615
|
+
};
|
|
1616
|
+
const outcome = mergeDiffOutcomes(
|
|
1617
|
+
changesToDiffOutcomes([
|
|
1618
|
+
...diff.categories.changed,
|
|
1619
|
+
...diff.groups.changed,
|
|
1620
|
+
...diff.audits.changed
|
|
1621
|
+
])
|
|
1622
|
+
);
|
|
1623
|
+
const styleCommit = (commit) => style(commit.hash.slice(0, 7), ["c"]);
|
|
1624
|
+
const styleCommits = (commits) => {
|
|
1625
|
+
const src = styleCommit(commits.before);
|
|
1626
|
+
const tgt = styleCommit(commits.after);
|
|
1627
|
+
return `compared target commit ${tgt} with source commit ${src}`;
|
|
1628
|
+
};
|
|
1629
|
+
return paragraphs(
|
|
1630
|
+
h1("Code PushUp"),
|
|
1631
|
+
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
function formatDiffCategoriesSection(diff) {
|
|
1635
|
+
const { changed, unchanged, added } = diff.categories;
|
|
1636
|
+
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
1637
|
+
const hasChanges = unchanged.length < categoriesCount;
|
|
1638
|
+
if (categoriesCount === 0) {
|
|
1639
|
+
return "";
|
|
1640
|
+
}
|
|
1641
|
+
return paragraphs(
|
|
1642
|
+
h2("\u{1F3F7}\uFE0F Categories"),
|
|
1643
|
+
categoriesCount > 0 && tableMd(
|
|
1644
|
+
[
|
|
1645
|
+
[
|
|
1646
|
+
"\u{1F3F7}\uFE0F Category",
|
|
1647
|
+
hasChanges ? "\u2B50 Current score" : "\u2B50 Score",
|
|
1648
|
+
"\u2B50 Previous score",
|
|
1649
|
+
"\u{1F504} Score change"
|
|
1650
|
+
],
|
|
1651
|
+
...sortChanges(changed).map((category) => [
|
|
1652
|
+
category.title,
|
|
1653
|
+
formatScoreWithColor(category.scores.after),
|
|
1654
|
+
formatScoreWithColor(category.scores.before, { skipBold: true }),
|
|
1655
|
+
formatScoreChange(category.scores.diff)
|
|
1656
|
+
]),
|
|
1657
|
+
...added.map((category) => [
|
|
1658
|
+
category.title,
|
|
1659
|
+
formatScoreWithColor(category.score),
|
|
1660
|
+
style("n/a (\\*)", ["i"]),
|
|
1661
|
+
style("n/a (\\*)", ["i"])
|
|
1662
|
+
]),
|
|
1663
|
+
...unchanged.map((category) => [
|
|
1664
|
+
category.title,
|
|
1665
|
+
formatScoreWithColor(category.score),
|
|
1666
|
+
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1667
|
+
"\u2013"
|
|
1668
|
+
])
|
|
1669
|
+
].map((row) => hasChanges ? row : row.slice(0, 2)),
|
|
1670
|
+
hasChanges ? ["l", "c", "c", "c"] : ["l", "c"]
|
|
1671
|
+
),
|
|
1672
|
+
added.length > 0 && style("(\\*) New category.", ["i"])
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
function formatDiffGroupsSection(diff) {
|
|
1676
|
+
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1677
|
+
return "";
|
|
1678
|
+
}
|
|
1679
|
+
return paragraphs(
|
|
1680
|
+
h2("\u{1F397}\uFE0F Groups"),
|
|
1681
|
+
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1682
|
+
headings: [
|
|
1683
|
+
"\u{1F50C} Plugin",
|
|
1684
|
+
"\u{1F5C3}\uFE0F Group",
|
|
1685
|
+
"\u2B50 Current score",
|
|
1686
|
+
"\u2B50 Previous score",
|
|
1687
|
+
"\u{1F504} Score change"
|
|
1688
|
+
],
|
|
1689
|
+
rows: sortChanges(diff.groups.changed).map((group) => [
|
|
1690
|
+
group.plugin.title,
|
|
1691
|
+
group.title,
|
|
1692
|
+
formatScoreWithColor(group.scores.after),
|
|
1693
|
+
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1694
|
+
formatScoreChange(group.scores.diff)
|
|
1695
|
+
]),
|
|
1696
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1697
|
+
})
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
function formatDiffAuditsSection(diff) {
|
|
1701
|
+
return paragraphs(
|
|
1702
|
+
h2("\u{1F6E1}\uFE0F Audits"),
|
|
1703
|
+
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1704
|
+
headings: [
|
|
1705
|
+
"\u{1F50C} Plugin",
|
|
1706
|
+
"\u{1F6E1}\uFE0F Audit",
|
|
1707
|
+
"\u{1F4CF} Current value",
|
|
1708
|
+
"\u{1F4CF} Previous value",
|
|
1709
|
+
"\u{1F504} Value change"
|
|
1710
|
+
],
|
|
1711
|
+
rows: sortChanges(diff.audits.changed).map((audit) => [
|
|
1712
|
+
audit.plugin.title,
|
|
1713
|
+
audit.title,
|
|
1714
|
+
`${getSquaredScoreMarker(audit.scores.after)} ${style(
|
|
1715
|
+
audit.displayValues.after || audit.values.after.toString()
|
|
1716
|
+
)}`,
|
|
1717
|
+
`${getSquaredScoreMarker(audit.scores.before)} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
1718
|
+
formatValueChange(audit)
|
|
1719
|
+
]),
|
|
1720
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1721
|
+
})
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, table) {
|
|
1725
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details(
|
|
1726
|
+
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1727
|
+
paragraphs(
|
|
1728
|
+
tableMd(
|
|
1729
|
+
[table.headings, ...table.rows.slice(0, MAX_ROWS)],
|
|
1730
|
+
table.align
|
|
1731
|
+
),
|
|
1732
|
+
changed.length > MAX_ROWS && style(
|
|
1733
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1734
|
+
token
|
|
1735
|
+
)} are listed above for brevity.`,
|
|
1736
|
+
["i"]
|
|
1737
|
+
),
|
|
1738
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1739
|
+
)
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
function formatScoreChange(diff) {
|
|
1743
|
+
const marker = getDiffMarker(diff);
|
|
1744
|
+
const text = formatDiffNumber(Math.round(diff * 100));
|
|
1745
|
+
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
1746
|
+
}
|
|
1747
|
+
function formatValueChange({
|
|
1748
|
+
values,
|
|
1749
|
+
scores
|
|
1750
|
+
}) {
|
|
1751
|
+
const marker = getDiffMarker(values.diff);
|
|
1752
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
1753
|
+
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
1754
|
+
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1755
|
+
}
|
|
1756
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1757
|
+
return [
|
|
1758
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
1759
|
+
unchanged.length === 1 ? "is" : "are",
|
|
1760
|
+
"unchanged."
|
|
1761
|
+
].join(" ");
|
|
1762
|
+
}
|
|
1763
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
1764
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1765
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1766
|
+
).map(([outcome, count]) => {
|
|
1767
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1768
|
+
token,
|
|
1769
|
+
count
|
|
1770
|
+
)}`;
|
|
1771
|
+
switch (outcome) {
|
|
1772
|
+
case "positive":
|
|
1773
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
1774
|
+
case "negative":
|
|
1775
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1776
|
+
case "mixed":
|
|
1777
|
+
return `${formattedCount} changed without impacting score`;
|
|
1778
|
+
}
|
|
1779
|
+
}).join(", ");
|
|
1780
|
+
}
|
|
1781
|
+
function sortChanges(changes) {
|
|
1782
|
+
return [...changes].sort(
|
|
1783
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
function changesToDiffOutcomes(changes) {
|
|
1787
|
+
return changes.map((change) => {
|
|
1788
|
+
if (change.scores.diff > 0) {
|
|
1789
|
+
return "positive";
|
|
1790
|
+
}
|
|
1791
|
+
if (change.scores.diff < 0) {
|
|
1792
|
+
return "negative";
|
|
1793
|
+
}
|
|
1794
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
1795
|
+
return "mixed";
|
|
1796
|
+
}
|
|
1797
|
+
return "unchanged";
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
function mergeDiffOutcomes(outcomes) {
|
|
1801
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
1802
|
+
return "unchanged";
|
|
1803
|
+
}
|
|
1804
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
1805
|
+
return "positive";
|
|
1806
|
+
}
|
|
1807
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
1808
|
+
return "negative";
|
|
1809
|
+
}
|
|
1810
|
+
return "mixed";
|
|
1811
|
+
}
|
|
1812
|
+
function countDiffOutcomes(outcomes) {
|
|
1813
|
+
return {
|
|
1814
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
1815
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
1816
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
1817
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1822
|
+
import chalk4 from "chalk";
|
|
1823
|
+
function log(msg = "") {
|
|
1824
|
+
ui().logger.log(msg);
|
|
1825
|
+
}
|
|
1826
|
+
function logStdoutSummary(report) {
|
|
1827
|
+
const printCategories = report.categories.length > 0;
|
|
1828
|
+
log(reportToHeaderSection2(report));
|
|
1829
|
+
log();
|
|
1830
|
+
logPlugins(report);
|
|
1831
|
+
if (printCategories) {
|
|
1832
|
+
logCategories(report);
|
|
1833
|
+
}
|
|
1834
|
+
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1835
|
+
log();
|
|
1836
|
+
}
|
|
1837
|
+
function reportToHeaderSection2(report) {
|
|
1838
|
+
const { packageName, version: version2 } = report;
|
|
1839
|
+
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1840
|
+
}
|
|
1841
|
+
function logPlugins(report) {
|
|
1842
|
+
const { plugins } = report;
|
|
1843
|
+
plugins.forEach((plugin) => {
|
|
1844
|
+
const { title, audits } = plugin;
|
|
1845
|
+
log();
|
|
1846
|
+
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1847
|
+
log();
|
|
1848
|
+
audits.forEach((audit) => {
|
|
1849
|
+
ui().row([
|
|
1850
|
+
{
|
|
1851
|
+
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1852
|
+
width: 2,
|
|
1853
|
+
padding: [0, 1, 0, 0]
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
text: audit.title,
|
|
1857
|
+
// eslint-disable-next-line no-magic-numbers
|
|
1858
|
+
padding: [0, 3, 0, 0]
|
|
1859
|
+
},
|
|
1860
|
+
{
|
|
1861
|
+
text: chalk4.cyanBright(audit.displayValue || `${audit.value}`),
|
|
1862
|
+
width: 10,
|
|
1863
|
+
padding: [0, 0, 0, 0]
|
|
1864
|
+
}
|
|
1865
|
+
]);
|
|
1866
|
+
});
|
|
1867
|
+
log();
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
function logCategories({ categories, plugins }) {
|
|
1871
|
+
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1872
|
+
const rows = categories.map(({ title, score, refs }) => [
|
|
1873
|
+
title,
|
|
1874
|
+
applyScoreColor({ score }),
|
|
1875
|
+
countCategoryAudits(refs, plugins)
|
|
1876
|
+
]);
|
|
1877
|
+
const table = ui().table();
|
|
1878
|
+
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1879
|
+
table.head(
|
|
1880
|
+
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1881
|
+
content: chalk4.cyan(heading),
|
|
1882
|
+
hAlign: hAlign(idx)
|
|
1883
|
+
}))
|
|
1884
|
+
);
|
|
1885
|
+
rows.forEach(
|
|
1886
|
+
(row) => table.row(
|
|
1887
|
+
row.map((content, idx) => ({
|
|
1888
|
+
content: content.toString(),
|
|
1889
|
+
hAlign: hAlign(idx)
|
|
1890
|
+
}))
|
|
1891
|
+
)
|
|
1892
|
+
);
|
|
1893
|
+
log(chalk4.magentaBright.bold("Categories"));
|
|
1894
|
+
log();
|
|
1895
|
+
table.render();
|
|
1896
|
+
log();
|
|
1897
|
+
}
|
|
1898
|
+
function applyScoreColor({ score, text }) {
|
|
1899
|
+
const formattedScore = text ?? formatReportScore(score);
|
|
1900
|
+
const style2 = text ? chalk4 : chalk4.bold;
|
|
1901
|
+
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1902
|
+
return style2.green(formattedScore);
|
|
1903
|
+
}
|
|
1904
|
+
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1905
|
+
return style2.yellow(formattedScore);
|
|
1906
|
+
}
|
|
1907
|
+
return style2.red(formattedScore);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1627
1910
|
// packages/utils/src/lib/reports/scoring.ts
|
|
1628
1911
|
var GroupRefInvalidError = class extends Error {
|
|
1629
1912
|
constructor(auditSlug, pluginSlug) {
|
|
@@ -1783,7 +2066,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
1783
2066
|
|
|
1784
2067
|
// packages/core/package.json
|
|
1785
2068
|
var name = "@code-pushup/core";
|
|
1786
|
-
var version = "0.
|
|
2069
|
+
var version = "0.28.0";
|
|
1787
2070
|
|
|
1788
2071
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
1789
2072
|
import chalk5 from "chalk";
|
|
@@ -2010,6 +2293,7 @@ async function collectAndPersistReports(options2) {
|
|
|
2010
2293
|
|
|
2011
2294
|
// packages/core/src/lib/compare.ts
|
|
2012
2295
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
2296
|
+
import { join as join5 } from "node:path";
|
|
2013
2297
|
|
|
2014
2298
|
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
2015
2299
|
function compareCategories(reports) {
|
|
@@ -2155,7 +2439,8 @@ function pluginAuditPairToDiff({
|
|
|
2155
2439
|
}
|
|
2156
2440
|
|
|
2157
2441
|
// packages/core/src/lib/compare.ts
|
|
2158
|
-
async function compareReportFiles(inputPaths,
|
|
2442
|
+
async function compareReportFiles(inputPaths, persistConfig) {
|
|
2443
|
+
const { outputDir, filename, format } = persistConfig;
|
|
2159
2444
|
const [reportBefore, reportAfter] = await Promise.all([
|
|
2160
2445
|
readJsonFile(inputPaths.before),
|
|
2161
2446
|
readJsonFile(inputPaths.after)
|
|
@@ -2165,7 +2450,15 @@ async function compareReportFiles(inputPaths, outputPath) {
|
|
|
2165
2450
|
after: reportSchema.parse(reportAfter)
|
|
2166
2451
|
};
|
|
2167
2452
|
const reportsDiff = compareReports(reports);
|
|
2168
|
-
|
|
2453
|
+
return Promise.all(
|
|
2454
|
+
format.map(async (fmt) => {
|
|
2455
|
+
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2456
|
+
const content = reportsDiffToFileContent(reportsDiff, fmt);
|
|
2457
|
+
await ensureDirectoryExists(outputDir);
|
|
2458
|
+
await writeFile2(outputPath, content);
|
|
2459
|
+
return outputPath;
|
|
2460
|
+
})
|
|
2461
|
+
);
|
|
2169
2462
|
}
|
|
2170
2463
|
function compareReports(reports) {
|
|
2171
2464
|
const start = performance.now();
|
|
@@ -2190,9 +2483,17 @@ function compareReports(reports) {
|
|
|
2190
2483
|
duration
|
|
2191
2484
|
};
|
|
2192
2485
|
}
|
|
2486
|
+
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2487
|
+
switch (format) {
|
|
2488
|
+
case "json":
|
|
2489
|
+
return JSON.stringify(reportsDiff, null, 2);
|
|
2490
|
+
case "md":
|
|
2491
|
+
return generateMdReportsDiff(reportsDiff);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2193
2494
|
|
|
2194
2495
|
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2195
|
-
import { join as
|
|
2496
|
+
import { join as join6 } from "node:path";
|
|
2196
2497
|
var ConfigPathError = class extends Error {
|
|
2197
2498
|
constructor(configPath) {
|
|
2198
2499
|
super(`Provided path '${configPath}' is not valid.`);
|
|
@@ -2226,7 +2527,7 @@ async function autoloadRc(tsconfig) {
|
|
|
2226
2527
|
);
|
|
2227
2528
|
}
|
|
2228
2529
|
return readRcByPath(
|
|
2229
|
-
|
|
2530
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2230
2531
|
tsconfig
|
|
2231
2532
|
);
|
|
2232
2533
|
}
|
|
@@ -2481,7 +2782,6 @@ function renderUploadAutorunHint() {
|
|
|
2481
2782
|
|
|
2482
2783
|
// packages/cli/src/lib/compare/compare-command.ts
|
|
2483
2784
|
import chalk9 from "chalk";
|
|
2484
|
-
import { join as join6 } from "node:path";
|
|
2485
2785
|
|
|
2486
2786
|
// packages/cli/src/lib/implementation/compare.options.ts
|
|
2487
2787
|
function yargsCompareOptionsDefinition() {
|
|
@@ -2511,12 +2811,10 @@ function yargsCompareCommandObject() {
|
|
|
2511
2811
|
ui().logger.info(chalk9.gray(`Run ${command}...`));
|
|
2512
2812
|
const options2 = args;
|
|
2513
2813
|
const { before, after, persist } = options2;
|
|
2514
|
-
const
|
|
2515
|
-
|
|
2516
|
-
|
|
2814
|
+
const outputPaths = await compareReportFiles({ before, after }, persist);
|
|
2815
|
+
ui().logger.info(
|
|
2816
|
+
`Reports diff written to ${outputPaths.map((path) => chalk9.bold(path)).join(" and ")}`
|
|
2517
2817
|
);
|
|
2518
|
-
await compareReportFiles({ before, after }, outputPath);
|
|
2519
|
-
ui().logger.info(`Reports diff written to ${chalk9.bold(outputPath)}`);
|
|
2520
2818
|
}
|
|
2521
2819
|
};
|
|
2522
2820
|
}
|
|
@@ -2617,9 +2915,9 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
2617
2915
|
return {
|
|
2618
2916
|
...config != null && { config },
|
|
2619
2917
|
persist: {
|
|
2620
|
-
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ??
|
|
2621
|
-
|
|
2622
|
-
|
|
2918
|
+
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ?? DEFAULT_PERSIST_OUTPUT_DIR,
|
|
2919
|
+
filename: cliPersist?.filename ?? rcPersist?.filename ?? DEFAULT_PERSIST_FILENAME,
|
|
2920
|
+
format: cliPersist?.format ?? rcPersist?.format ?? DEFAULT_PERSIST_FORMAT
|
|
2623
2921
|
},
|
|
2624
2922
|
...upload2 != null && { upload: upload2 },
|
|
2625
2923
|
categories: rcCategories ?? [],
|