@reddoorla/maintenance 0.14.0 → 0.15.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/dist/cli/bin.js CHANGED
@@ -938,6 +938,7 @@ import { cac } from "cac";
938
938
 
939
939
  // src/cli/commands/audit.ts
940
940
  import { resolve as resolve2 } from "path";
941
+ import { Listr } from "listr2";
941
942
 
942
943
  // src/audits/util/spawn.ts
943
944
  import { spawn } from "child_process";
@@ -1694,29 +1695,27 @@ var DEFAULT_AUDIT_TIMEOUT_MS = 3e4;
1694
1695
  function timedSpawn(timeoutMs) {
1695
1696
  return (cmd, args, opts = {}) => defaultSpawn(cmd, args, { ...opts, timeoutMs: opts.timeoutMs ?? timeoutMs });
1696
1697
  }
1698
+ async function runOneAudit(site, name) {
1699
+ if (!(name in REGISTRY)) throw new Error(`unknown audit: ${name}`);
1700
+ const spawn2 = timedSpawn(DEFAULT_AUDIT_TIMEOUT_MS);
1701
+ const label = site.name ?? site.path;
1702
+ try {
1703
+ return await REGISTRY[name]({ site, spawn: spawn2 });
1704
+ } catch (err) {
1705
+ return {
1706
+ audit: name,
1707
+ site: label,
1708
+ status: "fail",
1709
+ summary: `${name}: unexpected error \u2014 ${String(err)}`
1710
+ };
1711
+ }
1712
+ }
1697
1713
  async function runAudits(site, which) {
1698
1714
  const names = which ?? ALL_AUDIT_NAMES;
1699
1715
  for (const n of names) {
1700
1716
  if (!(n in REGISTRY)) throw new Error(`unknown audit: ${n}`);
1701
1717
  }
1702
- const spawn2 = timedSpawn(DEFAULT_AUDIT_TIMEOUT_MS);
1703
- const label = site.name ?? site.path;
1704
- return Promise.all(
1705
- names.map(
1706
- (n) => REGISTRY[n]({ site, spawn: spawn2 }).catch(
1707
- (err) => ({
1708
- audit: n,
1709
- site: label,
1710
- status: "fail",
1711
- summary: `${n}: unexpected error \u2014 ${String(err)}`
1712
- })
1713
- )
1714
- )
1715
- );
1716
- }
1717
- async function runAuditsAcross(sites, which) {
1718
- const all = await Promise.all(sites.map((s) => runAudits(s, which)));
1719
- return all.flat();
1718
+ return Promise.all(names.map((n) => runOneAudit(site, n)));
1720
1719
  }
1721
1720
 
1722
1721
  // src/cli/fleet/resolve-sites.ts
