@code-pushup/cli 0.8.13 → 0.8.15

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 CHANGED
@@ -7,8 +7,7 @@ import { hideBin } from "yargs/helpers";
7
7
  import chalk5 from "chalk";
8
8
 
9
9
  // packages/core/src/lib/implementation/persist.ts
10
- import { existsSync, mkdirSync } from "node:fs";
11
- import { stat as stat2, writeFile } from "node:fs/promises";
10
+ import { mkdir as mkdir2, stat as stat2, writeFile } from "node:fs/promises";
12
11
  import { join as join2 } from "node:path";
13
12
 
14
13
  // packages/utils/src/lib/execute-process.ts
@@ -656,6 +655,14 @@ async function fileExists(path) {
656
655
  return false;
657
656
  }
658
657
  }
658
+ async function directoryExists(path) {
659
+ try {
660
+ const stats = await stat(path);
661
+ return stats.isDirectory();
662
+ } catch {
663
+ return false;
664
+ }
665
+ }
659
666
  async function ensureDirectoryExists(baseDir) {
660
667
  try {
661
668
  await mkdir(baseDir, { recursive: true });
@@ -1556,47 +1563,55 @@ async function persistReport(report, options2) {
1556
1563
  const { outputDir, filename, format } = options2;
1557
1564
  const sortedScoredReport = sortReport(scoreReport(report));
1558
1565
  console.info(generateStdoutSummary(sortedScoredReport));
1559
- const results = [];
1560
- if (format.includes("json")) {
1561
- results.push({
1562
- format: "json",
1563
- content: JSON.stringify(report, null, 2)
1564
- });
1565
- }
1566
- if (format.includes("md")) {
1567
- const commitData = await getLatestCommit();
1568
- validateCommitData(commitData);
1569
- results.push({
1570
- format: "md",
1571
- content: generateMdReport(sortedScoredReport, commitData)
1572
- });
1573
- }
1574
- if (!existsSync(outputDir)) {
1566
+ const results = await Promise.all(
1567
+ format.map(async (reportType) => {
1568
+ switch (reportType) {
1569
+ case "json":
1570
+ return {
1571
+ format: "json",
1572
+ content: JSON.stringify(report, null, 2)
1573
+ };
1574
+ case "md":
1575
+ const commitData = await getLatestCommit();
1576
+ validateCommitData(commitData);
1577
+ return {
1578
+ format: "md",
1579
+ content: generateMdReport(sortedScoredReport, commitData)
1580
+ };
1581
+ }
1582
+ })
1583
+ );
1584
+ if (!await directoryExists(outputDir)) {
1575
1585
  try {
1576
- mkdirSync(outputDir, { recursive: true });
1577
- } catch (e) {
1578
- console.warn(e);
1586
+ await mkdir2(outputDir, { recursive: true });
1587
+ } catch (error) {
1588
+ console.warn(error);
1579
1589
  throw new PersistDirError(outputDir);
1580
1590
  }
1581
1591
  }
1582
1592
  return Promise.allSettled(
1583
- results.map(({ format: format2, content }) => {
1584
- const reportPath = join2(outputDir, `${filename}.${format2}`);
1585
- return writeFile(reportPath, content).then(() => stat2(reportPath)).then((stats) => [reportPath, stats.size]).catch((e) => {
1586
- console.warn(e);
1587
- throw new PersistError(reportPath);
1588
- });
1589
- })
1593
+ results.map(
1594
+ (result) => persistResult(
1595
+ join2(outputDir, `${filename}.${result.format}`),
1596
+ result.content
1597
+ )
1598
+ )
1590
1599
  );
1591
1600
  }
1592
- function logPersistedResults(persistResults) {
1593
- logMultipleFileResults(persistResults, "Generated reports");
1594
- }
1595
1601
  function validateCommitData(commitData) {
1596
1602
  if (!commitData) {
1597
1603
  console.warn("no commit data available");
1598
1604
  }
1599
1605
  }
1606
+ async function persistResult(reportPath, content) {
1607
+ return writeFile(reportPath, content).then(() => stat2(reportPath)).then((stats) => [reportPath, stats.size]).catch((error) => {
1608
+ console.warn(error);
1609
+ throw new PersistError(reportPath);
1610
+ });
1611
+ }
1612
+ function logPersistedResults(persistResults) {
1613
+ logMultipleFileResults(persistResults, "Generated reports");
1614
+ }
1600
1615
 
1601
1616
  // packages/core/src/lib/implementation/execute-plugin.ts
1602
1617
  import chalk4 from "chalk";
@@ -1610,12 +1625,8 @@ async function executeRunnerConfig(cfg, onProgress) {
1610
1625
  args,
1611
1626
  observer: { onStdout: onProgress }
1612
1627
  });
1613
- let audits = await readJsonFile(
1614
- join3(process.cwd(), outputFile)
1615
- );
1616
- if (outputTransform) {
1617
- audits = await outputTransform(audits);
1618
- }
1628
+ const outputs = await readJsonFile(join3(process.cwd(), outputFile));
1629
+ const audits = outputTransform ? await outputTransform(outputs) : outputs;
1619
1630
  return {
1620
1631
  duration,
1621
1632
  date,
@@ -1670,30 +1681,31 @@ async function executePlugin(pluginConfig, onProgress) {
1670
1681
  };
1671
1682
  }
1672
1683
  async function executePlugins(plugins, options2) {
1673
- const { progress = false } = options2 || {};
1674
- const progressName = "Run Plugins";
1675
- const progressBar = progress ? getProgressBar(progressName) : null;
1684
+ const { progress = false } = options2 ?? {};
1685
+ const progressBar = progress ? getProgressBar("Run plugins") : null;
1676
1686
  const pluginsResult = await plugins.reduce(async (acc, pluginCfg) => {
1677
- const outputs = await acc;
1678
- progressBar?.updateTitle(`Executing ${chalk4.bold(pluginCfg.title)}`);
1687
+ progressBar?.updateTitle(`Executing ${chalk4.bold(pluginCfg.title)}`);
1679
1688
  try {
1680
1689
  const pluginReport = await executePlugin(pluginCfg);
1681
1690
  progressBar?.incrementInSteps(plugins.length);
1682
- return outputs.concat(Promise.resolve(pluginReport));
1683
- } catch (e) {
1691
+ return [...await acc, Promise.resolve(pluginReport)];
1692
+ } catch (error) {
1684
1693
  progressBar?.incrementInSteps(plugins.length);
1685
- return outputs.concat(
1686
- Promise.reject(e instanceof Error ? e.message : String(e))
1687
- );
1694
+ return [
1695
+ ...await acc,
1696
+ Promise.reject(error instanceof Error ? error.message : String(error))
1697
+ ];
1688
1698
  }
1689
1699
  }, Promise.resolve([]));
1690
1700
  progressBar?.endProgress("Done running plugins");
1691
- const errorsCallback = ({ reason }) => console.error(reason);
1701
+ const errorsCallback = ({ reason }) => {
1702
+ console.error(reason);
1703
+ };
1692
1704
  const results = await Promise.allSettled(pluginsResult);
1693
1705
  logMultipleResults(results, "Plugins", void 0, errorsCallback);
1694
1706
  const { fulfilled, rejected } = groupByStatus(results);
1695
- if (rejected.length) {
1696
- const errorMessages = rejected.map(({ reason }) => reason).join(", ");
1707
+ if (rejected.length > 0) {
1708
+ const errorMessages = rejected.map(({ reason }) => String(reason)).join(", ");
1697
1709
  throw new Error(
1698
1710
  `Plugins failed: ${rejected.length} errors: ${errorMessages}`
1699
1711
  );
@@ -1713,14 +1725,11 @@ function auditOutputsCorrelateWithPluginOutput(auditOutputs, pluginConfigAudits)
1713
1725
 
1714
1726
  // packages/core/package.json
1715
1727
  var name = "@code-pushup/core";
1716
- var version = "0.8.13";
1728
+ var version = "0.8.15";
1717
1729
 
1718
1730
  // packages/core/src/lib/implementation/collect.ts
1719
1731
  async function collect(options2) {
1720
1732
  const { plugins, categories } = options2;
1721
- if (!plugins?.length) {
1722
- throw new Error("No plugins registered");
1723
- }
1724
1733
  const date = (/* @__PURE__ */ new Date()).toISOString();
1725
1734
  const start = performance.now();
1726
1735
  const pluginOutputs = await executePlugins(plugins, options2);
@@ -1743,62 +1752,74 @@ import {
1743
1752
  IssueSourceType,
1744
1753
  IssueSeverity as PortalIssueSeverity
1745
1754
  } from "@code-pushup/portal-client";
1746
- function jsonToGql(report) {
1755
+ function jsonReportToGql(report) {
1747
1756
  return {
1748
1757
  packageName: report.packageName,
1749
1758
  packageVersion: report.version,
1750
1759
  commandStartDate: report.date,
1751
1760
  commandDuration: report.duration,
1752
- plugins: report.plugins.map((plugin) => ({
1753
- audits: plugin.audits.map((audit) => ({
1754
- description: audit.description,
1755
- details: {
1756
- issues: audit.details?.issues.map((issue) => ({
1757
- message: issue.message,
1758
- severity: transformSeverity(issue.severity),
1759
- sourceEndColumn: issue.source?.position?.endColumn,
1760
- sourceEndLine: issue.source?.position?.endLine,
1761
- sourceFilePath: issue.source?.file,
1762
- sourceStartColumn: issue.source?.position?.startColumn,
1763
- sourceStartLine: issue.source?.position?.startLine,
1764
- sourceType: IssueSourceType.SourceCode
1765
- })) || []
1766
- },
1767
- docsUrl: audit.docsUrl,
1768
- formattedValue: audit.displayValue,
1769
- score: audit.score,
1770
- slug: audit.slug,
1771
- title: audit.title,
1772
- value: audit.value
1773
- })),
1774
- description: plugin.description,
1775
- docsUrl: plugin.docsUrl,
1776
- groups: plugin.groups?.map((group) => ({
1777
- slug: group.slug,
1778
- title: group.title,
1779
- description: group.description,
1780
- refs: group.refs.map((ref) => ({ slug: ref.slug, weight: ref.weight }))
1781
- })),
1782
- icon: plugin.icon,
1783
- slug: plugin.slug,
1784
- title: plugin.title,
1785
- packageName: plugin.packageName,
1786
- packageVersion: plugin.version,
1787
- runnerDuration: plugin.duration,
1788
- runnerStartDate: plugin.date
1761
+ plugins: pluginReportsToGql(report.plugins),
1762
+ categories: categoryConfigsToGql(toArray(report.categories))
1763
+ };
1764
+ }
1765
+ function pluginReportsToGql(plugins) {
1766
+ return plugins.map((plugin) => ({
1767
+ audits: auditReportsToGql(plugin.audits),
1768
+ description: plugin.description,
1769
+ docsUrl: plugin.docsUrl,
1770
+ groups: plugin.groups?.map((group) => ({
1771
+ slug: group.slug,
1772
+ title: group.title,
1773
+ description: group.description,
1774
+ refs: group.refs.map((ref) => ({ slug: ref.slug, weight: ref.weight }))
1789
1775
  })),
1790
- categories: report.categories.map((category) => ({
1791
- slug: category.slug,
1792
- title: category.title,
1793
- description: category.description,
1794
- refs: category.refs.map((ref) => ({
1795
- plugin: ref.plugin,
1796
- type: ref.type === "audit" ? CategoryConfigRefType.Audit : CategoryConfigRefType.Group,
1797
- weight: ref.weight,
1798
- slug: ref.slug
1799
- }))
1776
+ icon: plugin.icon,
1777
+ slug: plugin.slug,
1778
+ title: plugin.title,
1779
+ packageName: plugin.packageName,
1780
+ packageVersion: plugin.version,
1781
+ runnerDuration: plugin.duration,
1782
+ runnerStartDate: plugin.date
1783
+ }));
1784
+ }
1785
+ function auditReportsToGql(audits) {
1786
+ return audits.map((audit) => ({
1787
+ description: audit.description,
1788
+ details: {
1789
+ issues: issuesToGql(audit.details?.issues)
1790
+ },
1791
+ docsUrl: audit.docsUrl,
1792
+ formattedValue: audit.displayValue,
1793
+ score: audit.score,
1794
+ slug: audit.slug,
1795
+ title: audit.title,
1796
+ value: audit.value
1797
+ }));
1798
+ }
1799
+ function issuesToGql(issues) {
1800
+ return issues?.map((issue) => ({
1801
+ message: issue.message,
1802
+ severity: transformSeverity(issue.severity),
1803
+ sourceEndColumn: issue.source?.position?.endColumn,
1804
+ sourceEndLine: issue.source?.position?.endLine,
1805
+ sourceFilePath: issue.source?.file,
1806
+ sourceStartColumn: issue.source?.position?.startColumn,
1807
+ sourceStartLine: issue.source?.position?.startLine,
1808
+ sourceType: IssueSourceType.SourceCode
1809
+ })) ?? [];
1810
+ }
1811
+ function categoryConfigsToGql(categories) {
1812
+ return categories.map((category) => ({
1813
+ slug: category.slug,
1814
+ title: category.title,
1815
+ description: category.description,
1816
+ refs: category.refs.map((ref) => ({
1817
+ plugin: ref.plugin,
1818
+ type: ref.type === "audit" ? CategoryConfigRefType.Audit : CategoryConfigRefType.Group,
1819
+ weight: ref.weight,
1820
+ slug: ref.slug
1800
1821
  }))
1801
- };
1822
+ }));
1802
1823
  }
1803
1824
  function transformSeverity(severity) {
1804
1825
  switch (severity) {
@@ -1821,8 +1842,8 @@ var normalizePersistConfig = (cfg) => ({
1821
1842
 
1822
1843
  // packages/core/src/lib/upload.ts
1823
1844
  async function upload(options2, uploadFn = uploadToPortal) {
1824
- const persist = normalizePersistConfig(options2?.persist);
1825
- if (!options2?.upload) {
1845
+ const persist = normalizePersistConfig(options2.persist);
1846
+ if (!options2.upload) {
1826
1847
  throw new Error("upload config must be set");
1827
1848
  }
1828
1849
  const { apiKey, server, organization, project } = options2.upload;
@@ -1838,7 +1859,7 @@ async function upload(options2, uploadFn = uploadToPortal) {
1838
1859
  organization,
1839
1860
  project,
1840
1861
  commit: commitData.hash,
1841
- ...jsonToGql(report)
1862
+ ...jsonReportToGql(report)
1842
1863
  };
1843
1864
  return uploadFn({ apiKey, server, data });
1844
1865
  }
@@ -1847,9 +1868,11 @@ async function upload(options2, uploadFn = uploadToPortal) {
1847
1868
  async function collectAndPersistReports(options2) {
1848
1869
  const { exec } = verboseUtils(options2.verbose);
1849
1870
  const report = await collect(options2);
1850
- const persist = normalizePersistConfig(options2?.persist);
1871
+ const persist = normalizePersistConfig(options2.persist);
1851
1872
  const persistResults = await persistReport(report, persist);
1852
- exec(() => logPersistedResults(persistResults));
1873
+ exec(() => {
1874
+ logPersistedResults(persistResults);
1875
+ });
1853
1876
  report.plugins.forEach((plugin) => {
1854
1877
  pluginReportSchema.parse(plugin);
1855
1878
  });
@@ -1868,6 +1891,10 @@ async function readCodePushupConfig(filepath) {
1868
1891
  return coreConfigSchema.parse(await importEsmModule({ filepath }));
1869
1892
  }
1870
1893
 
1894
+ // packages/cli/src/lib/constants.ts
1895
+ var CLI_NAME = "Code PushUp CLI";
1896
+ var CLI_SCRIPT_NAME = "code-pushup";
1897
+
1871
1898
  // packages/cli/src/lib/implementation/only-plugins-options.ts
1872
1899
  var onlyPluginsOption = {
1873
1900
  describe: "List of plugins to run. If not set all plugins are run.",
@@ -1902,10 +1929,10 @@ function yargsAutorunCommandObject() {
1902
1929
  }
1903
1930
  };
1904
1931
  await collectAndPersistReports(optionsWithFormat);
1905
- if (!options2.upload) {
1906
- console.warn("Upload skipped because configuration is not set.");
1907
- } else {
1932
+ if (options2.upload) {
1908
1933
  await upload(options2);
1934
+ } else {
1935
+ console.warn("Upload skipped because configuration is not set.");
1909
1936
  }
1910
1937
  }
1911
1938
  };
@@ -1930,19 +1957,13 @@ function yargsCollectCommandObject() {
1930
1957
 
1931
1958
  // packages/cli/src/lib/implementation/filter-kebab-case-keys.ts
1932
1959
  function filterKebabCaseKeys(obj) {
1933
- const newObj = {};
1934
- Object.keys(obj).forEach((key) => {
1935
- if (key.includes("-")) {
1936
- return;
1937
- }
1938
- if (typeof obj[key] === "string" || typeof obj[key] === "object" && Array.isArray(obj[key])) {
1939
- newObj[key] = obj[key];
1940
- }
1941
- if (typeof obj[key] === "object" && !Array.isArray(obj[key]) && obj[key] != null) {
1942
- newObj[key] = filterKebabCaseKeys(obj[key]);
1943
- }
1944
- });
1945
- return newObj;
1960
+ return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
1961
+ (acc, [key, value]) => typeof value === "string" || typeof value === "object" && Array.isArray(obj[key]) ? { ...acc, [key]: value } : typeof value === "object" && !Array.isArray(value) && value != null ? {
1962
+ ...acc,
1963
+ [key]: filterKebabCaseKeys(value)
1964
+ } : { ...acc, [key]: value },
1965
+ {}
1966
+ );
1946
1967
  }
1947
1968
 
1948
1969
  // packages/cli/src/lib/print-config/print-config-command.ts
@@ -2041,10 +2062,10 @@ function logErrorBeforeThrow(fn) {
2041
2062
  return async (...args) => {
2042
2063
  try {
2043
2064
  return await fn(...args);
2044
- } catch (err) {
2045
- console.error(err);
2065
+ } catch (error) {
2066
+ console.error(error);
2046
2067
  await new Promise((resolve) => process.stdout.write("", resolve));
2047
- throw err;
2068
+ throw error;
2048
2069
  }
2049
2070
  };
2050
2071
  }
@@ -2072,10 +2093,10 @@ async function configMiddleware(processArgs) {
2072
2093
  // therefore this can't live in option defaults as the order would be `config`->`provided options`->default
2073
2094
  // so we have to manually implement the order
2074
2095
  persist: {
2075
- outputDir: cliOptions?.persist?.outputDir || importedRc?.persist?.outputDir || PERSIST_OUTPUT_DIR,
2076
- filename: cliOptions?.persist?.filename || importedRc?.persist?.filename || PERSIST_FILENAME,
2096
+ outputDir: cliOptions.persist?.outputDir || importedRc.persist?.outputDir || PERSIST_OUTPUT_DIR,
2097
+ filename: cliOptions.persist?.filename || importedRc.persist?.filename || PERSIST_FILENAME,
2077
2098
  format: coerceArray(
2078
- cliOptions?.persist?.format || importedRc?.persist?.format || PERSIST_FORMAT
2099
+ cliOptions.persist?.format ?? importedRc.persist?.format ?? PERSIST_FORMAT
2079
2100
  )
2080
2101
  },
2081
2102
  plugins: filterPluginsByOnlyPluginsOption(importedRc.plugins, cliOptions),
@@ -2173,19 +2194,19 @@ import chalk9 from "chalk";
2173
2194
  import yargs from "yargs";
2174
2195
  function yargsCli(argv, cfg) {
2175
2196
  const { usageMessage, scriptName, noExitProcess } = cfg;
2176
- let { commands: commands2, options: options2, middlewares: middlewares2 } = cfg;
2177
- commands2 = Array.isArray(commands2) ? commands2 : [];
2178
- middlewares2 = Array.isArray(middlewares2) ? middlewares2 : [];
2179
- options2 = options2 || {};
2197
+ const commands2 = cfg.commands ?? [];
2198
+ const middlewares2 = cfg.middlewares ?? [];
2199
+ const options2 = cfg.options ?? {};
2180
2200
  const cli2 = yargs(argv);
2181
- cli2.help().version(false).alias("h", "help").parserConfiguration({
2201
+ cli2.help().version(false).alias("h", "help").check((args) => {
2202
+ const persist = args["persist"];
2203
+ return persist == null || validatePersistFormat(persist);
2204
+ }).parserConfiguration({
2182
2205
  "strip-dashed": true
2183
- }).array("persist.format").coerce("config", (config) => {
2184
- if (Array.isArray(config)) {
2185
- return config[config.length - 1];
2186
- }
2187
- return config;
2188
- }).options(options2);
2206
+ }).coerce(
2207
+ "config",
2208
+ (config) => Array.isArray(config) ? config.at(-1) : config
2209
+ ).options(options2);
2189
2210
  if (usageMessage) {
2190
2211
  cli2.usage(chalk9.bold(usageMessage));
2191
2212
  }
@@ -2201,9 +2222,7 @@ function yargsCli(argv, cfg) {
2201
2222
  commands2.forEach((commandObj) => {
2202
2223
  cli2.command({
2203
2224
  ...commandObj,
2204
- ...commandObj.handler && {
2205
- handler: logErrorBeforeThrow(commandObj.handler)
2206
- },
2225
+ handler: logErrorBeforeThrow(commandObj.handler),
2207
2226
  ...typeof commandObj.builder === "function" && {
2208
2227
  builder: logErrorBeforeThrow(commandObj.builder)
2209
2228
  }
@@ -2214,10 +2233,22 @@ function yargsCli(argv, cfg) {
2214
2233
  }
2215
2234
  return cli2;
2216
2235
  }
2236
+ function validatePersistFormat(persist) {
2237
+ try {
2238
+ if (persist?.format != null) {
2239
+ persist.format.forEach((format) => formatSchema.parse(format));
2240
+ }
2241
+ return true;
2242
+ } catch {
2243
+ throw new Error(
2244
+ `Invalid persist.format option. Valid options are: ${Object.values(
2245
+ formatSchema.Values
2246
+ ).join(", ")}`
2247
+ );
2248
+ }
2249
+ }
2217
2250
 
2218
2251
  // packages/cli/src/lib/cli.ts
2219
- var CLI_NAME = "Code PushUp CLI";
2220
- var CLI_SCRIPT_NAME = "code-pushup";
2221
2252
  var cli = (args) => yargsCli(args, {
2222
2253
  usageMessage: CLI_NAME,
2223
2254
  scriptName: CLI_SCRIPT_NAME,
@@ -2227,4 +2258,4 @@ var cli = (args) => yargsCli(args, {
2227
2258
  });
2228
2259
 
2229
2260
  // packages/cli/src/index.ts
2230
- cli(hideBin(process.argv)).argv;
2261
+ await cli(hideBin(process.argv)).argv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-pushup/cli",
3
- "version": "0.8.13",
3
+ "version": "0.8.15",
4
4
  "bin": {
5
5
  "code-pushup": "index.js"
6
6
  },
package/src/lib/cli.d.ts CHANGED
@@ -1,3 +1 @@
1
- export declare const CLI_NAME = "Code PushUp CLI";
2
- export declare const CLI_SCRIPT_NAME = "code-pushup";
3
1
  export declare const cli: (args: string[]) => import("yargs").Argv<unknown>;
@@ -0,0 +1,2 @@
1
+ export declare const CLI_NAME = "Code PushUp CLI";
2
+ export declare const CLI_SCRIPT_NAME = "code-pushup";