@code-pushup/cli 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 +403 -110
- 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) {
|
|
@@ -1574,7 +1544,7 @@ function reportToDetailsSection(audit) {
|
|
|
1574
1544
|
function reportToAboutSection(report) {
|
|
1575
1545
|
const date = formatDate(/* @__PURE__ */ new Date());
|
|
1576
1546
|
const { duration, version: version2, commit, plugins, categories } = report;
|
|
1577
|
-
const commitInfo = commit ? `${commit.message} (${commit.hash
|
|
1547
|
+
const commitInfo = commit ? `${commit.message} (${commit.hash})` : "N/A";
|
|
1578
1548
|
const reportMetaTable = [
|
|
1579
1549
|
reportMetaTableHeaders,
|
|
1580
1550
|
[
|
|
@@ -1624,6 +1594,314 @@ 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 styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
1624
|
+
return paragraphs(
|
|
1625
|
+
h1("Code PushUp"),
|
|
1626
|
+
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
function formatDiffCategoriesSection(diff) {
|
|
1630
|
+
const { changed, unchanged, added } = diff.categories;
|
|
1631
|
+
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
1632
|
+
const hasChanges = unchanged.length < categoriesCount;
|
|
1633
|
+
if (categoriesCount === 0) {
|
|
1634
|
+
return "";
|
|
1635
|
+
}
|
|
1636
|
+
return paragraphs(
|
|
1637
|
+
h2("\u{1F3F7}\uFE0F Categories"),
|
|
1638
|
+
categoriesCount > 0 && tableMd(
|
|
1639
|
+
[
|
|
1640
|
+
[
|
|
1641
|
+
"\u{1F3F7}\uFE0F Category",
|
|
1642
|
+
hasChanges ? "\u2B50 Current score" : "\u2B50 Score",
|
|
1643
|
+
"\u2B50 Previous score",
|
|
1644
|
+
"\u{1F504} Score change"
|
|
1645
|
+
],
|
|
1646
|
+
...sortChanges(changed).map((category) => [
|
|
1647
|
+
category.title,
|
|
1648
|
+
formatScoreWithColor(category.scores.after),
|
|
1649
|
+
formatScoreWithColor(category.scores.before, { skipBold: true }),
|
|
1650
|
+
formatScoreChange(category.scores.diff)
|
|
1651
|
+
]),
|
|
1652
|
+
...added.map((category) => [
|
|
1653
|
+
category.title,
|
|
1654
|
+
formatScoreWithColor(category.score),
|
|
1655
|
+
style("n/a (\\*)", ["i"]),
|
|
1656
|
+
style("n/a (\\*)", ["i"])
|
|
1657
|
+
]),
|
|
1658
|
+
...unchanged.map((category) => [
|
|
1659
|
+
category.title,
|
|
1660
|
+
formatScoreWithColor(category.score),
|
|
1661
|
+
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1662
|
+
"\u2013"
|
|
1663
|
+
])
|
|
1664
|
+
].map((row) => hasChanges ? row : row.slice(0, 2)),
|
|
1665
|
+
hasChanges ? ["l", "c", "c", "c"] : ["l", "c"]
|
|
1666
|
+
),
|
|
1667
|
+
added.length > 0 && style("(\\*) New category.", ["i"])
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
function formatDiffGroupsSection(diff) {
|
|
1671
|
+
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1672
|
+
return "";
|
|
1673
|
+
}
|
|
1674
|
+
return paragraphs(
|
|
1675
|
+
h2("\u{1F397}\uFE0F Groups"),
|
|
1676
|
+
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1677
|
+
headings: [
|
|
1678
|
+
"\u{1F50C} Plugin",
|
|
1679
|
+
"\u{1F5C3}\uFE0F Group",
|
|
1680
|
+
"\u2B50 Current score",
|
|
1681
|
+
"\u2B50 Previous score",
|
|
1682
|
+
"\u{1F504} Score change"
|
|
1683
|
+
],
|
|
1684
|
+
rows: sortChanges(diff.groups.changed).map((group) => [
|
|
1685
|
+
group.plugin.title,
|
|
1686
|
+
group.title,
|
|
1687
|
+
formatScoreWithColor(group.scores.after),
|
|
1688
|
+
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1689
|
+
formatScoreChange(group.scores.diff)
|
|
1690
|
+
]),
|
|
1691
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1692
|
+
})
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
function formatDiffAuditsSection(diff) {
|
|
1696
|
+
return paragraphs(
|
|
1697
|
+
h2("\u{1F6E1}\uFE0F Audits"),
|
|
1698
|
+
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1699
|
+
headings: [
|
|
1700
|
+
"\u{1F50C} Plugin",
|
|
1701
|
+
"\u{1F6E1}\uFE0F Audit",
|
|
1702
|
+
"\u{1F4CF} Current value",
|
|
1703
|
+
"\u{1F4CF} Previous value",
|
|
1704
|
+
"\u{1F504} Value change"
|
|
1705
|
+
],
|
|
1706
|
+
rows: sortChanges(diff.audits.changed).map((audit) => [
|
|
1707
|
+
audit.plugin.title,
|
|
1708
|
+
audit.title,
|
|
1709
|
+
`${getSquaredScoreMarker(audit.scores.after)} ${style(
|
|
1710
|
+
audit.displayValues.after || audit.values.after.toString()
|
|
1711
|
+
)}`,
|
|
1712
|
+
`${getSquaredScoreMarker(audit.scores.before)} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
1713
|
+
formatValueChange(audit)
|
|
1714
|
+
]),
|
|
1715
|
+
align: ["l", "l", "c", "c", "c"]
|
|
1716
|
+
})
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, table) {
|
|
1720
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details(
|
|
1721
|
+
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1722
|
+
paragraphs(
|
|
1723
|
+
tableMd(
|
|
1724
|
+
[table.headings, ...table.rows.slice(0, MAX_ROWS)],
|
|
1725
|
+
table.align
|
|
1726
|
+
),
|
|
1727
|
+
changed.length > MAX_ROWS && style(
|
|
1728
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1729
|
+
token
|
|
1730
|
+
)} are listed above for brevity.`,
|
|
1731
|
+
["i"]
|
|
1732
|
+
),
|
|
1733
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1734
|
+
)
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
function formatScoreChange(diff) {
|
|
1738
|
+
const marker = getDiffMarker(diff);
|
|
1739
|
+
const text = formatDiffNumber(Math.round(diff * 100));
|
|
1740
|
+
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
1741
|
+
}
|
|
1742
|
+
function formatValueChange({
|
|
1743
|
+
values,
|
|
1744
|
+
scores
|
|
1745
|
+
}) {
|
|
1746
|
+
const marker = getDiffMarker(values.diff);
|
|
1747
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
1748
|
+
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
1749
|
+
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1750
|
+
}
|
|
1751
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1752
|
+
return [
|
|
1753
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
1754
|
+
unchanged.length === 1 ? "is" : "are",
|
|
1755
|
+
"unchanged."
|
|
1756
|
+
].join(" ");
|
|
1757
|
+
}
|
|
1758
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
1759
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1760
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1761
|
+
).map(([outcome, count]) => {
|
|
1762
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1763
|
+
token,
|
|
1764
|
+
count
|
|
1765
|
+
)}`;
|
|
1766
|
+
switch (outcome) {
|
|
1767
|
+
case "positive":
|
|
1768
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
1769
|
+
case "negative":
|
|
1770
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1771
|
+
case "mixed":
|
|
1772
|
+
return `${formattedCount} changed without impacting score`;
|
|
1773
|
+
}
|
|
1774
|
+
}).join(", ");
|
|
1775
|
+
}
|
|
1776
|
+
function sortChanges(changes) {
|
|
1777
|
+
return [...changes].sort(
|
|
1778
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
function changesToDiffOutcomes(changes) {
|
|
1782
|
+
return changes.map((change) => {
|
|
1783
|
+
if (change.scores.diff > 0) {
|
|
1784
|
+
return "positive";
|
|
1785
|
+
}
|
|
1786
|
+
if (change.scores.diff < 0) {
|
|
1787
|
+
return "negative";
|
|
1788
|
+
}
|
|
1789
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
1790
|
+
return "mixed";
|
|
1791
|
+
}
|
|
1792
|
+
return "unchanged";
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
function mergeDiffOutcomes(outcomes) {
|
|
1796
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
1797
|
+
return "unchanged";
|
|
1798
|
+
}
|
|
1799
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
1800
|
+
return "positive";
|
|
1801
|
+
}
|
|
1802
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
1803
|
+
return "negative";
|
|
1804
|
+
}
|
|
1805
|
+
return "mixed";
|
|
1806
|
+
}
|
|
1807
|
+
function countDiffOutcomes(outcomes) {
|
|
1808
|
+
return {
|
|
1809
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
1810
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
1811
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
1812
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
1817
|
+
import chalk4 from "chalk";
|
|
1818
|
+
function log(msg = "") {
|
|
1819
|
+
ui().logger.log(msg);
|
|
1820
|
+
}
|
|
1821
|
+
function logStdoutSummary(report) {
|
|
1822
|
+
const printCategories = report.categories.length > 0;
|
|
1823
|
+
log(reportToHeaderSection2(report));
|
|
1824
|
+
log();
|
|
1825
|
+
logPlugins(report);
|
|
1826
|
+
if (printCategories) {
|
|
1827
|
+
logCategories(report);
|
|
1828
|
+
}
|
|
1829
|
+
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1830
|
+
log();
|
|
1831
|
+
}
|
|
1832
|
+
function reportToHeaderSection2(report) {
|
|
1833
|
+
const { packageName, version: version2 } = report;
|
|
1834
|
+
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1835
|
+
}
|
|
1836
|
+
function logPlugins(report) {
|
|
1837
|
+
const { plugins } = report;
|
|
1838
|
+
plugins.forEach((plugin) => {
|
|
1839
|
+
const { title, audits } = plugin;
|
|
1840
|
+
log();
|
|
1841
|
+
log(chalk4.magentaBright.bold(`${title} audits`));
|
|
1842
|
+
log();
|
|
1843
|
+
audits.forEach((audit) => {
|
|
1844
|
+
ui().row([
|
|
1845
|
+
{
|
|
1846
|
+
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
1847
|
+
width: 2,
|
|
1848
|
+
padding: [0, 1, 0, 0]
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
text: audit.title,
|
|
1852
|
+
// eslint-disable-next-line no-magic-numbers
|
|
1853
|
+
padding: [0, 3, 0, 0]
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
text: chalk4.cyanBright(audit.displayValue || `${audit.value}`),
|
|
1857
|
+
width: 10,
|
|
1858
|
+
padding: [0, 0, 0, 0]
|
|
1859
|
+
}
|
|
1860
|
+
]);
|
|
1861
|
+
});
|
|
1862
|
+
log();
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
function logCategories({ categories, plugins }) {
|
|
1866
|
+
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
1867
|
+
const rows = categories.map(({ title, score, refs }) => [
|
|
1868
|
+
title,
|
|
1869
|
+
applyScoreColor({ score }),
|
|
1870
|
+
countCategoryAudits(refs, plugins)
|
|
1871
|
+
]);
|
|
1872
|
+
const table = ui().table();
|
|
1873
|
+
table.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
1874
|
+
table.head(
|
|
1875
|
+
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1876
|
+
content: chalk4.cyan(heading),
|
|
1877
|
+
hAlign: hAlign(idx)
|
|
1878
|
+
}))
|
|
1879
|
+
);
|
|
1880
|
+
rows.forEach(
|
|
1881
|
+
(row) => table.row(
|
|
1882
|
+
row.map((content, idx) => ({
|
|
1883
|
+
content: content.toString(),
|
|
1884
|
+
hAlign: hAlign(idx)
|
|
1885
|
+
}))
|
|
1886
|
+
)
|
|
1887
|
+
);
|
|
1888
|
+
log(chalk4.magentaBright.bold("Categories"));
|
|
1889
|
+
log();
|
|
1890
|
+
table.render();
|
|
1891
|
+
log();
|
|
1892
|
+
}
|
|
1893
|
+
function applyScoreColor({ score, text }) {
|
|
1894
|
+
const formattedScore = text ?? formatReportScore(score);
|
|
1895
|
+
const style2 = text ? chalk4 : chalk4.bold;
|
|
1896
|
+
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1897
|
+
return style2.green(formattedScore);
|
|
1898
|
+
}
|
|
1899
|
+
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1900
|
+
return style2.yellow(formattedScore);
|
|
1901
|
+
}
|
|
1902
|
+
return style2.red(formattedScore);
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1627
1905
|
// packages/utils/src/lib/reports/scoring.ts
|
|
1628
1906
|
var GroupRefInvalidError = class extends Error {
|
|
1629
1907
|
constructor(auditSlug, pluginSlug) {
|
|
@@ -1783,7 +2061,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
1783
2061
|
|
|
1784
2062
|
// packages/core/package.json
|
|
1785
2063
|
var name = "@code-pushup/core";
|
|
1786
|
-
var version = "0.
|
|
2064
|
+
var version = "0.29.0";
|
|
1787
2065
|
|
|
1788
2066
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
1789
2067
|
import chalk5 from "chalk";
|
|
@@ -2010,6 +2288,7 @@ async function collectAndPersistReports(options2) {
|
|
|
2010
2288
|
|
|
2011
2289
|
// packages/core/src/lib/compare.ts
|
|
2012
2290
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
2291
|
+
import { join as join5 } from "node:path";
|
|
2013
2292
|
|
|
2014
2293
|
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
2015
2294
|
function compareCategories(reports) {
|
|
@@ -2155,7 +2434,8 @@ function pluginAuditPairToDiff({
|
|
|
2155
2434
|
}
|
|
2156
2435
|
|
|
2157
2436
|
// packages/core/src/lib/compare.ts
|
|
2158
|
-
async function compareReportFiles(inputPaths,
|
|
2437
|
+
async function compareReportFiles(inputPaths, persistConfig) {
|
|
2438
|
+
const { outputDir, filename, format } = persistConfig;
|
|
2159
2439
|
const [reportBefore, reportAfter] = await Promise.all([
|
|
2160
2440
|
readJsonFile(inputPaths.before),
|
|
2161
2441
|
readJsonFile(inputPaths.after)
|
|
@@ -2165,7 +2445,15 @@ async function compareReportFiles(inputPaths, outputPath) {
|
|
|
2165
2445
|
after: reportSchema.parse(reportAfter)
|
|
2166
2446
|
};
|
|
2167
2447
|
const reportsDiff = compareReports(reports);
|
|
2168
|
-
|
|
2448
|
+
return Promise.all(
|
|
2449
|
+
format.map(async (fmt) => {
|
|
2450
|
+
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2451
|
+
const content = reportsDiffToFileContent(reportsDiff, fmt);
|
|
2452
|
+
await ensureDirectoryExists(outputDir);
|
|
2453
|
+
await writeFile2(outputPath, content);
|
|
2454
|
+
return outputPath;
|
|
2455
|
+
})
|
|
2456
|
+
);
|
|
2169
2457
|
}
|
|
2170
2458
|
function compareReports(reports) {
|
|
2171
2459
|
const start = performance.now();
|
|
@@ -2190,9 +2478,17 @@ function compareReports(reports) {
|
|
|
2190
2478
|
duration
|
|
2191
2479
|
};
|
|
2192
2480
|
}
|
|
2481
|
+
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2482
|
+
switch (format) {
|
|
2483
|
+
case "json":
|
|
2484
|
+
return JSON.stringify(reportsDiff, null, 2);
|
|
2485
|
+
case "md":
|
|
2486
|
+
return generateMdReportsDiff(reportsDiff);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2193
2489
|
|
|
2194
2490
|
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2195
|
-
import { join as
|
|
2491
|
+
import { join as join6 } from "node:path";
|
|
2196
2492
|
var ConfigPathError = class extends Error {
|
|
2197
2493
|
constructor(configPath) {
|
|
2198
2494
|
super(`Provided path '${configPath}' is not valid.`);
|
|
@@ -2226,7 +2522,7 @@ async function autoloadRc(tsconfig) {
|
|
|
2226
2522
|
);
|
|
2227
2523
|
}
|
|
2228
2524
|
return readRcByPath(
|
|
2229
|
-
|
|
2525
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2230
2526
|
tsconfig
|
|
2231
2527
|
);
|
|
2232
2528
|
}
|
|
@@ -2481,7 +2777,6 @@ function renderUploadAutorunHint() {
|
|
|
2481
2777
|
|
|
2482
2778
|
// packages/cli/src/lib/compare/compare-command.ts
|
|
2483
2779
|
import chalk9 from "chalk";
|
|
2484
|
-
import { join as join6 } from "node:path";
|
|
2485
2780
|
|
|
2486
2781
|
// packages/cli/src/lib/implementation/compare.options.ts
|
|
2487
2782
|
function yargsCompareOptionsDefinition() {
|
|
@@ -2511,12 +2806,10 @@ function yargsCompareCommandObject() {
|
|
|
2511
2806
|
ui().logger.info(chalk9.gray(`Run ${command}...`));
|
|
2512
2807
|
const options2 = args;
|
|
2513
2808
|
const { before, after, persist } = options2;
|
|
2514
|
-
const
|
|
2515
|
-
|
|
2516
|
-
|
|
2809
|
+
const outputPaths = await compareReportFiles({ before, after }, persist);
|
|
2810
|
+
ui().logger.info(
|
|
2811
|
+
`Reports diff written to ${outputPaths.map((path) => chalk9.bold(path)).join(" and ")}`
|
|
2517
2812
|
);
|
|
2518
|
-
await compareReportFiles({ before, after }, outputPath);
|
|
2519
|
-
ui().logger.info(`Reports diff written to ${chalk9.bold(outputPath)}`);
|
|
2520
2813
|
}
|
|
2521
2814
|
};
|
|
2522
2815
|
}
|
|
@@ -2617,9 +2910,9 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
2617
2910
|
return {
|
|
2618
2911
|
...config != null && { config },
|
|
2619
2912
|
persist: {
|
|
2620
|
-
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ??
|
|
2621
|
-
|
|
2622
|
-
|
|
2913
|
+
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ?? DEFAULT_PERSIST_OUTPUT_DIR,
|
|
2914
|
+
filename: cliPersist?.filename ?? rcPersist?.filename ?? DEFAULT_PERSIST_FILENAME,
|
|
2915
|
+
format: cliPersist?.format ?? rcPersist?.format ?? DEFAULT_PERSIST_FORMAT
|
|
2623
2916
|
},
|
|
2624
2917
|
...upload2 != null && { upload: upload2 },
|
|
2625
2918
|
categories: rcCategories ?? [],
|