@code-pushup/core 0.49.0 → 0.50.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 +301 -142
- package/package.json +7 -33
- package/src/index.d.ts +7 -6
- package/src/lib/collect-and-persist.d.ts +2 -2
- package/src/lib/compare.d.ts +3 -3
- package/src/lib/history.d.ts +2 -2
- package/src/lib/implementation/collect.d.ts +2 -2
- package/src/lib/implementation/compare-scorables.d.ts +2 -2
- package/src/lib/implementation/execute-plugin.d.ts +1 -1
- package/src/lib/implementation/persist.d.ts +2 -2
- package/src/lib/implementation/read-rc-file.d.ts +1 -1
- package/src/lib/implementation/runner.d.ts +1 -1
- package/src/lib/merge-diffs.d.ts +2 -0
- package/src/lib/normalize.d.ts +1 -1
- package/src/lib/upload.d.ts +2 -2
package/index.js
CHANGED
|
@@ -670,6 +670,8 @@ var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
|
670
670
|
);
|
|
671
671
|
var reportsDiffSchema = z15.object({
|
|
672
672
|
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
673
|
+
portalUrl: urlSchema.optional().describe("Link to comparison page in Code PushUp portal"),
|
|
674
|
+
label: z15.string().optional().describe("Label (e.g. project name)"),
|
|
673
675
|
categories: makeArraysComparisonSchema(
|
|
674
676
|
categoryDiffSchema,
|
|
675
677
|
categoryResultSchema,
|
|
@@ -739,8 +741,24 @@ function comparePairs(pairs, equalsFn) {
|
|
|
739
741
|
);
|
|
740
742
|
}
|
|
741
743
|
|
|
744
|
+
// packages/utils/src/lib/errors.ts
|
|
745
|
+
function stringifyError(error) {
|
|
746
|
+
if (error instanceof Error) {
|
|
747
|
+
if (error.name === "Error" || error.message.startsWith(error.name)) {
|
|
748
|
+
return error.message;
|
|
749
|
+
}
|
|
750
|
+
return `${error.name}: ${error.message}`;
|
|
751
|
+
}
|
|
752
|
+
if (typeof error === "string") {
|
|
753
|
+
return error;
|
|
754
|
+
}
|
|
755
|
+
return JSON.stringify(error);
|
|
756
|
+
}
|
|
757
|
+
|
|
742
758
|
// packages/utils/src/lib/execute-process.ts
|
|
743
|
-
import {
|
|
759
|
+
import {
|
|
760
|
+
spawn
|
|
761
|
+
} from "node:child_process";
|
|
744
762
|
|
|
745
763
|
// packages/utils/src/lib/reports/utils.ts
|
|
746
764
|
import ansis from "ansis";
|
|
@@ -867,12 +885,12 @@ function countCategoryAudits(refs, plugins) {
|
|
|
867
885
|
}, 0);
|
|
868
886
|
}
|
|
869
887
|
function compareCategoryAuditsAndGroups(a, b) {
|
|
870
|
-
if (a.weight !== b.weight) {
|
|
871
|
-
return b.weight - a.weight;
|
|
872
|
-
}
|
|
873
888
|
if (a.score !== b.score) {
|
|
874
889
|
return a.score - b.score;
|
|
875
890
|
}
|
|
891
|
+
if (a.weight !== b.weight) {
|
|
892
|
+
return b.weight - a.weight;
|
|
893
|
+
}
|
|
876
894
|
if ("value" in a && "value" in b && a.value !== b.value) {
|
|
877
895
|
return b.value - a.value;
|
|
878
896
|
}
|
|
@@ -964,25 +982,29 @@ var ProcessError = class extends Error {
|
|
|
964
982
|
}
|
|
965
983
|
};
|
|
966
984
|
function executeProcess(cfg) {
|
|
967
|
-
const {
|
|
968
|
-
const { onStdout, onError, onComplete } = observer ?? {};
|
|
985
|
+
const { command, args, observer, ignoreExitCode = false, ...options } = cfg;
|
|
986
|
+
const { onStdout, onStderr, onError, onComplete } = observer ?? {};
|
|
969
987
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
970
988
|
const start = performance.now();
|
|
971
989
|
return new Promise((resolve, reject) => {
|
|
972
|
-
const
|
|
990
|
+
const spawnedProcess = spawn(command, args ?? [], {
|
|
991
|
+
shell: true,
|
|
992
|
+
...options
|
|
993
|
+
});
|
|
973
994
|
let stdout = "";
|
|
974
995
|
let stderr = "";
|
|
975
|
-
|
|
996
|
+
spawnedProcess.stdout.on("data", (data) => {
|
|
976
997
|
stdout += String(data);
|
|
977
|
-
onStdout?.(String(data));
|
|
998
|
+
onStdout?.(String(data), spawnedProcess);
|
|
978
999
|
});
|
|
979
|
-
|
|
1000
|
+
spawnedProcess.stderr.on("data", (data) => {
|
|
980
1001
|
stderr += String(data);
|
|
1002
|
+
onStderr?.(String(data), spawnedProcess);
|
|
981
1003
|
});
|
|
982
|
-
|
|
1004
|
+
spawnedProcess.on("error", (err) => {
|
|
983
1005
|
stderr += err.toString();
|
|
984
1006
|
});
|
|
985
|
-
|
|
1007
|
+
spawnedProcess.on("close", (code2) => {
|
|
986
1008
|
const timings = { date, duration: calcDuration(start) };
|
|
987
1009
|
if (code2 === 0 || ignoreExitCode) {
|
|
988
1010
|
onComplete?.();
|
|
@@ -1181,6 +1203,9 @@ import { isAbsolute, join, relative } from "node:path";
|
|
|
1181
1203
|
import { simpleGit } from "simple-git";
|
|
1182
1204
|
|
|
1183
1205
|
// packages/utils/src/lib/transform.ts
|
|
1206
|
+
function toArray(val) {
|
|
1207
|
+
return Array.isArray(val) ? val : [val];
|
|
1208
|
+
}
|
|
1184
1209
|
function objectToEntries(obj) {
|
|
1185
1210
|
return Object.entries(obj);
|
|
1186
1211
|
}
|
|
@@ -1452,7 +1477,10 @@ function getColumnAlignments(tableData) {
|
|
|
1452
1477
|
}
|
|
1453
1478
|
|
|
1454
1479
|
// packages/utils/src/lib/reports/formatting.ts
|
|
1455
|
-
import {
|
|
1480
|
+
import {
|
|
1481
|
+
MarkdownDocument,
|
|
1482
|
+
md as md2
|
|
1483
|
+
} from "build-md";
|
|
1456
1484
|
function tableSection(tableData, options) {
|
|
1457
1485
|
if (tableData.rows.length === 0) {
|
|
1458
1486
|
return null;
|
|
@@ -1812,19 +1840,111 @@ function reportMetaTable({
|
|
|
1812
1840
|
|
|
1813
1841
|
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1814
1842
|
import {
|
|
1815
|
-
MarkdownDocument as
|
|
1816
|
-
md as
|
|
1843
|
+
MarkdownDocument as MarkdownDocument5,
|
|
1844
|
+
md as md6
|
|
1817
1845
|
} from "build-md";
|
|
1846
|
+
|
|
1847
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff-utils.ts
|
|
1848
|
+
import { MarkdownDocument as MarkdownDocument4, md as md5 } from "build-md";
|
|
1818
1849
|
var MAX_ROWS = 100;
|
|
1819
|
-
function
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
).
|
|
1850
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1851
|
+
const pluralizedCount = changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`;
|
|
1852
|
+
const pluralizedVerb = unchanged.length === 1 ? "is" : "are";
|
|
1853
|
+
return `${pluralizedCount} ${pluralizedVerb} unchanged.`;
|
|
1854
|
+
}
|
|
1855
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
1856
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1857
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1858
|
+
).map(([outcome, count]) => {
|
|
1859
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1860
|
+
token,
|
|
1861
|
+
count
|
|
1862
|
+
)}`;
|
|
1863
|
+
switch (outcome) {
|
|
1864
|
+
case "positive":
|
|
1865
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
1866
|
+
case "negative":
|
|
1867
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1868
|
+
case "mixed":
|
|
1869
|
+
return `${formattedCount} changed without impacting score`;
|
|
1870
|
+
}
|
|
1871
|
+
}).join(", ");
|
|
1872
|
+
}
|
|
1873
|
+
function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
|
|
1874
|
+
if (changed.length === 0) {
|
|
1875
|
+
return new MarkdownDocument4().paragraph(
|
|
1876
|
+
summarizeUnchanged(token, { changed, unchanged })
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
return new MarkdownDocument4().table(columns, rows.slice(0, MAX_ROWS)).paragraph(
|
|
1880
|
+
changed.length > MAX_ROWS && md5.italic(
|
|
1881
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1882
|
+
token
|
|
1883
|
+
)} are listed above for brevity.`
|
|
1884
|
+
)
|
|
1885
|
+
).paragraph(
|
|
1886
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
function formatTitle({
|
|
1890
|
+
title,
|
|
1891
|
+
docsUrl
|
|
1892
|
+
}) {
|
|
1893
|
+
if (docsUrl) {
|
|
1894
|
+
return md5.link(docsUrl, title);
|
|
1895
|
+
}
|
|
1896
|
+
return title;
|
|
1897
|
+
}
|
|
1898
|
+
function formatPortalLink(portalUrl) {
|
|
1899
|
+
return portalUrl && md5.link(portalUrl, "\u{1F575}\uFE0F See full comparison in Code PushUp portal \u{1F50D}");
|
|
1900
|
+
}
|
|
1901
|
+
function sortChanges(changes) {
|
|
1902
|
+
return [...changes].sort(
|
|
1903
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1904
|
+
);
|
|
1905
|
+
}
|
|
1906
|
+
function getDiffChanges(diff) {
|
|
1907
|
+
return [
|
|
1908
|
+
...diff.categories.changed,
|
|
1909
|
+
...diff.groups.changed,
|
|
1910
|
+
...diff.audits.changed
|
|
1911
|
+
];
|
|
1912
|
+
}
|
|
1913
|
+
function changesToDiffOutcomes(changes) {
|
|
1914
|
+
return changes.map((change) => {
|
|
1915
|
+
if (change.scores.diff > 0) {
|
|
1916
|
+
return "positive";
|
|
1917
|
+
}
|
|
1918
|
+
if (change.scores.diff < 0) {
|
|
1919
|
+
return "negative";
|
|
1920
|
+
}
|
|
1921
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
1922
|
+
return "mixed";
|
|
1923
|
+
}
|
|
1924
|
+
return "unchanged";
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
function mergeDiffOutcomes(outcomes) {
|
|
1928
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
1929
|
+
return "unchanged";
|
|
1930
|
+
}
|
|
1931
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
1932
|
+
return "positive";
|
|
1933
|
+
}
|
|
1934
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
1935
|
+
return "negative";
|
|
1936
|
+
}
|
|
1937
|
+
return "mixed";
|
|
1938
|
+
}
|
|
1939
|
+
function countDiffOutcomes(outcomes) {
|
|
1940
|
+
return {
|
|
1941
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
1942
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
1943
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
1944
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
1945
|
+
};
|
|
1826
1946
|
}
|
|
1827
|
-
function
|
|
1947
|
+
function formatReportOutcome(outcome, commits) {
|
|
1828
1948
|
const outcomeTexts = {
|
|
1829
1949
|
positive: md5`🥳 Code PushUp report has ${md5.bold("improved")}`,
|
|
1830
1950
|
negative: md5`😟 Code PushUp report has ${md5.bold("regressed")}`,
|
|
@@ -1833,27 +1953,91 @@ function createDiffHeaderSection(diff, portalUrl) {
|
|
|
1833
1953
|
)}`,
|
|
1834
1954
|
unchanged: md5`😐 Code PushUp report is ${md5.bold("unchanged")}`
|
|
1835
1955
|
};
|
|
1956
|
+
if (commits) {
|
|
1957
|
+
const commitsText = `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
1958
|
+
return md5`${outcomeTexts[outcome]} – ${commitsText}.`;
|
|
1959
|
+
}
|
|
1960
|
+
return md5`${outcomeTexts[outcome]}.`;
|
|
1961
|
+
}
|
|
1962
|
+
function compareDiffsBy(type, a, b) {
|
|
1963
|
+
return sumScoreChanges(b[type].changed) - sumScoreChanges(a[type].changed) || sumConfigChanges(b[type]) - sumConfigChanges(a[type]);
|
|
1964
|
+
}
|
|
1965
|
+
function sumScoreChanges(changes) {
|
|
1966
|
+
return changes.reduce(
|
|
1967
|
+
(acc, { scores }) => acc + Math.abs(scores.diff),
|
|
1968
|
+
0
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
function sumConfigChanges({
|
|
1972
|
+
added,
|
|
1973
|
+
removed
|
|
1974
|
+
}) {
|
|
1975
|
+
return added.length + removed.length;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1979
|
+
function generateMdReportsDiff(diff) {
|
|
1980
|
+
return new MarkdownDocument5().$concat(
|
|
1981
|
+
createDiffHeaderSection(diff),
|
|
1982
|
+
createDiffCategoriesSection(diff),
|
|
1983
|
+
createDiffDetailsSection(diff)
|
|
1984
|
+
).toString();
|
|
1985
|
+
}
|
|
1986
|
+
function generateMdReportsDiffForMonorepo(diffs) {
|
|
1987
|
+
const diffsWithOutcomes = diffs.map((diff) => ({
|
|
1988
|
+
...diff,
|
|
1989
|
+
outcome: mergeDiffOutcomes(changesToDiffOutcomes(getDiffChanges(diff)))
|
|
1990
|
+
})).sort(
|
|
1991
|
+
(a, b) => compareDiffsBy("categories", a, b) || compareDiffsBy("groups", a, b) || compareDiffsBy("audits", a, b) || a.label.localeCompare(b.label)
|
|
1992
|
+
);
|
|
1993
|
+
const unchanged = diffsWithOutcomes.filter(
|
|
1994
|
+
({ outcome }) => outcome === "unchanged"
|
|
1995
|
+
);
|
|
1996
|
+
const changed = diffsWithOutcomes.filter((diff) => !unchanged.includes(diff));
|
|
1997
|
+
return new MarkdownDocument5().$concat(
|
|
1998
|
+
createDiffHeaderSection(diffs),
|
|
1999
|
+
...changed.map(createDiffProjectSection)
|
|
2000
|
+
).$if(
|
|
2001
|
+
unchanged.length > 0,
|
|
2002
|
+
(doc) => doc.rule().paragraph(summarizeUnchanged("project", { unchanged, changed }))
|
|
2003
|
+
).toString();
|
|
2004
|
+
}
|
|
2005
|
+
function createDiffHeaderSection(diff) {
|
|
1836
2006
|
const outcome = mergeDiffOutcomes(
|
|
1837
|
-
changesToDiffOutcomes(
|
|
1838
|
-
...diff.categories.changed,
|
|
1839
|
-
...diff.groups.changed,
|
|
1840
|
-
...diff.audits.changed
|
|
1841
|
-
])
|
|
2007
|
+
changesToDiffOutcomes(toArray(diff).flatMap(getDiffChanges))
|
|
1842
2008
|
);
|
|
1843
|
-
const
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
2009
|
+
const commits = Array.isArray(diff) ? diff[0]?.commits : diff.commits;
|
|
2010
|
+
const portalUrl = Array.isArray(diff) ? void 0 : diff.portalUrl;
|
|
2011
|
+
return new MarkdownDocument5().heading(HIERARCHY.level_1, "Code PushUp").paragraph(formatReportOutcome(outcome, commits)).paragraph(formatPortalLink(portalUrl));
|
|
2012
|
+
}
|
|
2013
|
+
function createDiffProjectSection(diff) {
|
|
2014
|
+
return new MarkdownDocument5().heading(HIERARCHY.level_2, md6`💼 Project ${md6.code(diff.label)}`).paragraph(formatReportOutcome(diff.outcome)).paragraph(formatPortalLink(diff.portalUrl)).$concat(
|
|
2015
|
+
createDiffCategoriesSection(diff, {
|
|
2016
|
+
skipHeading: true,
|
|
2017
|
+
skipUnchanged: true
|
|
2018
|
+
}),
|
|
2019
|
+
createDiffDetailsSection(diff, HIERARCHY.level_3)
|
|
1848
2020
|
);
|
|
1849
2021
|
}
|
|
1850
|
-
function createDiffCategoriesSection(diff) {
|
|
2022
|
+
function createDiffCategoriesSection(diff, options) {
|
|
1851
2023
|
const { changed, unchanged, added } = diff.categories;
|
|
2024
|
+
const { skipHeading, skipUnchanged } = options ?? {};
|
|
1852
2025
|
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
1853
2026
|
const hasChanges = unchanged.length < categoriesCount;
|
|
1854
2027
|
if (categoriesCount === 0) {
|
|
1855
2028
|
return null;
|
|
1856
2029
|
}
|
|
2030
|
+
const [columns, rows] = createCategoriesTable(diff, {
|
|
2031
|
+
hasChanges,
|
|
2032
|
+
skipUnchanged
|
|
2033
|
+
});
|
|
2034
|
+
return new MarkdownDocument5().heading(HIERARCHY.level_2, !skipHeading && "\u{1F3F7}\uFE0F Categories").table(columns, rows).paragraph(added.length > 0 && md6.italic("(\\*) New category.")).paragraph(
|
|
2035
|
+
skipUnchanged && unchanged.length > 0 && summarizeUnchanged("category", { changed, unchanged })
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
2038
|
+
function createCategoriesTable(diff, options) {
|
|
2039
|
+
const { changed, unchanged, added } = diff.categories;
|
|
2040
|
+
const { hasChanges, skipUnchanged } = options;
|
|
1857
2041
|
const columns = [
|
|
1858
2042
|
{ heading: "\u{1F3F7}\uFE0F Category", alignment: "left" },
|
|
1859
2043
|
{
|
|
@@ -1874,27 +2058,43 @@ function createDiffCategoriesSection(diff) {
|
|
|
1874
2058
|
]),
|
|
1875
2059
|
...added.map((category) => [
|
|
1876
2060
|
formatTitle(category),
|
|
1877
|
-
|
|
2061
|
+
md6.italic("n/a (\\*)"),
|
|
1878
2062
|
formatScoreWithColor(category.score),
|
|
1879
|
-
|
|
2063
|
+
md6.italic("n/a (\\*)")
|
|
1880
2064
|
]),
|
|
1881
|
-
...unchanged.map((category) => [
|
|
2065
|
+
...skipUnchanged ? [] : unchanged.map((category) => [
|
|
1882
2066
|
formatTitle(category),
|
|
1883
2067
|
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1884
2068
|
formatScoreWithColor(category.score),
|
|
1885
2069
|
"\u2013"
|
|
1886
2070
|
])
|
|
1887
2071
|
];
|
|
1888
|
-
return
|
|
2072
|
+
return [
|
|
1889
2073
|
hasChanges ? columns : columns.slice(0, 2),
|
|
1890
2074
|
rows.map((row) => hasChanges ? row : row.slice(0, 2))
|
|
1891
|
-
|
|
2075
|
+
];
|
|
1892
2076
|
}
|
|
1893
|
-
function
|
|
2077
|
+
function createDiffDetailsSection(diff, level = HIERARCHY.level_2) {
|
|
2078
|
+
if (diff.groups.changed.length + diff.audits.changed.length === 0) {
|
|
2079
|
+
return null;
|
|
2080
|
+
}
|
|
2081
|
+
const summary = ["group", "audit"].map(
|
|
2082
|
+
(token) => summarizeDiffOutcomes(
|
|
2083
|
+
changesToDiffOutcomes(diff[`${token}s`].changed),
|
|
2084
|
+
token
|
|
2085
|
+
)
|
|
2086
|
+
).filter(Boolean).join(", ");
|
|
2087
|
+
const details2 = new MarkdownDocument5().$concat(
|
|
2088
|
+
createDiffGroupsSection(diff, level),
|
|
2089
|
+
createDiffAuditsSection(diff, level)
|
|
2090
|
+
);
|
|
2091
|
+
return new MarkdownDocument5().details(summary, details2);
|
|
2092
|
+
}
|
|
2093
|
+
function createDiffGroupsSection(diff, level) {
|
|
1894
2094
|
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1895
2095
|
return null;
|
|
1896
2096
|
}
|
|
1897
|
-
return new
|
|
2097
|
+
return new MarkdownDocument5().heading(level, "\u{1F5C3}\uFE0F Groups").$concat(
|
|
1898
2098
|
createGroupsOrAuditsDetails(
|
|
1899
2099
|
"group",
|
|
1900
2100
|
diff.groups,
|
|
@@ -1915,8 +2115,8 @@ function createDiffGroupsSection(diff) {
|
|
|
1915
2115
|
)
|
|
1916
2116
|
);
|
|
1917
2117
|
}
|
|
1918
|
-
function createDiffAuditsSection(diff) {
|
|
1919
|
-
return new
|
|
2118
|
+
function createDiffAuditsSection(diff, level) {
|
|
2119
|
+
return new MarkdownDocument5().heading(level, "\u{1F6E1}\uFE0F Audits").$concat(
|
|
1920
2120
|
createGroupsOrAuditsDetails(
|
|
1921
2121
|
"audit",
|
|
1922
2122
|
diff.audits,
|
|
@@ -1931,7 +2131,7 @@ function createDiffAuditsSection(diff) {
|
|
|
1931
2131
|
formatTitle(audit.plugin),
|
|
1932
2132
|
formatTitle(audit),
|
|
1933
2133
|
`${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
1934
|
-
|
|
2134
|
+
md6`${scoreMarker(audit.scores.after, "square")} ${md6.bold(
|
|
1935
2135
|
audit.displayValues.after || audit.values.after.toString()
|
|
1936
2136
|
)}`,
|
|
1937
2137
|
formatValueChange(audit)
|
|
@@ -1939,96 +2139,6 @@ function createDiffAuditsSection(diff) {
|
|
|
1939
2139
|
)
|
|
1940
2140
|
);
|
|
1941
2141
|
}
|
|
1942
|
-
function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
|
|
1943
|
-
if (changed.length === 0) {
|
|
1944
|
-
return new MarkdownDocument4().paragraph(
|
|
1945
|
-
summarizeUnchanged(token, { changed, unchanged })
|
|
1946
|
-
);
|
|
1947
|
-
}
|
|
1948
|
-
return new MarkdownDocument4().details(
|
|
1949
|
-
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1950
|
-
md5`${md5.table(columns, rows.slice(0, MAX_ROWS))}${changed.length > MAX_ROWS ? md5.paragraph(
|
|
1951
|
-
md5.italic(
|
|
1952
|
-
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1953
|
-
token
|
|
1954
|
-
)} are listed above for brevity.`
|
|
1955
|
-
)
|
|
1956
|
-
) : ""}${unchanged.length > 0 ? md5.paragraph(summarizeUnchanged(token, { changed, unchanged })) : ""}`
|
|
1957
|
-
);
|
|
1958
|
-
}
|
|
1959
|
-
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1960
|
-
return [
|
|
1961
|
-
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
1962
|
-
unchanged.length === 1 ? "is" : "are",
|
|
1963
|
-
"unchanged."
|
|
1964
|
-
].join(" ");
|
|
1965
|
-
}
|
|
1966
|
-
function summarizeDiffOutcomes(outcomes, token) {
|
|
1967
|
-
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
1968
|
-
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
1969
|
-
).map(([outcome, count]) => {
|
|
1970
|
-
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
1971
|
-
token,
|
|
1972
|
-
count
|
|
1973
|
-
)}`;
|
|
1974
|
-
switch (outcome) {
|
|
1975
|
-
case "positive":
|
|
1976
|
-
return `\u{1F44D} ${formattedCount} improved`;
|
|
1977
|
-
case "negative":
|
|
1978
|
-
return `\u{1F44E} ${formattedCount} regressed`;
|
|
1979
|
-
case "mixed":
|
|
1980
|
-
return `${formattedCount} changed without impacting score`;
|
|
1981
|
-
}
|
|
1982
|
-
}).join(", ");
|
|
1983
|
-
}
|
|
1984
|
-
function formatTitle({
|
|
1985
|
-
title,
|
|
1986
|
-
docsUrl
|
|
1987
|
-
}) {
|
|
1988
|
-
if (docsUrl) {
|
|
1989
|
-
return md5.link(docsUrl, title);
|
|
1990
|
-
}
|
|
1991
|
-
return title;
|
|
1992
|
-
}
|
|
1993
|
-
function sortChanges(changes) {
|
|
1994
|
-
return [...changes].sort(
|
|
1995
|
-
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
1996
|
-
);
|
|
1997
|
-
}
|
|
1998
|
-
function changesToDiffOutcomes(changes) {
|
|
1999
|
-
return changes.map((change) => {
|
|
2000
|
-
if (change.scores.diff > 0) {
|
|
2001
|
-
return "positive";
|
|
2002
|
-
}
|
|
2003
|
-
if (change.scores.diff < 0) {
|
|
2004
|
-
return "negative";
|
|
2005
|
-
}
|
|
2006
|
-
if (change.values != null && change.values.diff !== 0) {
|
|
2007
|
-
return "mixed";
|
|
2008
|
-
}
|
|
2009
|
-
return "unchanged";
|
|
2010
|
-
});
|
|
2011
|
-
}
|
|
2012
|
-
function mergeDiffOutcomes(outcomes) {
|
|
2013
|
-
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
2014
|
-
return "unchanged";
|
|
2015
|
-
}
|
|
2016
|
-
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
2017
|
-
return "positive";
|
|
2018
|
-
}
|
|
2019
|
-
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
2020
|
-
return "negative";
|
|
2021
|
-
}
|
|
2022
|
-
return "mixed";
|
|
2023
|
-
}
|
|
2024
|
-
function countDiffOutcomes(outcomes) {
|
|
2025
|
-
return {
|
|
2026
|
-
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
2027
|
-
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
2028
|
-
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
2029
|
-
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
2030
|
-
};
|
|
2031
|
-
}
|
|
2032
2142
|
|
|
2033
2143
|
// packages/utils/src/lib/reports/load-report.ts
|
|
2034
2144
|
import { join as join2 } from "node:path";
|
|
@@ -2085,7 +2195,8 @@ function logPlugins(report) {
|
|
|
2085
2195
|
},
|
|
2086
2196
|
{
|
|
2087
2197
|
text: cyanBright(audit.displayValue || `${audit.value}`),
|
|
2088
|
-
|
|
2198
|
+
// eslint-disable-next-line no-magic-numbers
|
|
2199
|
+
width: 20,
|
|
2089
2200
|
padding: [0, 0, 0, 0]
|
|
2090
2201
|
}
|
|
2091
2202
|
]);
|
|
@@ -2238,7 +2349,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2238
2349
|
|
|
2239
2350
|
// packages/core/package.json
|
|
2240
2351
|
var name = "@code-pushup/core";
|
|
2241
|
-
var version = "0.
|
|
2352
|
+
var version = "0.50.0";
|
|
2242
2353
|
|
|
2243
2354
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2244
2355
|
import { bold as bold5 } from "ansis";
|
|
@@ -2634,7 +2745,7 @@ function selectMeta(meta) {
|
|
|
2634
2745
|
}
|
|
2635
2746
|
|
|
2636
2747
|
// packages/core/src/lib/compare.ts
|
|
2637
|
-
async function compareReportFiles(inputPaths, persistConfig, uploadConfig) {
|
|
2748
|
+
async function compareReportFiles(inputPaths, persistConfig, uploadConfig, label) {
|
|
2638
2749
|
const { outputDir, filename, format } = persistConfig;
|
|
2639
2750
|
const [reportBefore, reportAfter] = await Promise.all([
|
|
2640
2751
|
readJsonFile(inputPaths.before),
|
|
@@ -2644,12 +2755,20 @@ async function compareReportFiles(inputPaths, persistConfig, uploadConfig) {
|
|
|
2644
2755
|
before: reportSchema.parse(reportBefore),
|
|
2645
2756
|
after: reportSchema.parse(reportAfter)
|
|
2646
2757
|
};
|
|
2647
|
-
const
|
|
2648
|
-
|
|
2758
|
+
const diff = compareReports(reports);
|
|
2759
|
+
if (label) {
|
|
2760
|
+
diff.label = label;
|
|
2761
|
+
}
|
|
2762
|
+
if (uploadConfig && diff.commits) {
|
|
2763
|
+
diff.portalUrl = await fetchPortalComparisonLink(
|
|
2764
|
+
uploadConfig,
|
|
2765
|
+
diff.commits
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2649
2768
|
return Promise.all(
|
|
2650
2769
|
format.map(async (fmt) => {
|
|
2651
2770
|
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2652
|
-
const content = reportsDiffToFileContent(
|
|
2771
|
+
const content = reportsDiffToFileContent(diff, fmt);
|
|
2653
2772
|
await ensureDirectoryExists(outputDir);
|
|
2654
2773
|
await writeFile2(outputPath, content);
|
|
2655
2774
|
return outputPath;
|
|
@@ -2679,12 +2798,12 @@ function compareReports(reports) {
|
|
|
2679
2798
|
duration
|
|
2680
2799
|
};
|
|
2681
2800
|
}
|
|
2682
|
-
function reportsDiffToFileContent(reportsDiff, format
|
|
2801
|
+
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2683
2802
|
switch (format) {
|
|
2684
2803
|
case "json":
|
|
2685
2804
|
return JSON.stringify(reportsDiff, null, 2);
|
|
2686
2805
|
case "md":
|
|
2687
|
-
return generateMdReportsDiff(reportsDiff
|
|
2806
|
+
return generateMdReportsDiff(reportsDiff);
|
|
2688
2807
|
}
|
|
2689
2808
|
}
|
|
2690
2809
|
async function fetchPortalComparisonLink(uploadConfig, commits) {
|
|
@@ -2956,6 +3075,45 @@ async function autoloadRc(tsconfig) {
|
|
|
2956
3075
|
tsconfig
|
|
2957
3076
|
);
|
|
2958
3077
|
}
|
|
3078
|
+
|
|
3079
|
+
// packages/core/src/lib/merge-diffs.ts
|
|
3080
|
+
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
3081
|
+
import { basename, dirname, join as join7 } from "node:path";
|
|
3082
|
+
async function mergeDiffs(files, persistConfig) {
|
|
3083
|
+
const results = await Promise.allSettled(
|
|
3084
|
+
files.map(async (file) => {
|
|
3085
|
+
const json = await readJsonFile(file).catch((error) => {
|
|
3086
|
+
throw new Error(
|
|
3087
|
+
`Failed to read JSON file ${file} - ${stringifyError(error)}`
|
|
3088
|
+
);
|
|
3089
|
+
});
|
|
3090
|
+
const result = await reportsDiffSchema.safeParseAsync(json);
|
|
3091
|
+
if (!result.success) {
|
|
3092
|
+
throw new Error(
|
|
3093
|
+
`Invalid reports diff in ${file} - ${result.error.message}`
|
|
3094
|
+
);
|
|
3095
|
+
}
|
|
3096
|
+
return { ...result.data, file };
|
|
3097
|
+
})
|
|
3098
|
+
);
|
|
3099
|
+
results.filter(isPromiseRejectedResult).forEach(({ reason }) => {
|
|
3100
|
+
ui().logger.warning(
|
|
3101
|
+
`Skipped invalid report diff - ${stringifyError(reason)}`
|
|
3102
|
+
);
|
|
3103
|
+
});
|
|
3104
|
+
const diffs = results.filter(isPromiseFulfilledResult).map(({ value }) => value);
|
|
3105
|
+
const labeledDiffs = diffs.map((diff) => ({
|
|
3106
|
+
...diff,
|
|
3107
|
+
label: diff.label || basename(dirname(diff.file))
|
|
3108
|
+
// fallback is parent folder name
|
|
3109
|
+
}));
|
|
3110
|
+
const markdown = generateMdReportsDiffForMonorepo(labeledDiffs);
|
|
3111
|
+
const { outputDir, filename } = persistConfig;
|
|
3112
|
+
const outputPath = join7(outputDir, `${filename}-diff.md`);
|
|
3113
|
+
await ensureDirectoryExists(outputDir);
|
|
3114
|
+
await writeFile3(outputPath, markdown);
|
|
3115
|
+
return outputPath;
|
|
3116
|
+
}
|
|
2959
3117
|
export {
|
|
2960
3118
|
ConfigPathError,
|
|
2961
3119
|
PersistDirError,
|
|
@@ -2969,6 +3127,7 @@ export {
|
|
|
2969
3127
|
executePlugin,
|
|
2970
3128
|
executePlugins,
|
|
2971
3129
|
history,
|
|
3130
|
+
mergeDiffs,
|
|
2972
3131
|
persistReport,
|
|
2973
3132
|
readRcByPath,
|
|
2974
3133
|
upload
|
package/package.json
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-pushup/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"description": "Core business logic for the used by the Code PushUp CLI",
|
|
5
6
|
"dependencies": {
|
|
6
|
-
"@code-pushup/models": "0.
|
|
7
|
-
"@code-pushup/utils": "0.
|
|
7
|
+
"@code-pushup/models": "0.50.0",
|
|
8
|
+
"@code-pushup/utils": "0.50.0",
|
|
8
9
|
"@code-pushup/portal-client": "^0.9.0",
|
|
9
10
|
"ansis": "^3.3.0"
|
|
10
11
|
},
|
|
11
|
-
"
|
|
12
|
-
"main": "./index.js",
|
|
13
|
-
"homepage": "https://github.com/code-pushup/cli#readme",
|
|
12
|
+
"homepage": "https://github.com/code-pushup/cli/tree/main/packages/core#readme",
|
|
14
13
|
"bugs": {
|
|
15
14
|
"url": "https://github.com/code-pushup/cli/issues"
|
|
16
15
|
},
|
|
@@ -19,32 +18,7 @@
|
|
|
19
18
|
"url": "git+https://github.com/code-pushup/cli.git",
|
|
20
19
|
"directory": "packages/core"
|
|
21
20
|
},
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"name": "Igor Katsuba",
|
|
25
|
-
"email": "igor@katsuba.dev",
|
|
26
|
-
"url": "https://katsuba.dev"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"name": "Kateřina Pilátová",
|
|
30
|
-
"email": "katerina.pilatova@flowup.cz",
|
|
31
|
-
"url": "https://github.com/Tlacenka"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "Matěj Chalk",
|
|
35
|
-
"email": "matej.chalk@flowup.cz",
|
|
36
|
-
"url": "https://github.com/matejchalk"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"name": "Michael Hladky",
|
|
40
|
-
"email": "michael.hladky@push-based.io",
|
|
41
|
-
"url": "https://push-based.io"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"name": "Michael Seredenko",
|
|
45
|
-
"email": "misha.seredenko@push-based.io",
|
|
46
|
-
"url": "https://github.com/MishaSeredenkoPushBased"
|
|
47
|
-
}
|
|
48
|
-
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./index.js",
|
|
49
23
|
"types": "./src/index.d.ts"
|
|
50
24
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export { CollectAndPersistReportsOptions, collectAndPersistReports, } from './lib/collect-and-persist';
|
|
1
|
+
export { type CollectAndPersistReportsOptions, collectAndPersistReports, } from './lib/collect-and-persist';
|
|
2
2
|
export { compareReportFiles, compareReports } from './lib/compare';
|
|
3
|
-
export { CollectOptions, collect } from './lib/implementation/collect';
|
|
4
|
-
export { ReportsToCompare } from './lib/implementation/compare-scorables';
|
|
3
|
+
export { type CollectOptions, collect } from './lib/implementation/collect';
|
|
4
|
+
export type { ReportsToCompare } from './lib/implementation/compare-scorables';
|
|
5
5
|
export { PluginOutputMissingAuditError, executePlugin, executePlugins, } from './lib/implementation/execute-plugin';
|
|
6
6
|
export { PersistDirError, PersistError, persistReport, } from './lib/implementation/persist';
|
|
7
|
-
export { history, HistoryOptions, HistoryOnlyOptions } from './lib/history';
|
|
7
|
+
export { history, type HistoryOptions, type HistoryOnlyOptions, } from './lib/history';
|
|
8
8
|
export { ConfigPathError, autoloadRc, readRcByPath, } from './lib/implementation/read-rc-file';
|
|
9
|
-
export { GlobalOptions } from './lib/types';
|
|
10
|
-
export { UploadOptions, upload } from './lib/upload';
|
|
9
|
+
export type { GlobalOptions } from './lib/types';
|
|
10
|
+
export { type UploadOptions, upload } from './lib/upload';
|
|
11
|
+
export { mergeDiffs } from './lib/merge-diffs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CoreConfig, PersistConfig } from '@code-pushup/models';
|
|
2
|
-
import { GlobalOptions } from './types';
|
|
1
|
+
import { type CoreConfig, type PersistConfig } from '@code-pushup/models';
|
|
2
|
+
import type { GlobalOptions } from './types';
|
|
3
3
|
export type CollectAndPersistReportsOptions = Required<Pick<CoreConfig, 'plugins' | 'categories'>> & {
|
|
4
4
|
persist: Required<PersistConfig>;
|
|
5
5
|
} & Partial<GlobalOptions>;
|
package/src/lib/compare.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PersistConfig, Report, ReportsDiff, type UploadConfig } from '@code-pushup/models';
|
|
2
|
-
import { Diff } from '@code-pushup/utils';
|
|
3
|
-
export declare function compareReportFiles(inputPaths: Diff<string>, persistConfig: Required<PersistConfig>, uploadConfig: UploadConfig | undefined): Promise<string[]>;
|
|
1
|
+
import { type PersistConfig, type Report, type ReportsDiff, type UploadConfig } from '@code-pushup/models';
|
|
2
|
+
import { type Diff } from '@code-pushup/utils';
|
|
3
|
+
export declare function compareReportFiles(inputPaths: Diff<string>, persistConfig: Required<PersistConfig>, uploadConfig: UploadConfig | undefined, label?: string): Promise<string[]>;
|
|
4
4
|
export declare function compareReports(reports: Diff<Report>): ReportsDiff;
|
package/src/lib/history.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CoreConfig, PersistConfig, UploadConfig } from '@code-pushup/models';
|
|
2
|
-
import { GlobalOptions } from './types';
|
|
1
|
+
import type { CoreConfig, PersistConfig, UploadConfig } from '@code-pushup/models';
|
|
2
|
+
import type { GlobalOptions } from './types';
|
|
3
3
|
export type HistoryOnlyOptions = {
|
|
4
4
|
targetBranch?: string;
|
|
5
5
|
skipUploads?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CoreConfig, Report } from '@code-pushup/models';
|
|
2
|
-
import { GlobalOptions } from '../types';
|
|
1
|
+
import type { CoreConfig, Report } from '@code-pushup/models';
|
|
2
|
+
import type { GlobalOptions } from '../types';
|
|
3
3
|
export type CollectOptions = Required<Pick<CoreConfig, 'plugins' | 'categories'>> & Partial<GlobalOptions>;
|
|
4
4
|
/**
|
|
5
5
|
* Run audits, collect plugin output and aggregate it into a JSON object
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ReportsDiff } from '@code-pushup/models';
|
|
2
|
-
import { Diff, ScoredReport } from '@code-pushup/utils';
|
|
1
|
+
import type { ReportsDiff } from '@code-pushup/models';
|
|
2
|
+
import { type Diff, type ScoredReport } from '@code-pushup/utils';
|
|
3
3
|
export type ReportsToCompare = Diff<ScoredReport>;
|
|
4
4
|
export declare function compareCategories(reports: ReportsToCompare): ReportsDiff['categories'];
|
|
5
5
|
export declare function compareGroups(reports: ReportsToCompare): ReportsDiff['groups'];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PersistConfig, Report } from '@code-pushup/models';
|
|
2
|
-
import { MultipleFileResults } from '@code-pushup/utils';
|
|
1
|
+
import type { PersistConfig, Report } from '@code-pushup/models';
|
|
2
|
+
import { type MultipleFileResults } from '@code-pushup/utils';
|
|
3
3
|
export declare class PersistDirError extends Error {
|
|
4
4
|
constructor(outputDir: string);
|
|
5
5
|
}
|
package/src/lib/normalize.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AuditOutputs, Issue } from '@code-pushup/models';
|
|
2
2
|
export declare function normalizeIssue(issue: Issue, gitRoot: string): Issue;
|
|
3
3
|
export declare function normalizeAuditOutputs(audits: AuditOutputs): Promise<AuditOutputs>;
|
package/src/lib/upload.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { uploadToPortal } from '@code-pushup/portal-client';
|
|
2
|
-
import { PersistConfig, UploadConfig } from '@code-pushup/models';
|
|
3
|
-
import { GlobalOptions } from './types';
|
|
2
|
+
import type { PersistConfig, UploadConfig } from '@code-pushup/models';
|
|
3
|
+
import type { GlobalOptions } from './types';
|
|
4
4
|
export type UploadOptions = {
|
|
5
5
|
upload?: UploadConfig;
|
|
6
6
|
} & {
|