@code-pushup/cli 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.
Files changed (29) hide show
  1. package/README.md +20 -5
  2. package/index.js +415 -170
  3. package/package.json +6 -32
  4. package/src/lib/autorun/autorun-command.d.ts +1 -1
  5. package/src/lib/collect/collect-command.d.ts +1 -1
  6. package/src/lib/commands.d.ts +1 -1
  7. package/src/lib/compare/compare-command.d.ts +1 -2
  8. package/src/lib/history/history.model.d.ts +2 -2
  9. package/src/lib/history/history.options.d.ts +2 -2
  10. package/src/lib/history/utils.d.ts +2 -2
  11. package/src/lib/implementation/compare.model.d.ts +4 -2
  12. package/src/lib/implementation/compare.options.d.ts +1 -1
  13. package/src/lib/implementation/core-config.middleware.d.ts +5 -5
  14. package/src/lib/implementation/core-config.options.d.ts +2 -2
  15. package/src/lib/implementation/formatting.d.ts +11 -0
  16. package/src/lib/implementation/global.model.d.ts +2 -2
  17. package/src/lib/implementation/global.options.d.ts +2 -2
  18. package/src/lib/implementation/merge-diffs.model.d.ts +3 -0
  19. package/src/lib/implementation/merge-diffs.options.d.ts +3 -0
  20. package/src/lib/implementation/only-plugins.middleware.d.ts +1 -1
  21. package/src/lib/implementation/only-plugins.model.d.ts +2 -2
  22. package/src/lib/implementation/only-plugins.options.d.ts +3 -2
  23. package/src/lib/implementation/skip-plugins.middleware.d.ts +1 -1
  24. package/src/lib/implementation/skip-plugins.model.d.ts +2 -2
  25. package/src/lib/implementation/skip-plugins.options.d.ts +3 -2
  26. package/src/lib/merge-diffs/merge-diffs-command.d.ts +6 -0
  27. package/src/lib/middlewares.d.ts +1 -1
  28. package/src/lib/upload/upload-command.d.ts +1 -1
  29. package/src/lib/yargs-cli.d.ts +14 -1
package/README.md CHANGED
@@ -268,12 +268,13 @@ Usage:
268
268
  Description:
269
269
  Compare 2 reports and produce a report diff file.
270
270
 
