@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 +119 -41
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +115 -44
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -348,6 +348,7 @@ var init_write_audits_to_airtable = __esm({
|
|
|
348
348
|
|
|
349
349
|
// src/cli/commands/audit.ts
|
|
350
350
|
import { resolve as resolve2 } from "path";
|
|
351
|
+
import { Listr } from "listr2";
|
|
351
352
|
|
|
352
353
|
// src/audits/util/spawn.ts
|
|
353
354
|
import { spawn } from "child_process";
|
|
@@ -1104,29 +1105,20 @@ var DEFAULT_AUDIT_TIMEOUT_MS = 3e4;
|
|
|
1104
1105
|
function timedSpawn(timeoutMs) {
|
|
1105
1106
|
return (cmd, args, opts = {}) => defaultSpawn(cmd, args, { ...opts, timeoutMs: opts.timeoutMs ?? timeoutMs });
|
|
1106
1107
|
}
|
|
1107
|
-
async function
|
|
1108
|
-
|
|
1109
|
-
for (const n of names) {
|
|
1110
|
-
if (!(n in REGISTRY)) throw new Error(`unknown audit: ${n}`);
|
|
1111
|
-
}
|
|
1108
|
+
async function runOneAudit(site, name) {
|
|
1109
|
+
if (!(name in REGISTRY)) throw new Error(`unknown audit: ${name}`);
|
|
1112
1110
|
const spawn2 = timedSpawn(DEFAULT_AUDIT_TIMEOUT_MS);
|
|
1113
1111
|
const label = site.name ?? site.path;
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
)
|
|
1125
|
-
);
|
|
1126
|
-
}
|
|
1127
|
-
async function runAuditsAcross(sites, which) {
|
|
1128
|
-
const all = await Promise.all(sites.map((s) => runAudits(s, which)));
|
|
1129
|
-
return all.flat();
|
|
1112
|
+
try {
|
|
1113
|
+
return await REGISTRY[name]({ site, spawn: spawn2 });
|
|
1114
|
+
} catch (err) {
|
|
1115
|
+
return {
|
|
1116
|
+
audit: name,
|
|
1117
|
+
site: label,
|
|
1118
|
+
status: "fail",
|
|
1119
|
+
summary: `${name}: unexpected error \u2014 ${String(err)}`
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1130
1122
|
}
|
|
1131
1123
|
|
|
1132
1124
|
// src/cli/fleet/resolve-sites.ts
|
|
@@ -1292,8 +1284,87 @@ function formatTable(results) {
|
|
|
1292
1284
|
function exitCode(results) {
|
|
1293
1285
|
return results.some((r) => r.status === "fail") ? 1 : 0;
|
|
1294
1286
|
}
|
|
1287
|
+
function formatDuration(ms) {
|
|
1288
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
1289
|
+
const totalSeconds = Math.round(ms / 1e3);
|
|
1290
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
1291
|
+
const m = Math.floor(totalSeconds / 60);
|
|
1292
|
+
const s = totalSeconds % 60;
|
|
1293
|
+
return `${m}m${s.toString().padStart(2, "0")}s`;
|
|
1294
|
+
}
|
|
1295
|
+
function buildAuditTasks(sites, which, results, renderer) {
|
|
1296
|
+
const singleSite = sites.length === 1;
|
|
1297
|
+
if (singleSite) {
|
|
1298
|
+
const site = sites[0];
|
|
1299
|
+
return new Listr(
|
|
1300
|
+
which.map((name) => ({
|
|
1301
|
+
title: name,
|
|
1302
|
+
task: async (_ctx, task) => {
|
|
1303
|
+
const start = Date.now();
|
|
1304
|
+
const result = await runOneAudit(site, name);
|
|
1305
|
+
results.push(result);
|
|
1306
|
+
const elapsed = formatDuration(Date.now() - start);
|
|
1307
|
+
task.title = `${name}: ${result.summary} (${elapsed})`;
|
|
1308
|
+
if (result.status === "fail") throw new Error(result.summary);
|
|
1309
|
+
}
|
|
1310
|
+
})),
|
|
1311
|
+
{ concurrent: true, exitOnError: false, renderer }
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
return new Listr(
|
|
1315
|
+
sites.map((site) => {
|
|
1316
|
+
const label = site.name ?? site.path;
|
|
1317
|
+
return {
|
|
1318
|
+
title: label,
|
|
1319
|
+
task: async (_ctx, task) => {
|
|
1320
|
+
const start = Date.now();
|
|
1321
|
+
let done = 0;
|
|
1322
|
+
task.output = `0/${which.length} audits`;
|
|
1323
|
+
const settled = await Promise.all(
|
|
1324
|
+
which.map(async (name) => {
|
|
1325
|
+
const r = await runOneAudit(site, name);
|
|
1326
|
+
results.push(r);
|
|
1327
|
+
done += 1;
|
|
1328
|
+
task.output = `${done}/${which.length} audits`;
|
|
1329
|
+
return r;
|
|
1330
|
+
})
|
|
1331
|
+
);
|
|
1332
|
+
const elapsed = formatDuration(Date.now() - start);
|
|
1333
|
+
const failed = settled.filter((r) => r.status === "fail").length;
|
|
1334
|
+
const warned = settled.filter((r) => r.status === "warn").length;
|
|
1335
|
+
const note = failed > 0 ? `${failed} failed` : warned > 0 ? `${warned} warning${warned === 1 ? "" : "s"}` : "all green";
|
|
1336
|
+
task.title = `${label}: ${note} (${elapsed})`;
|
|
1337
|
+
if (failed > 0) throw new Error(`${label}: ${failed} audit(s) failed`);
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
}),
|
|
1341
|
+
{ concurrent: true, exitOnError: false, renderer }
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
function formatWriteSummary(summary) {
|
|
1345
|
+
const lines = summary.writes.map((w) => {
|
|
1346
|
+
if (w.audit === "lighthouse") {
|
|
1347
|
+
const s = w.counts;
|
|
1348
|
+
return ` lighthouse: P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo}`;
|
|
1349
|
+
}
|
|
1350
|
+
if (w.audit === "a11y") {
|
|
1351
|
+
return ` a11y: ${w.counts.violations} violations`;
|
|
1352
|
+
}
|
|
1353
|
+
if (w.audit === "deps") {
|
|
1354
|
+
const c2 = w.counts;
|
|
1355
|
+
return ` deps: ${c2.drifted} drifted (${c2.majorBehind} major)`;
|
|
1356
|
+
}
|
|
1357
|
+
const c = w.counts;
|
|
1358
|
+
return ` security: ${c.critical}C/${c.high}H/${c.moderate}M/${c.low}L`;
|
|
1359
|
+
});
|
|
1360
|
+
return `\u2192 wrote to Websites[${summary.siteName}]:
|
|
1361
|
+
${lines.join("\n")}`;
|
|
1362
|
+
}
|
|
1363
|
+
function rendererFor(json) {
|
|
1364
|
+
return json ? "silent" : "default";
|
|
1365
|
+
}
|
|
1295
1366
|
async function runAuditCommand(site, opts) {
|
|
1296
|
-
const which = parseOnly(opts.only);
|
|
1367
|
+
const which = parseOnly(opts.only) ?? ALL_AUDIT_NAMES;
|
|
1297
1368
|
const cwd = opts.cwd ? resolve2(opts.cwd) : process.cwd();
|
|
1298
1369
|
let sites = await resolveSites({
|
|
1299
1370
|
...site !== void 0 ? { site } : {},
|
|
@@ -1305,7 +1376,9 @@ async function runAuditCommand(site, opts) {
|
|
|
1305
1376
|
const workdir = opts.workdir ?? `${process.env.HOME ?? ""}/.reddoor-maint/sites`;
|
|
1306
1377
|
sites = await Promise.all(sites.map((s) => cloneIfNeeded(s, { workdir })));
|
|
1307
1378
|
}
|
|
1308
|
-
const results =
|
|
1379
|
+
const results = [];
|
|
1380
|
+
const renderer = rendererFor(opts.json);
|
|
1381
|
+
await buildAuditTasks(sites, which, results, renderer).run();
|
|
1309
1382
|
let output = opts.json ? JSON.stringify(results, null, 2) : formatTable(results);
|
|
1310
1383
|
if (opts.writeAirtable !== void 0) {
|
|
1311
1384
|
const { openBase: openBase2, readAirtableConfig: readAirtableConfig2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
@@ -1313,28 +1386,26 @@ async function runAuditCommand(site, opts) {
|
|
|
1313
1386
|
const { resolveSlugFromCwd: resolveSlugFromCwd2 } = await Promise.resolve().then(() => (init_lighthouse_airtable(), lighthouse_airtable_exports));
|
|
1314
1387
|
const { writeAuditsToAirtable: writeAuditsToAirtable2 } = await Promise.resolve().then(() => (init_write_audits_to_airtable(), write_audits_to_airtable_exports));
|
|
1315
1388
|
const slug = typeof opts.writeAirtable === "string" && opts.writeAirtable.length > 0 ? opts.writeAirtable : await resolveSlugFromCwd2(cwd);
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
output += `
|
|
1389
|
+
let writeSummary = null;
|
|
1390
|
+
await new Listr(
|
|
1391
|
+
[
|
|
1392
|
+
{
|
|
1393
|
+
title: `Write to Airtable[${slug}]`,
|
|
1394
|
+
task: async (_ctx, task) => {
|
|
1395
|
+
const base = openBase2(readAirtableConfig2());
|
|
1396
|
+
task.output = "loading Websites\u2026";
|
|
1397
|
+
const websites = await listWebsites2(base);
|
|
1398
|
+
task.output = "writing scores\u2026";
|
|
1399
|
+
writeSummary = await writeAuditsToAirtable2({ base, websites, slug, results });
|
|
1400
|
+
task.title = `Wrote to Websites[${writeSummary.siteName}] (${writeSummary.writes.length} audit type${writeSummary.writes.length === 1 ? "" : "s"})`;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
],
|
|
1404
|
+
{ renderer }
|
|
1405
|
+
).run();
|
|
1406
|
+
if (writeSummary) output += `
|
|
1335
1407
|
|
|
1336
|
-
|
|
1337
|
-
${lines.join("\n")}`;
|
|
1408
|
+
${formatWriteSummary(writeSummary)}`;
|
|
1338
1409
|
}
|
|
1339
1410
|
return { output, code: exitCode(results) };
|
|
1340
1411
|
}
|