@code-pushup/cli 0.8.14 → 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.
Files changed (2) hide show
  1. package/index.js +150 -110
  2. package/package.json +1 -1
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.14";
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
  });
@@ -2175,9 +2198,12 @@ function yargsCli(argv, cfg) {
2175
2198
  const middlewares2 = cfg.middlewares ?? [];
2176
2199
  const options2 = cfg.options ?? {};
2177
2200
  const cli2 = yargs(argv);
2178
- 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({
2179
2205
  "strip-dashed": true
2180
- }).array("persist.format").coerce(
2206
+ }).coerce(
2181
2207
  "config",
2182
2208
  (config) => Array.isArray(config) ? config.at(-1) : config
2183
2209
  ).options(options2);
@@ -2207,6 +2233,20 @@ function yargsCli(argv, cfg) {
2207
2233
  }
2208
2234
  return cli2;
2209
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
+ }
2210
2250
 
2211
2251
  // packages/cli/src/lib/cli.ts
2212
2252
  var cli = (args) => yargsCli(args, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-pushup/cli",
3
- "version": "0.8.14",
3
+ "version": "0.8.15",
4
4
  "bin": {
5
5
  "code-pushup": "index.js"
6
6
  },