271
- In addition to the [Common Command Options](#common-command-options), the following options are required:
271
+ In addition to the [Common Command Options](#common-command-options), the following options are recognized by the `compare` command:
272
272
 
273
- | Option | Type | Description |
274
- | -------------- | -------- | ----------------------------- |
275
- | **`--before`** | `string` | Path to source `report.json`. |
276
- | **`--after`** | `string` | Path to target `report.json`. |
273
+ | Option | Required | Type | Description |
274
+ | -------------- | :------: | -------- | ----------------------------------- |
275
+ | **`--before`** | yes | `string` | Path to source `report.json`. |
276
+ | **`--after`** | yes | `string` | Path to target `report.json`. |
277
+ | **`--label`** | no | `string` | Label for diff (e.g. project name). |
277
278
 
278
279
  #### `print-config` command
279
280
 
@@ -284,3 +285,17 @@ Description:
284
285
  Print the resolved configuration.
285
286
 
286
287
  Refer to the [Common Command Options](#common-command-options) for the list of available options.
288
+
289
+ ### `merge-diffs` command
290
+
291
+ Usage:
292
+ `code-pushup merge-diffs --files PATH_1 PATH_2 ... [options]`
293
+
294
+ Description:
295
+ Combine multiple report diffs into a single `report-diff.md`.
296
+
297
+ In addition to the [Common Command Options](#common-command-options), the following options are recognized by the `merge-diffs` command:
298
+
299
+ | Option | Required | Type | Description |
300
+ | ------------- | :------: | ---------- | --------------------------------- |
301
+ | **`--files`** | yes | `string[]` | List of `report-diff.json` paths. |
package/index.js CHANGED
@@ -683,6 +683,8 @@ var auditResultSchema = scorableWithPluginMetaSchema.merge(
683
683
  );
684
684
  var reportsDiffSchema = z15.object({
685
685
  commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
686
+ portalUrl: urlSchema.optional().describe("Link to comparison page in Code PushUp portal"),
687
+ label: z15.string().optional().describe("Label (e.g. project name)"),
686
688
  categories: makeArraysComparisonSchema(
687
689
  categoryDiffSchema,
688
690
  categoryResultSchema,
@@ -752,8 +754,24 @@ function comparePairs(pairs, equalsFn) {
752
754
  );
753
755
  }
754
756
 
757
+ // packages/utils/src/lib/errors.ts
758
+ function stringifyError(error) {
759
+ if (error instanceof Error) {
760
+ if (error.name === "Error" || error.message.startsWith(error.name)) {
761
+ return error.message;
762
+ }
763
+ return `${error.name}: ${error.message}`;
764
+ }
765
+ if (typeof error === "string") {
766
+ return error;
767
+ }
768
+ return JSON.stringify(error);
769
+ }
770
+
755
771
  // packages/utils/src/lib/execute-process.ts
756
- import { spawn } from "node:child_process";
772
+ import {
773
+ spawn
774
+ } from "node:child_process";
757
775
 
758
776
  // packages/utils/src/lib/reports/utils.ts
759
777
  import ansis from "ansis";
@@ -880,12 +898,12 @@ function countCategoryAudits(refs, plugins) {
880
898
  }, 0);
881
899
  }
882
900
  function compareCategoryAuditsAndGroups(a, b) {
883
- if (a.weight !== b.weight) {
884
- return b.weight - a.weight;
885
- }
886
901
  if (a.score !== b.score) {
887
902
  return a.score - b.score;
888
903
  }
904
+ if (a.weight !== b.weight) {
905
+ return b.weight - a.weight;
906
+ }
889
907
  if ("value" in a && "value" in b && a.value !== b.value) {
890
908
  return b.value - a.value;
891
909
  }
@@ -977,25 +995,29 @@ var ProcessError = class extends Error {
977
995
  }
978
996
  };
979
997
  function executeProcess(cfg) {
980
- const { observer, cwd, command: command2, args, ignoreExitCode = false } = cfg;
981
- const { onStdout, onError, onComplete } = observer ?? {};
998
+ const { command: command2, args, observer, ignoreExitCode = false, ...options2 } = cfg;
999
+ const { onStdout, onStderr, onError, onComplete } = observer ?? {};
982
1000
  const date = (/* @__PURE__ */ new Date()).toISOString();
983
1001
  const start = performance.now();
984
1002
  return new Promise((resolve, reject) => {
985
- const process2 = spawn(command2, args, { cwd, shell: true });
1003
+ const spawnedProcess = spawn(command2, args ?? [], {
1004
+ shell: true,
1005
+ ...options2
1006
+ });
986
1007
  let stdout = "";
987
1008
  let stderr = "";
988
- process2.stdout.on("data", (data) => {
1009
+ spawnedProcess.stdout.on("data", (data) => {
989
1010
  stdout += String(data);
990
- onStdout?.(String(data));
1011
+ onStdout?.(String(data), spawnedProcess);
991
1012
  });
992
- process2.stderr.on("data", (data) => {
1013
+ spawnedProcess.stderr.on("data", (data) => {
993
1014
  stderr += String(data);
1015
+ onStderr?.(String(data), spawnedProcess);
994
1016
  });
995
- process2.on("error", (err) => {
1017
+ spawnedProcess.on("error", (err) => {
996
1018
  stderr += err.toString();
997
1019
  });
998
- process2.on("close", (code2) => {
1020
+ spawnedProcess.on("close", (code2) => {
999
1021
  const timings = { date, duration: calcDuration(start) };
1000
1022
  if (code2 === 0 || ignoreExitCode) {
1001
1023
  onComplete?.();
@@ -1569,7 +1591,10 @@ function getColumnAlignments(tableData) {
1569
1591
  }
1570
1592
 
1571
1593
  // packages/utils/src/lib/reports/formatting.ts
1572
- import { MarkdownDocument, md as md2 } from "build-md";
1594
+ import {
1595
+ MarkdownDocument,
1596
+ md as md2
1597
+ } from "build-md";
1573
1598
  function tableSection(tableData, options2) {
1574
1599
  if (tableData.rows.length === 0) {
1575
1600
  return null;
@@ -1890,17 +1915,17 @@ function pluginMetaTable({
1890
1915
  { heading: "Version", alignment: "center" },
1891
1916
  { heading: "Duration", alignment: "right" }
1892
1917
  ],
1893
- plugins.map(({ title, audits, version: version2 = "", duration }) => [
1918
+ plugins.map(({ title, audits, version: version3 = "", duration }) => [
1894
1919
  title,
1895
1920
  audits.length.toString(),
1896
- version2 && md4.code(version2),
1921
+ version3 && md4.code(version3),
1897
1922
  formatDuration(duration)
1898
1923
  ])
1899
1924
  ];
1900
1925
  }
1901
1926
  function reportMetaTable({
1902
1927
  commit,
1903
- version: version2,
1928
+ version: version3,
1904
1929
  duration,
1905
1930
  plugins,
1906
1931
  categories
@@ -1917,7 +1942,7 @@ function reportMetaTable({
1917
1942
  [
1918
1943
  [
1919
1944
  commit ? `${commit.message} (${commit.hash})` : "N/A",
1920
- md4.code(version2),
1945
+ md4.code(version3),
1921
1946
  formatDuration(duration),
1922
1947
  plugins.length.toString(),
1923
1948
  categories.length.toString(),
@@ -1929,19 +1954,111 @@ function reportMetaTable({
1929
1954
 
1930
1955
  // packages/utils/src/lib/reports/generate-md-reports-diff.ts
1931
1956
  import {
1932
- MarkdownDocument as MarkdownDocument4,
1933
- md as md5
1957
+ MarkdownDocument as MarkdownDocument5,
1958
+ md as md6
1934
1959
  } from "build-md";
1960
+
1961
+ // packages/utils/src/lib/reports/generate-md-reports-diff-utils.ts
1962
+ import { MarkdownDocument as MarkdownDocument4, md as md5 } from "build-md";
1935
1963
  var MAX_ROWS = 100;
1936
- function generateMdReportsDiff(diff, portalUrl) {
1937
- return new MarkdownDocument4().$concat(
1938
- createDiffHeaderSection(diff, portalUrl),
1939
- createDiffCategoriesSection(diff),
1940
- createDiffGroupsSection(diff),
1941
- createDiffAuditsSection(diff)
1942
- ).toString();
1964
+ function summarizeUnchanged(token, { changed, unchanged }) {
1965
+ const pluralizedCount = changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`;
1966
+ const pluralizedVerb = unchanged.length === 1 ? "is" : "are";
1967
+ return `${pluralizedCount} ${pluralizedVerb} unchanged.`;
1968
+ }
1969
+ function summarizeDiffOutcomes(outcomes, token) {
1970
+ return objectToEntries(countDiffOutcomes(outcomes)).filter(
1971
+ (entry) => entry[0] !== "unchanged" && entry[1] > 0
1972
+ ).map(([outcome, count]) => {
1973
+ const formattedCount = `<strong>${count}</strong> ${pluralize(
1974
+ token,
1975
+ count
1976
+ )}`;
1977
+ switch (outcome) {
1978
+ case "positive":
1979
+ return `\u{1F44D} ${formattedCount} improved`;
1980
+ case "negative":
1981
+ return `\u{1F44E} ${formattedCount} regressed`;
1982
+ case "mixed":
1983
+ return `${formattedCount} changed without impacting score`;
1984
+ }
1985
+ }).join(", ");
1986
+ }
1987
+ function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
1988
+ if (changed.length === 0) {
1989
+ return new MarkdownDocument4().paragraph(
1990
+ summarizeUnchanged(token, { changed, unchanged })
1991
+ );
1992
+ }
1993
+ return new MarkdownDocument4().table(columns, rows.slice(0, MAX_ROWS)).paragraph(
1994
+ changed.length > MAX_ROWS && md5.italic(
1995
+ `Only the ${MAX_ROWS} most affected ${pluralize(
1996
+ token
1997
+ )} are listed above for brevity.`
1998
+ )
1999
+ ).paragraph(
2000
+ unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
2001
+ );
2002
+ }
2003
+ function formatTitle({
2004
+ title,
2005
+ docsUrl
2006
+ }) {
2007
+ if (docsUrl) {
2008
+ return md5.link(docsUrl, title);
2009
+ }
2010
+ return title;
2011
+ }
2012
+ function formatPortalLink(portalUrl) {
2013
+ return portalUrl && md5.link(portalUrl, "\u{1F575}\uFE0F See full comparison in Code PushUp portal \u{1F50D}");
2014
+ }
2015
+ function sortChanges(changes) {
2016
+ return [...changes].sort(
2017
+ (a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
2018
+ );
2019
+ }
2020
+ function getDiffChanges(diff) {
2021
+ return [
2022
+ ...diff.categories.changed,
2023
+ ...diff.groups.changed,
2024
+ ...diff.audits.changed
2025
+ ];
2026
+ }
2027
+ function changesToDiffOutcomes(changes) {
2028
+ return changes.map((change) => {
2029
+ if (change.scores.diff > 0) {
2030
+ return "positive";
2031
+ }
2032
+ if (change.scores.diff < 0) {
2033
+ return "negative";
2034
+ }
2035
+ if (change.values != null && change.values.diff !== 0) {
2036
+ return "mixed";
2037
+ }
2038
+ return "unchanged";
2039
+ });
2040
+ }
2041
+ function mergeDiffOutcomes(outcomes) {
2042
+ if (outcomes.every((outcome) => outcome === "unchanged")) {
2043
+ return "unchanged";
2044
+ }
2045
+ if (outcomes.includes("positive") && !outcomes.includes("negative")) {
2046
+ return "positive";
2047
+ }
2048
+ if (outcomes.includes("negative") && !outcomes.includes("positive")) {
2049
+ return "negative";
2050
+ }
2051
+ return "mixed";
1943
2052
  }
1944
- function createDiffHeaderSection(diff, portalUrl) {
2053
+ function countDiffOutcomes(outcomes) {
2054
+ return {
2055
+ positive: outcomes.filter((outcome) => outcome === "positive").length,
2056
+ negative: outcomes.filter((outcome) => outcome === "negative").length,
2057
+ mixed: outcomes.filter((outcome) => outcome === "mixed").length,
2058
+ unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
2059
+ };
2060
+ }
2061
+ function formatReportOutcome(outcome, commits) {
1945
2062
  const outcomeTexts = {
1946
2063
  positive: md5`🥳 Code PushUp report has ${md5.bold("improved")}`,
1947
2064
  negative: md5`😟 Code PushUp report has ${md5.bold("regressed")}`,
@@ -1950,27 +2067,91 @@ function createDiffHeaderSection(diff, portalUrl) {
1950
2067
  )}`,
1951
2068
  unchanged: md5`😐 Code PushUp report is ${md5.bold("unchanged")}`
1952
2069
  };
2070
+ if (commits) {
2071
+ const commitsText = `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
2072
+ return md5`${outcomeTexts[outcome]} – ${commitsText}.`;
2073
+ }
2074
+ return md5`${outcomeTexts[outcome]}.`;
2075
+ }
2076
+ function compareDiffsBy(type, a, b) {
2077
+ return sumScoreChanges(b[type].changed) - sumScoreChanges(a[type].changed) || sumConfigChanges(b[type]) - sumConfigChanges(a[type]);
2078
+ }
2079
+ function sumScoreChanges(changes) {
2080
+ return changes.reduce(
2081
+ (acc, { scores }) => acc + Math.abs(scores.diff),
2082
+ 0
2083
+ );
2084
+ }
2085
+ function sumConfigChanges({
2086
+ added,
2087
+ removed
2088
+ }) {
2089
+ return added.length + removed.length;
2090
+ }
2091
+
2092
+ // packages/utils/src/lib/reports/generate-md-reports-diff.ts
2093
+ function generateMdReportsDiff(diff) {
2094
+ return new MarkdownDocument5().$concat(
2095
+ createDiffHeaderSection(diff),
2096
+ createDiffCategoriesSection(diff),
2097
+ createDiffDetailsSection(diff)
2098
+ ).toString();
2099
+ }
2100
+ function generateMdReportsDiffForMonorepo(diffs) {
2101
+ const diffsWithOutcomes = diffs.map((diff) => ({
2102
+ ...diff,
2103
+ outcome: mergeDiffOutcomes(changesToDiffOutcomes(getDiffChanges(diff)))
2104
+ })).sort(
2105
+ (a, b) => compareDiffsBy("categories", a, b) || compareDiffsBy("groups", a, b) || compareDiffsBy("audits", a, b) || a.label.localeCompare(b.label)
2106
+ );
2107
+ const unchanged = diffsWithOutcomes.filter(
2108
+ ({ outcome }) => outcome === "unchanged"
2109
+ );
2110
+ const changed = diffsWithOutcomes.filter((diff) => !unchanged.includes(diff));
2111
+ return new MarkdownDocument5().$concat(
2112
+ createDiffHeaderSection(diffs),
2113
+ ...changed.map(createDiffProjectSection)
2114
+ ).$if(
2115
+ unchanged.length > 0,
2116
+ (doc) => doc.rule().paragraph(summarizeUnchanged("project", { unchanged, changed }))
2117
+ ).toString();
2118
+ }
2119
+ function createDiffHeaderSection(diff) {
1953
2120
  const outcome = mergeDiffOutcomes(
1954
- changesToDiffOutcomes([
1955
- ...diff.categories.changed,
1956
- ...diff.groups.changed,
1957
- ...diff.audits.changed
1958
- ])
2121
+ changesToDiffOutcomes(toArray(diff).flatMap(getDiffChanges))
1959
2122
  );
1960
- const styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
1961
- return new MarkdownDocument4().heading(HIERARCHY.level_1, "Code PushUp").paragraph(
1962
- diff.commits ? md5`${outcomeTexts[outcome]} – ${styleCommits(diff.commits)}.` : outcomeTexts[outcome]
1963
- ).paragraph(
1964
- portalUrl && md5.link(portalUrl, "\u{1F575}\uFE0F See full comparison in Code PushUp portal \u{1F50D}")
2123
+ const commits = Array.isArray(diff) ? diff[0]?.commits : diff.commits;
2124
+ const portalUrl = Array.isArray(diff) ? void 0 : diff.portalUrl;
2125
+ return new MarkdownDocument5().heading(HIERARCHY.level_1, "Code PushUp").paragraph(formatReportOutcome(outcome, commits)).paragraph(formatPortalLink(portalUrl));
2126
+ }
2127
+ function createDiffProjectSection(diff) {
2128
+ return new MarkdownDocument5().heading(HIERARCHY.level_2, md6`💼 Project ${md6.code(diff.label)}`).paragraph(formatReportOutcome(diff.outcome)).paragraph(formatPortalLink(diff.portalUrl)).$concat(
2129
+ createDiffCategoriesSection(diff, {
2130
+ skipHeading: true,
2131
+ skipUnchanged: true
2132
+ }),
2133
+ createDiffDetailsSection(diff, HIERARCHY.level_3)
1965
2134
  );
1966
2135
  }
1967
- function createDiffCategoriesSection(diff) {
2136
+ function createDiffCategoriesSection(diff, options2) {
1968
2137
  const { changed, unchanged, added } = diff.categories;
2138
+ const { skipHeading, skipUnchanged } = options2 ?? {};
1969
2139
  const categoriesCount = changed.length + unchanged.length + added.length;
1970
2140
  const hasChanges = unchanged.length < categoriesCount;
1971
2141
  if (categoriesCount === 0) {
1972
2142
  return null;
1973
2143
  }
2144
+ const [columns, rows] = createCategoriesTable(diff, {
2145
+ hasChanges,
2146
+ skipUnchanged
2147
+ });
2148
+ return new MarkdownDocument5().heading(HIERARCHY.level_2, !skipHeading && "\u{1F3F7}\uFE0F Categories").table(columns, rows).paragraph(added.length > 0 && md6.italic("(\\*) New category.")).paragraph(
2149
+ skipUnchanged && unchanged.length > 0 && summarizeUnchanged("category", { changed, unchanged })
2150
+ );
2151
+ }
2152
+ function createCategoriesTable(diff, options2) {
2153
+ const { changed, unchanged, added } = diff.categories;
2154
+ const { hasChanges, skipUnchanged } = options2;
1974
2155
  const columns = [
1975
2156
  { heading: "\u{1F3F7}\uFE0F Category", alignment: "left" },
1976
2157
  {
@@ -1991,27 +2172,43 @@ function createDiffCategoriesSection(diff) {
1991
2172
  ]),
1992
2173
  ...added.map((category) => [
1993
2174
  formatTitle(category),
1994
- md5.italic("n/a (\\*)"),
2175
+ md6.italic("n/a (\\*)"),
1995
2176
  formatScoreWithColor(category.score),
1996
- md5.italic("n/a (\\*)")
2177
+ md6.italic("n/a (\\*)")
1997
2178
  ]),
1998
- ...unchanged.map((category) => [
2179
+ ...skipUnchanged ? [] : unchanged.map((category) => [
1999
2180
  formatTitle(category),
2000
2181
  formatScoreWithColor(category.score, { skipBold: true }),
2001
2182
  formatScoreWithColor(category.score),
2002
2183
  "\u2013"
2003
2184
  ])
2004
2185
  ];
2005
- return new MarkdownDocument4().heading(HIERARCHY.level_2, "\u{1F3F7}\uFE0F Categories").table(
2186
+ return [
2006
2187
  hasChanges ? columns : columns.slice(0, 2),
2007
2188
  rows.map((row) => hasChanges ? row : row.slice(0, 2))
2008
- ).paragraph(added.length > 0 && md5.italic("(\\*) New category."));
2189
+ ];
2190
+ }
2191
+ function createDiffDetailsSection(diff, level = HIERARCHY.level_2) {
2192
+ if (diff.groups.changed.length + diff.audits.changed.length === 0) {
2193
+ return null;
2194
+ }
2195
+ const summary = ["group", "audit"].map(
2196
+ (token) => summarizeDiffOutcomes(
2197
+ changesToDiffOutcomes(diff[`${token}s`].changed),
2198
+ token
2199
+ )
2200
+ ).filter(Boolean).join(", ");
2201
+ const details2 = new MarkdownDocument5().$concat(
2202
+ createDiffGroupsSection(diff, level),
2203
+ createDiffAuditsSection(diff, level)
2204
+ );
2205
+ return new MarkdownDocument5().details(summary, details2);
2009
2206
  }
2010
- function createDiffGroupsSection(diff) {
2207
+ function createDiffGroupsSection(diff, level) {
2011
2208
  if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
2012
2209
  return null;
2013
2210
  }
2014
- return new MarkdownDocument4().heading(HIERARCHY.level_2, "\u{1F5C3}\uFE0F Groups").$concat(
2211
+ return new MarkdownDocument5().heading(level, "\u{1F5C3}\uFE0F Groups").$concat(
2015
2212
  createGroupsOrAuditsDetails(
2016
2213
  "group",
2017
2214
  diff.groups,
@@ -2032,8 +2229,8 @@ function createDiffGroupsSection(diff) {
2032
2229
  )
2033
2230
  );
2034
2231
  }
2035
- function createDiffAuditsSection(diff) {
2036
- return new MarkdownDocument4().heading(HIERARCHY.level_2, "\u{1F6E1}\uFE0F Audits").$concat(
2232
+ function createDiffAuditsSection(diff, level) {
2233
+ return new MarkdownDocument5().heading(level, "\u{1F6E1}\uFE0F Audits").$concat(
2037
2234
  createGroupsOrAuditsDetails(
2038
2235
  "audit",
2039
2236
  diff.audits,
@@ -2048,7 +2245,7 @@ function createDiffAuditsSection(diff) {
2048
2245
  formatTitle(audit.plugin),
2049
2246
  formatTitle(audit),
2050
2247
  `${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
2051
- md5`${scoreMarker(audit.scores.after, "square")} ${md5.bold(
2248
+ md6`${scoreMarker(audit.scores.after, "square")} ${md6.bold(
2052
2249
  audit.displayValues.after || audit.values.after.toString()
2053
2250
  )}`,
2054
2251
  formatValueChange(audit)
@@ -2056,96 +2253,6 @@ function createDiffAuditsSection(diff) {
2056
2253
  )
2057
2254
  );
2058
2255
  }
2059
- function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
2060
- if (changed.length === 0) {
2061
- return new MarkdownDocument4().paragraph(
2062
- summarizeUnchanged(token, { changed, unchanged })
2063
- );
2064
- }
2065
- return new MarkdownDocument4().details(
2066
- summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
2067
- md5`${md5.table(columns, rows.slice(0, MAX_ROWS))}${changed.length > MAX_ROWS ? md5.paragraph(
2068
- md5.italic(
2069
- `Only the ${MAX_ROWS} most affected ${pluralize(
2070
- token
2071
- )} are listed above for brevity.`
2072
- )
2073
- ) : ""}${unchanged.length > 0 ? md5.paragraph(summarizeUnchanged(token, { changed, unchanged })) : ""}`
2074
- );
2075
- }
2076
- function summarizeUnchanged(token, { changed, unchanged }) {
2077
- return [
2078
- changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
2079
- unchanged.length === 1 ? "is" : "are",
2080
- "unchanged."
2081
- ].join(" ");
2082
- }
2083
- function summarizeDiffOutcomes(outcomes, token) {
2084
- return objectToEntries(countDiffOutcomes(outcomes)).filter(
2085
- (entry) => entry[0] !== "unchanged" && entry[1] > 0
2086
- ).map(([outcome, count]) => {
2087
- const formattedCount = `<strong>${count}</strong> ${pluralize(
2088
- token,
2089
- count
2090
- )}`;
2091
- switch (outcome) {
2092
- case "positive":
2093
- return `\u{1F44D} ${formattedCount} improved`;
2094
- case "negative":
2095
- return `\u{1F44E} ${formattedCount} regressed`;
2096
- case "mixed":
2097
- return `${formattedCount} changed without impacting score`;
2098
- }
2099
- }).join(", ");
2100
- }
2101
- function formatTitle({
2102
- title,
2103
- docsUrl
2104
- }) {
2105
- if (docsUrl) {
2106
- return md5.link(docsUrl, title);
2107
- }
2108
- return title;
2109
- }
2110
- function sortChanges(changes) {
2111
- return [...changes].sort(
2112
- (a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
2113
- );
2114
- }
2115
- function changesToDiffOutcomes(changes) {
2116
- return changes.map((change) => {
2117
- if (change.scores.diff > 0) {
2118
- return "positive";
2119
- }
2120
- if (change.scores.diff < 0) {
2121
- return "negative";
2122
- }
2123
- if (change.values != null && change.values.diff !== 0) {
2124
- return "mixed";
2125
- }
2126
- return "unchanged";
2127
- });
2128
- }
2129
- function mergeDiffOutcomes(outcomes) {
2130
- if (outcomes.every((outcome) => outcome === "unchanged")) {
2131
- return "unchanged";
2132
- }
2133
- if (outcomes.includes("positive") && !outcomes.includes("negative")) {
2134
- return "positive";
2135
- }
2136
- if (outcomes.includes("negative") && !outcomes.includes("positive")) {
2137
- return "negative";
2138
- }
2139
- return "mixed";
2140
- }
2141
- function countDiffOutcomes(outcomes) {
2142
- return {
2143
- positive: outcomes.filter((outcome) => outcome === "positive").length,
2144
- negative: outcomes.filter((outcome) => outcome === "negative").length,
2145
- mixed: outcomes.filter((outcome) => outcome === "mixed").length,
2146
- unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
2147
- };
2148
- }
2149
2256
 
2150
2257
  // packages/utils/src/lib/reports/load-report.ts
2151
2258
  import { join as join2 } from "node:path";
@@ -2178,8 +2285,8 @@ function logStdoutSummary(report) {
2178
2285
  log();
2179
2286
  }
2180
2287
  function reportToHeaderSection(report) {
2181
- const { packageName, version: version2 } = report;
2182
- return `${bold4(REPORT_HEADLINE_TEXT)} - ${packageName}@${version2}`;
2288
+ const { packageName, version: version3 } = report;
2289
+ return `${bold4(REPORT_HEADLINE_TEXT)} - ${packageName}@${version3}`;
2183
2290
  }
2184
2291
  function logPlugins(report) {
2185
2292
  const { plugins } = report;
@@ -2202,7 +2309,8 @@ function logPlugins(report) {
2202
2309
  },
2203
2310
  {
2204
2311
  text: cyanBright(audit.displayValue || `${audit.value}`),
2205
- width: 10,
2312
+ // eslint-disable-next-line no-magic-numbers
2313
+ width: 20,
2206
2314
  padding: [0, 0, 0, 0]
2207
2315
  }
2208
2316
  ]);
@@ -2355,7 +2463,7 @@ var verboseUtils = (verbose = false) => ({
2355
2463
 
2356
2464
  // packages/core/package.json
2357
2465
  var name = "@code-pushup/core";
2358
- var version = "0.49.0";
2466
+ var version = "0.50.0";
2359
2467
 
2360
2468
  // packages/core/src/lib/implementation/execute-plugin.ts
2361
2469
  import { bold as bold5 } from "ansis";
@@ -2751,7 +2859,7 @@ function selectMeta(meta) {
2751
2859
  }
2752
2860
 
2753
2861
  // packages/core/src/lib/compare.ts
2754
- async function compareReportFiles(inputPaths, persistConfig, uploadConfig) {
2862
+ async function compareReportFiles(inputPaths, persistConfig, uploadConfig, label) {
2755
2863
  const { outputDir, filename, format } = persistConfig;
2756
2864
  const [reportBefore, reportAfter] = await Promise.all([
2757
2865
  readJsonFile(inputPaths.before),
@@ -2761,12 +2869,20 @@ async function compareReportFiles(inputPaths, persistConfig, uploadConfig) {
2761
2869
  before: reportSchema.parse(reportBefore),
2762
2870
  after: reportSchema.parse(reportAfter)
2763
2871
  };
2764
- const reportsDiff = compareReports(reports);
2765
- const portalUrl = uploadConfig && reportsDiff.commits && format.includes("md") ? await fetchPortalComparisonLink(uploadConfig, reportsDiff.commits) : void 0;
2872
+ const diff = compareReports(reports);
2873
+ if (label) {
2874
+ diff.label = label;
2875
+ }
2876
+ if (uploadConfig && diff.commits) {
2877
+ diff.portalUrl = await fetchPortalComparisonLink(
2878
+ uploadConfig,
2879
+ diff.commits
2880
+ );
2881
+ }
2766
2882
  return Promise.all(
2767
2883
  format.map(async (fmt) => {
2768
2884
  const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
2769
- const content = reportsDiffToFileContent(reportsDiff, fmt, portalUrl);
2885
+ const content = reportsDiffToFileContent(diff, fmt);
2770
2886
  await ensureDirectoryExists(outputDir);
2771
2887
  await writeFile2(outputPath, content);
2772
2888
  return outputPath;
@@ -2796,12 +2912,12 @@ function compareReports(reports) {
2796
2912
  duration
2797
2913
  };
2798
2914
  }
2799
- function reportsDiffToFileContent(reportsDiff, format, portalUrl) {
2915
+ function reportsDiffToFileContent(reportsDiff, format) {
2800
2916
  switch (format) {
2801
2917
  case "json":
2802
2918
  return JSON.stringify(reportsDiff, null, 2);
2803
2919
  case "md":
2804
- return generateMdReportsDiff(reportsDiff, portalUrl ?? void 0);
2920
+ return generateMdReportsDiff(reportsDiff);
2805
2921
  }
2806
2922
  }
2807
2923
  async function fetchPortalComparisonLink(uploadConfig, commits) {
@@ -3074,6 +3190,45 @@ async function autoloadRc(tsconfig) {
3074
3190
  );
3075
3191
  }
3076
3192
 
3193
+ // packages/core/src/lib/merge-diffs.ts
3194
+ import { writeFile as writeFile3 } from "node:fs/promises";
3195
+ import { basename, dirname, join as join7 } from "node:path";
3196
+ async function mergeDiffs(files, persistConfig) {
3197
+ const results = await Promise.allSettled(
3198
+ files.map(async (file) => {
3199
+ const json = await readJsonFile(file).catch((error) => {
3200
+ throw new Error(
3201
+ `Failed to read JSON file ${file} - ${stringifyError(error)}`
3202
+ );
3203
+ });
3204
+ const result = await reportsDiffSchema.safeParseAsync(json);
3205
+ if (!result.success) {
3206
+ throw new Error(
3207
+ `Invalid reports diff in ${file} - ${result.error.message}`
3208
+ );
3209
+ }
3210
+ return { ...result.data, file };
3211
+ })
3212
+ );
3213
+ results.filter(isPromiseRejectedResult).forEach(({ reason }) => {
3214
+ ui().logger.warning(
3215
+ `Skipped invalid report diff - ${stringifyError(reason)}`
3216
+ );
3217
+ });
3218
+ const diffs = results.filter(isPromiseFulfilledResult).map(({ value }) => value);
3219
+ const labeledDiffs = diffs.map((diff) => ({
3220
+ ...diff,
3221
+ label: diff.label || basename(dirname(diff.file))
3222
+ // fallback is parent folder name
3223
+ }));
3224
+ const markdown = generateMdReportsDiffForMonorepo(labeledDiffs);
3225
+ const { outputDir, filename } = persistConfig;
3226
+ const outputPath = join7(outputDir, `${filename}-diff.md`);
3227
+ await ensureDirectoryExists(outputDir);
3228
+ await writeFile3(outputPath, markdown);
3229
+ return outputPath;
3230
+ }
3231
+
3077
3232
  // packages/cli/src/lib/constants.ts
3078
3233
  var CLI_NAME = "Code PushUp CLI";
3079
3234
  var CLI_SCRIPT_NAME = "code-pushup";
@@ -3207,6 +3362,10 @@ function yargsCompareOptionsDefinition() {
3207
3362
  describe: "Path to target report.json",
3208
3363
  type: "string",
3209
3364
  demandOption: true
3365
+ },
3366
+ label: {
3367
+ describe: "Label for diff (e.g. project name)",
3368
+ type: "string"
3210
3369
  }
3211
3370
  };
3212
3371
  }
@@ -3222,11 +3381,12 @@ function yargsCompareCommandObject() {
3222
3381
  ui().logger.log(bold9(CLI_NAME));
3223
3382
  ui().logger.info(gray6(`Run ${command2}...`));
3224
3383
  const options2 = args;
3225
- const { before, after, persist, upload: upload2 } = options2;
3384
+ const { before, after, label, persist, upload: upload2 } = options2;
3226
3385
  const outputPaths = await compareReportFiles(
3227
3386
  { before, after },
3228
3387
  persist,
3229
- upload2
3388
+ upload2,
3389
+ label
3230
3390
  );
3231
3391
  ui().logger.info(
3232
3392
  `Reports diff written to ${outputPaths.map((path) => bold9(path)).join(" and ")}`
@@ -3412,6 +3572,38 @@ function yargsHistoryCommandObject() {
3412
3572
  };
3413
3573
  }
3414
3574
 
3575
+ // packages/cli/src/lib/merge-diffs/merge-diffs-command.ts
3576
+ import { bold as bold11, gray as gray8 } from "ansis";
3577
+
3578
+ // packages/cli/src/lib/implementation/merge-diffs.options.ts
3579
+ function yargsMergeDiffsOptionsDefinition() {
3580
+ return {
3581
+ files: {
3582
+ describe: "List of report-diff.json paths",
3583
+ type: "array",
3584
+ demandOption: true
3585
+ }
3586
+ };
3587
+ }
3588
+
3589
+ // packages/cli/src/lib/merge-diffs/merge-diffs-command.ts
3590
+ function yargsMergeDiffsCommandObject() {
3591
+ const command2 = "merge-diffs";
3592
+ return {
3593
+ command: command2,
3594
+ describe: "Combine many report diffs into a single diff file",
3595
+ builder: yargsMergeDiffsOptionsDefinition(),
3596
+ handler: async (args) => {
3597
+ ui().logger.log(bold11(CLI_NAME));
3598
+ ui().logger.info(gray8(`Run ${command2}...`));
3599
+ const options2 = args;
3600
+ const { files, persist } = options2;
3601
+ const outputPath = await mergeDiffs(files, persist);
3602
+ ui().logger.info(`Reports diff written to ${bold11(outputPath)}`);
3603
+ }
3604
+ };
3605
+ }
3606
+
3415
3607
  // packages/cli/src/lib/print-config/print-config-command.ts
3416
3608
  function yargsConfigCommandObject() {
3417
3609
  const command2 = "print-config";
@@ -3427,15 +3619,15 @@ function yargsConfigCommandObject() {
3427
3619
  }
3428
3620
 
3429
3621
  // packages/cli/src/lib/upload/upload-command.ts
3430
- import { bold as bold11, gray as gray8 } from "ansis";
3622
+ import { bold as bold12, gray as gray9 } from "ansis";
3431
3623
  function yargsUploadCommandObject() {
3432
3624
  const command2 = "upload";
3433
3625
  return {
3434
3626
  command: command2,
3435
3627
  describe: "Upload report results to the portal",
3436
3628
  handler: async (args) => {
3437
- ui().logger.log(bold11(CLI_NAME));
3438
- ui().logger.info(gray8(`Run ${command2}...`));
3629
+ ui().logger.log(bold12(CLI_NAME));
3630
+ ui().logger.info(gray9(`Run ${command2}...`));
3439
3631
  const options2 = args;
3440
3632
  if (options2.upload == null) {
3441
3633
  renderIntegratePortalHint();
@@ -3458,7 +3650,8 @@ var commands = [
3458
3650
  yargsUploadCommandObject(),
3459
3651
  yargsHistoryCommandObject(),
3460
3652
  yargsCompareCommandObject(),
3461
- yargsConfigCommandObject()
3653
+ yargsConfigCommandObject(),
3654
+ yargsMergeDiffsCommandObject()
3462
3655
  ];
3463
3656
 
3464
3657
  // packages/cli/src/lib/implementation/core-config.middleware.ts
@@ -3670,7 +3863,7 @@ function yargsGlobalOptionsDefinition() {
3670
3863
  default: false
3671
3864
  },
3672
3865
  config: {
3673
- describe: "Path to config file, e.g. code-pushup.config.ts. By default it loads code-pushup.config.(ts|mjs|js).",
3866
+ describe: "Path to config file. By default it loads code-pushup.config.(ts|mjs|js).",
3674
3867
  type: "string"
3675
3868
  },
3676
3869
  tsconfig: {
@@ -3698,8 +3891,55 @@ var groups = {
3698
3891
  };
3699
3892
 
3700
3893
  // packages/cli/src/lib/yargs-cli.ts
3701
- import { bold as bold12 } from "ansis";
3894
+ import { blue, dim as dim2, green as green4 } from "ansis";
3702
3895
  import yargs from "yargs";
3896
+
3897
+ // packages/cli/package.json
3898
+ var version2 = "0.50.0";
3899
+
3900
+ // packages/cli/src/lib/implementation/formatting.ts
3901
+ import { bold as bold13, dim, green as green3 } from "ansis";
3902
+ function titleStyle(title) {
3903
+ return `${bold13(title)}`;
3904
+ }
3905
+ function headerStyle(title) {
3906
+ return `${green3(title)}`;
3907
+ }
3908
+ function descriptionStyle(title) {
3909
+ return `${dim(title)}`;
3910
+ }
3911
+ function formatObjectValue(opts, propName) {
3912
+ const description = opts[propName];
3913
+ return {
3914
+ ...opts,
3915
+ ...typeof description === "string" && {
3916
+ [propName]: descriptionStyle(description)
3917
+ }
3918
+ };
3919
+ }
3920
+ function formatNestedValues(options2, propName) {
3921
+ return Object.fromEntries(
3922
+ Object.entries(options2).map(([key, opts]) => [
3923
+ key,
3924
+ formatObjectValue(opts, propName)
3925
+ ])
3926
+ );
3927
+ }
3928
+
3929
+ // packages/cli/src/lib/yargs-cli.ts
3930
+ var yargsDecorator = {
3931
+ "Commands:": `${green4("Commands")}:`,
3932
+ "Options:": `${green4("Options")}:`,
3933
+ "Examples:": `${green4("Examples")}:`,
3934
+ boolean: blue("boolean"),
3935
+ count: blue("count"),
3936
+ string: blue("string"),
3937
+ array: blue("array"),
3938
+ required: blue("required"),
3939
+ "default:": `${blue("default")}:`,
3940
+ "choices:": `${blue("choices")}:`,
3941
+ "aliases:": `${blue("aliases")}:`
3942
+ };
3703
3943
  function yargsCli(argv, cfg) {
3704
3944
  const { usageMessage, scriptName, noExitProcess } = cfg;
3705
3945
  const commands2 = cfg.commands ?? [];
@@ -3708,7 +3948,7 @@ function yargsCli(argv, cfg) {
3708
3948
  const groups2 = cfg.groups ?? {};
3709
3949
  const examples = cfg.examples ?? [];
3710
3950
  const cli2 = yargs(argv);
3711
- cli2.help().version(false).alias("h", "help").check((args) => {
3951
+ cli2.updateLocale(yargsDecorator).wrap(Math.max(TERMINAL_WIDTH, cli2.terminalWidth())).help("help", descriptionStyle("Show help")).alias("h", "help").showHelpOnFail(false).version("version", dim2`Show version`, version2).check((args) => {
3712
3952
  const persist = args["persist"];
3713
3953
  return persist == null || validatePersistFormat(persist);
3714
3954
  }).parserConfiguration({
@@ -3716,18 +3956,18 @@ function yargsCli(argv, cfg) {
3716
3956
  }).coerce(
3717
3957
  "config",
3718
3958
  (config) => Array.isArray(config) ? config.at(-1) : config
3719
- ).options(options2).wrap(TERMINAL_WIDTH);
3959
+ ).options(formatNestedValues(options2, "describe"));
3720
3960
  if (usageMessage) {
3721
- cli2.usage(bold12(usageMessage));
3961
+ cli2.usage(titleStyle(usageMessage));
3722
3962
  }
3723
3963
  if (scriptName) {
3724
3964
  cli2.scriptName(scriptName);
3725
3965
  }
3726
3966
  examples.forEach(
3727
- ([exampleName, description]) => cli2.example(exampleName, description)
3967
+ ([exampleName, description]) => cli2.example(exampleName, descriptionStyle(description))
3728
3968
  );
3729
3969
  Object.entries(groups2).forEach(
3730
- ([groupName, optionNames]) => cli2.group(optionNames, groupName)
3970
+ ([groupName, optionNames]) => cli2.group(optionNames, headerStyle(groupName))
3731
3971
  );
3732
3972
  middlewares2.forEach(({ middlewareFunction, applyBeforeValidation }) => {
3733
3973
  cli2.middleware(
@@ -3736,13 +3976,18 @@ function yargsCli(argv, cfg) {
3736
3976
  );
3737
3977
  });
3738
3978
  commands2.forEach((commandObj) => {
3739
- cli2.command({
3740
- ...commandObj,
3741
- handler: logErrorBeforeThrow(commandObj.handler),
3742
- ...typeof commandObj.builder === "function" && {
3743
- builder: logErrorBeforeThrow(commandObj.builder)
3744
- }
3745
- });
3979
+ cli2.command(
3980
+ formatObjectValue(
3981
+ {
3982
+ ...commandObj,
3983
+ handler: logErrorBeforeThrow(commandObj.handler),
3984
+ ...typeof commandObj.builder === "function" && {
3985
+ builder: logErrorBeforeThrow(commandObj.builder)
3986
+ }
3987
+ },
3988
+ "describe"
3989
+ )
3990
+ );
3746
3991
  });
3747
3992
  if (noExitProcess) {
3748
3993
  cli2.exitProcess(false);
@@ -3788,8 +4033,8 @@ var cli = (args) => yargsCli(args, {
3788
4033
  "Run collect skiping the coverage plugin, other plugins from config file will be included."
3789
4034
  ],
3790
4035
  [
3791
- "code-pushup upload --persist.outputDir=dist --persist.filename=cp-report --upload.apiKey=$CP_API_KEY",
3792
- "Upload dist/cp-report.json to portal using API key from environment variable"
4036
+ "code-pushup upload --persist.outputDir=dist --upload.apiKey=$CP_API_KEY",
4037
+ "Upload dist/report.json to portal using API key from environment variable"
3793
4038
  ],
3794
4039
  [
3795
4040
  "code-pushup print-config --config code-pushup.config.test.js",
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@code-pushup/cli",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "license": "MIT",
5
+ "description": "A CLI to run all kinds of code quality measurements to align your team with company goals",
5
6
  "bin": {
6
7
  "code-pushup": "index.js"
7
8
  },
8
9
  "dependencies": {
9
- "@code-pushup/models": "0.49.0",
10
- "@code-pushup/core": "0.49.0",
11
- "@code-pushup/utils": "0.49.0",
10
+ "@code-pushup/models": "0.50.0",
11
+ "@code-pushup/core": "0.50.0",
12
+ "@code-pushup/utils": "0.50.0",
12
13
  "yargs": "^17.7.2",
13
14
  "ansis": "^3.3.0",
14
15
  "simple-git": "^3.20.0"
15
16
  },
16
- "homepage": "https://github.com/code-pushup/cli#readme",
17
+ "homepage": "code-pushup.dev",
17
18
  "bugs": {
18
19
  "url": "https://github.com/code-pushup/cli/issues"
19
20
  },
@@ -22,33 +23,6 @@
22
23
  "url": "git+https://github.com/code-pushup/cli.git",
23
24
  "directory": "packages/cli"
24
25
  },
25
- "contributors": [
26
- {
27
- "name": "Igor Katsuba",
28
- "email": "igor@katsuba.dev",
29
- "url": "https://katsuba.dev"
30
- },
31
- {
32
- "name": "Kateřina Pilátová",
33
- "email": "katerina.pilatova@flowup.cz",
34
- "url": "https://github.com/Tlacenka"
35
- },
36
- {
37
- "name": "Matěj Chalk",
38
- "email": "matej.chalk@flowup.cz",
39
- "url": "https://github.com/matejchalk"
40
- },
41
- {
42
- "name": "Michael Hladky",
43
- "email": "michael.hladky@push-based.io",
44
- "url": "https://push-based.io"
45
- },
46
- {
47
- "name": "Michael Seredenko",
48
- "email": "misha.seredenko@push-based.io",
49
- "url": "https://github.com/MishaSeredenkoPushBased"
50
- }
51
- ],
52
26
  "type": "module",
53
27
  "main": "./index.js",
54
28
  "types": "./src/index.d.ts"
@@ -1,4 +1,4 @@
1
- import { ArgumentsCamelCase } from 'yargs';
1
+ import type { ArgumentsCamelCase } from 'yargs';
2
2
  export declare function yargsAutorunCommandObject(): {
3
3
  command: string;
4
4
  describe: string;
@@ -1,3 +1,3 @@
1
- import { CommandModule } from 'yargs';
1
+ import type { CommandModule } from 'yargs';
2
2
  export declare function yargsCollectCommandObject(): CommandModule;
3
3
  export declare function renderUploadAutorunHint(): void;
@@ -1,2 +1,2 @@
1
- import { CommandModule } from 'yargs';
1
+ import type { CommandModule } from 'yargs';
2
2
  export declare const commands: CommandModule[];
@@ -1,7 +1,6 @@
1
- import { CompareOptions } from '../implementation/compare.model';
2
1
  export declare function yargsCompareCommandObject(): {
3
2
  command: string;
4
3
  describe: string;
5
- builder: Record<keyof CompareOptions, import("yargs").Options>;
4
+ builder: Record<"label" | keyof import("@code-pushup/utils").Diff<string>, import("yargs").Options>;
6
5
  handler: (args: unknown) => Promise<void>;
7
6
  };
@@ -1,5 +1,5 @@
1
- import { type LogOptions } from 'simple-git';
2
- import { HistoryOnlyOptions } from '@code-pushup/core';
1
+ import type { LogOptions } from 'simple-git';
2
+ import type { HistoryOnlyOptions } from '@code-pushup/core';
3
3
  export type HistoryCliOptions = {
4
4
  targetBranch?: string;
5
5
  onlySemverTags?: boolean;
@@ -1,3 +1,3 @@
1
- import { Options } from 'yargs';
2
- import { HistoryCliOptions } from './history.model';
1
+ import type { Options } from 'yargs';
2
+ import type { HistoryCliOptions } from './history.model';
3
3
  export declare function yargsHistoryOptionsDefinition(): Record<keyof HistoryCliOptions, Options>;
@@ -1,3 +1,3 @@
1
- import { HistoryOptions } from '@code-pushup/core';
2
- import { HistoryCliOptions } from './history.model';
1
+ import type { HistoryOptions } from '@code-pushup/core';
2
+ import type { HistoryCliOptions } from './history.model';
3
3
  export declare function normalizeHashOptions(processArgs: HistoryCliOptions & HistoryOptions): Promise<HistoryCliOptions & HistoryOptions>;
@@ -1,2 +1,4 @@
1
- import { Diff } from '@code-pushup/utils';
2
- export type CompareOptions = Diff<string>;
1
+ import type { Diff } from '@code-pushup/utils';
2
+ export type CompareOptions = Diff<string> & {
3
+ label?: string;
4
+ };
@@ -1,3 +1,3 @@
1
- import { Options } from 'yargs';
1
+ import type { Options } from 'yargs';
2
2
  import type { CompareOptions } from './compare.model';
3
3
  export declare function yargsCompareOptionsDefinition(): Record<keyof CompareOptions, Options>;
@@ -1,8 +1,8 @@
1
- import { CoreConfig, Format } from '@code-pushup/models';
2
- import { CoreConfigCliOptions } from './core-config.model';
3
- import { GeneralCliOptions } from './global.model';
4
- import { OnlyPluginsOptions } from './only-plugins.model';
5
- import { SkipPluginsOptions } from './skip-plugins.model';
1
+ import { type CoreConfig, type Format } from '@code-pushup/models';
2
+ import type { CoreConfigCliOptions } from './core-config.model';
3
+ import type { GeneralCliOptions } from './global.model';
4
+ import type { OnlyPluginsOptions } from './only-plugins.model';
5
+ import type { SkipPluginsOptions } from './skip-plugins.model';
6
6
  export type CoreConfigMiddlewareOptions = GeneralCliOptions & CoreConfigCliOptions & OnlyPluginsOptions & SkipPluginsOptions;
7
7
  export declare function coreConfigMiddleware<T extends CoreConfigMiddlewareOptions>(processArgs: T): Promise<GeneralCliOptions & CoreConfig & OnlyPluginsOptions & SkipPluginsOptions>;
8
8
  export declare const normalizeFormats: (formats?: string[]) => Format[];
@@ -1,5 +1,5 @@
1
- import { Options } from 'yargs';
2
- import { PersistConfigCliOptions, UploadConfigCliOptions } from './core-config.model';
1
+ import type { Options } from 'yargs';
2
+ import type { PersistConfigCliOptions, UploadConfigCliOptions } from './core-config.model';
3
3
  export declare function yargsCoreConfigOptionsDefinition(): Record<keyof (PersistConfigCliOptions & UploadConfigCliOptions), Options>;
4
4
  export declare function yargsPersistConfigOptionsDefinition(): Record<keyof PersistConfigCliOptions, Options>;
5
5
  export declare function yargsUploadConfigOptionsDefinition(): Record<keyof UploadConfigCliOptions, Options>;
@@ -0,0 +1,11 @@
1
+ export declare function titleStyle(title: string): string;
2
+ export declare function headerStyle(title: string): string;
3
+ export declare function descriptionStyle(title: string): string;
4
+ export declare function formatObjectValue<T>(opts: T, propName: keyof T): T & {
5
+ [x: string]: string;
6
+ };
7
+ export declare function formatNestedValues<T>(options: Record<string, T>, propName: keyof T): {
8
+ [k: string]: T & {
9
+ [x: string]: string;
10
+ };
11
+ };
@@ -1,3 +1,3 @@
1
- import { GlobalOptions } from '@code-pushup/core';
2
- import { ConfigCliOptions } from './core-config.model';
1
+ import type { GlobalOptions } from '@code-pushup/core';
2
+ import type { ConfigCliOptions } from './core-config.model';
3
3
  export type GeneralCliOptions = ConfigCliOptions & GlobalOptions;
@@ -1,3 +1,3 @@
1
- import { Options } from 'yargs';
2
- import { GeneralCliOptions } from './global.model';
1
+ import type { Options } from 'yargs';
2
+ import type { GeneralCliOptions } from './global.model';
3
3
  export declare function yargsGlobalOptionsDefinition(): Record<keyof GeneralCliOptions, Options>;
@@ -0,0 +1,3 @@
1
+ export type MergeDiffsOptions = {
2
+ files: string[];
3
+ };
@@ -0,0 +1,3 @@
1
+ import type { Options } from 'yargs';
2
+ import type { MergeDiffsOptions } from './merge-diffs.model';
3
+ export declare function yargsMergeDiffsOptionsDefinition(): Record<keyof MergeDiffsOptions, Options>;
@@ -1,2 +1,2 @@
1
- import { OnlyPluginsOptions } from './only-plugins.model';
1
+ import type { OnlyPluginsOptions } from './only-plugins.model';
2
2
  export declare function onlyPluginsMiddleware<T extends OnlyPluginsOptions>(originalProcessArgs: T): T;
@@ -1,5 +1,5 @@
1
- import { GlobalOptions } from '@code-pushup/core';
2
- import { CoreConfig } from '@code-pushup/models';
1
+ import type { GlobalOptions } from '@code-pushup/core';
2
+ import type { CoreConfig } from '@code-pushup/models';
3
3
  export type OnlyPluginsCliOptions = {
4
4
  onlyPlugins?: string[];
5
5
  };
@@ -1,3 +1,4 @@
1
- import { Options } from 'yargs';
1
+ import type { Options } from 'yargs';
2
+ import type { OnlyPluginsCliOptions } from './only-plugins.model';
2
3
  export declare const onlyPluginsOption: Options;
3
- export declare function yargsOnlyPluginsOptionsDefinition(): Record<'onlyPlugins', Options>;
4
+ export declare function yargsOnlyPluginsOptionsDefinition(): Record<keyof OnlyPluginsCliOptions, Options>;
@@ -1,2 +1,2 @@
1
- import { SkipPluginsOptions } from './skip-plugins.model';
1
+ import type { SkipPluginsOptions } from './skip-plugins.model';
2
2
  export declare function skipPluginsMiddleware<T extends SkipPluginsOptions>(originalProcessArgs: T): T;
@@ -1,5 +1,5 @@
1
- import { GlobalOptions } from '@code-pushup/core';
2
- import { CoreConfig } from '@code-pushup/models';
1
+ import type { GlobalOptions } from '@code-pushup/core';
2
+ import type { CoreConfig } from '@code-pushup/models';
3
3
  export type SkipPluginsCliOptions = {
4
4
  skipPlugins?: string[];
5
5
  };
@@ -1,3 +1,4 @@
1
- import { Options } from 'yargs';
1
+ import type { Options } from 'yargs';
2
+ import type { SkipPluginsCliOptions } from './skip-plugins.model';
2
3
  export declare const skipPluginsOption: Options;
3
- export declare function yargsSkipPluginsOptionsDefinition(): Record<'skipPlugins', Options>;
4
+ export declare function yargsSkipPluginsOptionsDefinition(): Record<keyof SkipPluginsCliOptions, Options>;
@@ -0,0 +1,6 @@
1
+ export declare function yargsMergeDiffsCommandObject(): {
2
+ command: string;
3
+ describe: string;
4
+ builder: Record<"files", import("yargs").Options>;
5
+ handler: (args: unknown) => Promise<void>;
6
+ };
@@ -1,4 +1,4 @@
1
- import { MiddlewareFunction } from 'yargs';
1
+ import type { MiddlewareFunction } from 'yargs';
2
2
  export declare const middlewares: {
3
3
  middlewareFunction: MiddlewareFunction;
4
4
  applyBeforeValidation: boolean;
@@ -1,4 +1,4 @@
1
- import { ArgumentsCamelCase } from 'yargs';
1
+ import type { ArgumentsCamelCase } from 'yargs';
2
2
  export declare function yargsUploadCommandObject(): {
3
3
  command: string;
4
4
  describe: string;
@@ -1,4 +1,17 @@
1
- import { Argv, CommandModule, Options } from 'yargs';
1
+ import { type Argv, type CommandModule, type Options } from 'yargs';
2
+ export declare const yargsDecorator: {
3
+ 'Commands:': string;
4
+ 'Options:': string;
5
+ 'Examples:': string;
6
+ boolean: string;
7
+ count: string;
8
+ string: string;
9
+ array: string;
10
+ required: string;
11
+ 'default:': string;
12
+ 'choices:': string;
13
+ 'aliases:': string;
14
+ };
2
15
  /**
3
16
  * returns configurable yargs CLI for code-pushup
4
17
  *