@@ -1882,8 +1881,87 @@ function formatTable(results) {
1882
1881
  function exitCode(results) {
1883
1882
  return results.some((r) => r.status === "fail") ? 1 : 0;
1884
1883
  }
1884
+ function formatDuration(ms) {
1885
+ if (ms < 1e3) return `${ms}ms`;
1886
+ const totalSeconds = Math.round(ms / 1e3);
1887
+ if (totalSeconds < 60) return `${totalSeconds}s`;
1888
+ const m = Math.floor(totalSeconds / 60);
1889
+ const s = totalSeconds % 60;
1890
+ return `${m}m${s.toString().padStart(2, "0")}s`;
1891
+ }
1892
+ function buildAuditTasks(sites, which, results, renderer) {
1893
+ const singleSite = sites.length === 1;
1894
+ if (singleSite) {
1895
+ const site = sites[0];
1896
+ return new Listr(
1897
+ which.map((name) => ({
1898
+ title: name,
1899
+ task: async (_ctx, task) => {
1900
+ const start = Date.now();
1901
+ const result = await runOneAudit(site, name);
1902
+ results.push(result);
1903
+ const elapsed = formatDuration(Date.now() - start);
1904
+ task.title = `${name}: ${result.summary} (${elapsed})`;
1905
+ if (result.status === "fail") throw new Error(result.summary);
1906
+ }
1907
+ })),
1908
+ { concurrent: true, exitOnError: false, renderer }
1909
+ );
1910
+ }
1911
+ return new Listr(
1912
+ sites.map((site) => {
1913
+ const label = site.name ?? site.path;
1914
+ return {
1915
+ title: label,
1916
+ task: async (_ctx, task) => {
1917
+ const start = Date.now();
1918
+ let done = 0;
1919
+ task.output = `0/${which.length} audits`;
1920
+ const settled = await Promise.all(
1921
+ which.map(async (name) => {
1922
+ const r = await runOneAudit(site, name);
1923
+ results.push(r);
1924
+ done += 1;
1925
+ task.output = `${done}/${which.length} audits`;
1926
+ return r;
1927
+ })
1928
+ );
1929
+ const elapsed = formatDuration(Date.now() - start);
1930
+ const failed = settled.filter((r) => r.status === "fail").length;
1931
+ const warned = settled.filter((r) => r.status === "warn").length;
1932
+ const note = failed > 0 ? `${failed} failed` : warned > 0 ? `${warned} warning${warned === 1 ? "" : "s"}` : "all green";
1933
+ task.title = `${label}: ${note} (${elapsed})`;
1934
+ if (failed > 0) throw new Error(`${label}: ${failed} audit(s) failed`);
1935
+ }
1936
+ };
1937
+ }),
1938
+ { concurrent: true, exitOnError: false, renderer }
1939
+ );
1940
+ }
1941
+ function formatWriteSummary(summary) {
1942
+ const lines = summary.writes.map((w) => {
1943
+ if (w.audit === "lighthouse") {
1944
+ const s = w.counts;
1945
+ return ` lighthouse: P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo}`;
1946
+ }
1947
+ if (w.audit === "a11y") {
1948
+ return ` a11y: ${w.counts.violations} violations`;
1949
+ }
1950
+ if (w.audit === "deps") {
1951
+ const c2 = w.counts;
1952
+ return ` deps: ${c2.drifted} drifted (${c2.majorBehind} major)`;
1953
+ }
1954
+ const c = w.counts;
1955
+ return ` security: ${c.critical}C/${c.high}H/${c.moderate}M/${c.low}L`;
1956
+ });
1957
+ return `\u2192 wrote to Websites[${summary.siteName}]:
1958
+ ${lines.join("\n")}`;
1959
+ }
1960
+ function rendererFor(json) {
1961
+ return json ? "silent" : "default";
1962
+ }
1885
1963
  async function runAuditCommand(site, opts) {
1886
- const which = parseOnly(opts.only);
1964
+ const which = parseOnly(opts.only) ?? ALL_AUDIT_NAMES;
1887
1965
  const cwd = opts.cwd ? resolve2(opts.cwd) : process.cwd();
1888
1966
  let sites = await resolveSites({
1889
1967
  ...site !== void 0 ? { site } : {},
@@ -1895,7 +1973,9 @@ async function runAuditCommand(site, opts) {
1895
1973
  const workdir = opts.workdir ?? `${process.env.HOME ?? ""}/.reddoor-maint/sites`;
1896
1974
  sites = await Promise.all(sites.map((s) => cloneIfNeeded(s, { workdir })));
1897
1975
  }
1898
- const results = await runAuditsAcross(sites, which);
1976
+ const results = [];
1977
+ const renderer = rendererFor(opts.json);
1978
+ await buildAuditTasks(sites, which, results, renderer).run();
1899
1979
  let output = opts.json ? JSON.stringify(results, null, 2) : formatTable(results);
1900
1980
  if (opts.writeAirtable !== void 0) {
1901
1981
  const { openBase: openBase2, readAirtableConfig: readAirtableConfig2 } = await Promise.resolve().then(() => (init_client(), client_exports));
@@ -1903,28 +1983,26 @@ async function runAuditCommand(site, opts) {
1903
1983
  const { resolveSlugFromCwd: resolveSlugFromCwd2 } = await Promise.resolve().then(() => (init_lighthouse_airtable(), lighthouse_airtable_exports));
1904
1984
  const { writeAuditsToAirtable: writeAuditsToAirtable2 } = await Promise.resolve().then(() => (init_write_audits_to_airtable(), write_audits_to_airtable_exports));
1905
1985
  const slug = typeof opts.writeAirtable === "string" && opts.writeAirtable.length > 0 ? opts.writeAirtable : await resolveSlugFromCwd2(cwd);
1906
- const base = openBase2(readAirtableConfig2());
1907
- const websites = await listWebsites2(base);
1908
- const summary = await writeAuditsToAirtable2({ base, websites, slug, results });
1909
- const lines = summary.writes.map((w) => {
1910
- if (w.audit === "lighthouse") {
1911
- const s = w.counts;
1912
- return ` lighthouse: P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo}`;
1913
- }
1914
- if (w.audit === "a11y") {
1915
- return ` a11y: ${w.counts.violations} violations`;
1916
- }
1917
- if (w.audit === "deps") {
1918
- const c2 = w.counts;
1919
- return ` deps: ${c2.drifted} drifted (${c2.majorBehind} major)`;
1920
- }
1921
- const c = w.counts;
1922
- return ` security: ${c.critical}C/${c.high}H/${c.moderate}M/${c.low}L`;
1923
- });
1924
- output += `
1986
+ let writeSummary = null;
1987
+ await new Listr(
1988
+ [
1989
+ {
1990
+ title: `Write to Airtable[${slug}]`,
1991
+ task: async (_ctx, task) => {
1992
+ const base = openBase2(readAirtableConfig2());
1993
+ task.output = "loading Websites\u2026";
1994
+ const websites = await listWebsites2(base);
1995
+ task.output = "writing scores\u2026";
1996
+ writeSummary = await writeAuditsToAirtable2({ base, websites, slug, results });
1997
+ task.title = `Wrote to Websites[${writeSummary.siteName}] (${writeSummary.writes.length} audit type${writeSummary.writes.length === 1 ? "" : "s"})`;
1998
+ }
1999
+ }
2000
+ ],
2001
+ { renderer }
2002
+ ).run();
2003
+ if (writeSummary) output += `
1925
2004
 
1926
- \u2192 wrote to Websites[${summary.siteName}]:
1927
- ${lines.join("\n")}`;
2005
+ ${formatWriteSummary(writeSummary)}`;
1928
2006
  }
1929
2007
  return { output, code: exitCode(results) };
1930
2008
  }