@reddoorla/maintenance 0.6.6 → 0.6.7

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
@@ -41,6 +41,11 @@ var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
41
41
  import { readFile } from "fs/promises";
42
42
  import { join } from "path";
43
43
 
44
+ // src/util/site.ts
45
+ function siteLabel(site) {
46
+ return site.name ?? site.path;
47
+ }
48
+
44
49
  // src/configs/baseline-versions.ts
45
50
  var baselineVersions = {
46
51
  // SvelteKit core
@@ -81,9 +86,6 @@ var baselineVersions = {
81
86
  };
82
87
 
83
88
  // src/audits/deps.ts
84
- function siteLabel(site) {
85
- return site.name ?? site.path;
86
- }
87
89
  function stripCaret(range) {
88
90
  return range.replace(/^[\^~]/, "");
89
91
  }
@@ -156,9 +158,6 @@ import { check as prettierCheck, resolveConfig as prettierResolveConfig } from "
156
158
  import { glob } from "tinyglobby";
157
159
  var TARGET_GLOBS = ["**/*.{ts,js,svelte}"];
158
160
  var IGNORE = ["node_modules/**", "dist/**", ".svelte-kit/**", "build/**", ".netlify/**"];
159
- function siteLabel2(site) {
160
- return site.name ?? site.path;
161
- }
162
161
  async function listFiles(cwd) {
163
162
  return glob(TARGET_GLOBS, { cwd, ignore: IGNORE, absolute: false });
164
163
  }
@@ -168,7 +167,7 @@ async function lintAudit(ctx) {
168
167
  if (!existsSync(configPath)) {
169
168
  return {
170
169
  audit: "lint",
171
- site: siteLabel2(site),
170
+ site: siteLabel(site),
172
171
  status: "skip",
173
172
  summary: "no eslint config at site root"
174
173
  };
@@ -194,7 +193,7 @@ async function lintAudit(ctx) {
194
193
  const summary = status === "pass" ? `lint clean across ${relFiles.length} files` : `${eslintErrors} eslint errors, ${eslintWarnings} warnings, ${prettierUnformatted.length} unformatted`;
195
194
  return {
196
195
  audit: "lint",
197
- site: siteLabel2(site),
196
+ site: siteLabel(site),
198
197
  status,
199
198
  summary,
200
199
  details: {
@@ -207,9 +206,6 @@ async function lintAudit(ctx) {
207
206
  }
208
207
 
209
208
  // src/audits/security.ts
210
- function siteLabel3(site) {
211
- return site.name ?? site.path;
212
- }
213
209
  function classify(v) {
214
210
  if (v.critical > 0 || v.high > 0) return "fail";
215
211
  if (v.moderate > 0 || v.low > 0) return "warn";
@@ -295,7 +291,8 @@ async function runAuditTool(spawn2, cmd, args, cwd) {
295
291
  if (errEnvelope && typeof errEnvelope === "object") {
296
292
  return { kind: "error", reason: errEnvelope.code ?? "error envelope returned" };
297
293
  }
298
- if (!parsed.metadata?.vulnerabilities) {
294
+ const vulnsMeta = parsed.metadata?.vulnerabilities;
295
+ if (!vulnsMeta || Object.keys(vulnsMeta).length === 0) {
299
296
  return { kind: "error", reason: "no metadata.vulnerabilities in output" };
300
297
  }
301
298
  return { kind: "ok", parsed };
@@ -303,7 +300,7 @@ async function runAuditTool(spawn2, cmd, args, cwd) {
303
300
  async function securityAudit(ctx) {
304
301
  const spawn2 = ctx.spawn ?? defaultSpawn;
305
302
  const site = ctx.site;
306
- const label = siteLabel3(site);
303
+ const label = siteLabel(site);
307
304
  let used = "pnpm audit";
308
305
  let result = await runAuditTool(spawn2, "pnpm", ["audit", "--json", "--prod"], site.path);
309
306
  if (result.kind !== "ok") {
@@ -384,9 +381,6 @@ var lighthouseConfig = {
384
381
  };
385
382
 
386
383
  // src/audits/lighthouse.ts
387
- function siteLabel4(site) {
388
- return site.name ?? site.path;
389
- }
390
384
  async function readJsonMaybe(path) {
391
385
  try {
392
386
  const raw = await readFile3(path, "utf-8");
@@ -424,7 +418,7 @@ function messageForAssertion(a) {
424
418
  async function lighthouseAudit(ctx) {
425
419
  const spawn2 = ctx.spawn ?? defaultSpawn;
426
420
  const site = ctx.site;
427
- const label = siteLabel4(site);
421
+ const label = siteLabel(site);
428
422
  const configDir = await mkdtemp(join3(tmpdir(), "reddoor-lhci-"));
429
423
  const configPath = join3(configDir, "lighthouserc.json");
430
424
  await writeFile(configPath, JSON.stringify(lighthouseConfig), "utf-8");
@@ -522,9 +516,6 @@ var playwrightA11yConfig = defineConfig({
522
516
 
523
517
  // src/audits/a11y.ts
524
518
  var RESULTS_REL = ".reddoor-a11y/results.json";
525
- function siteLabel5(site) {
526
- return site.name ?? site.path;
527
- }
528
519
  async function readJsonMaybe2(path) {
529
520
  try {
530
521
  const raw = await readFile4(path, "utf-8");
@@ -582,7 +573,7 @@ test("a11y across configured routes", async ({ page }) => {
582
573
  async function a11yAudit(ctx) {
583
574
  const spawn2 = ctx.spawn ?? defaultSpawn;
584
575
  const site = ctx.site;
585
- const label = siteLabel5(site);
576
+ const label = siteLabel(site);
586
577
  const specDir = await mkdtemp2(join4(tmpdir2(), "reddoor-a11y-spec-"));
587
578
  const specPath = join4(specDir, "a11y.spec.ts");
588
579
  await writeFile2(specPath, buildSpec(), "utf-8");
@@ -1007,9 +998,8 @@ async function git(cwd, args) {
1007
998
  return exec("git", args, { cwd, env: process.env });
1008
999
  }
1009
1000
  function branchName(recipe, when = /* @__PURE__ */ new Date()) {
1010
- const iso = when.toISOString().replace(/[-:.]/g, "").replace(/Z$/, "Z");
1011
- const trimmed = iso.replace(/(\d{8}T\d{6})\d+(Z)$/, "$1$2");
1012
- return `maint/${recipe}-${trimmed}`;
1001
+ const compact = when.toISOString().replace(/[-:.]/g, "");
1002
+ return `maint/${recipe}-${compact}`;
1013
1003
  }
1014
1004
  async function isWorkingTreeClean(cwd) {
1015
1005
  const { stdout } = await git(cwd, ["status", "--porcelain"]);
@@ -1051,9 +1041,6 @@ var ALL_CONFIG_NAMES = [
1051
1041
  function isConfigName(value) {
1052
1042
  return ALL_CONFIG_NAMES.includes(value);
1053
1043
  }
1054
- function siteLabel6(site) {
1055
- return site.name ?? site.path;
1056
- }
1057
1044
  async function readMaybe(path) {
1058
1045
  try {
1059
1046
  return await readFile6(path, "utf-8");
@@ -1084,7 +1071,7 @@ async function applyGitignore(cwd, plan) {
1084
1071
  }
1085
1072
  }
1086
1073
  async function syncConfigs(site, opts = {}) {
1087
- const label = siteLabel6(site);
1074
+ const label = siteLabel(site);
1088
1075
  const requested = opts.which ?? ALL_TEMPLATES.map((t) => t.config).concat(GITIGNORE_CONFIG);
1089
1076
  const templateNames = requested.filter((c) => c !== GITIGNORE_CONFIG);
1090
1077
  const templates = templatesByName(templateNames);
@@ -1209,9 +1196,6 @@ import { resolve as resolve4 } from "path";
1209
1196
  // src/recipes/bump-deps.ts
1210
1197
  import { stat as stat2 } from "fs/promises";
1211
1198
  import { join as join8 } from "path";
1212
- function siteLabel7(site) {
1213
- return site.name ?? site.path;
1214
- }
1215
1199
  async function exists(path) {
1216
1200
  try {
1217
1201
  await stat2(path);
@@ -1230,7 +1214,7 @@ function upFlagsForGroup(group) {
1230
1214
  return [];
1231
1215
  }
1232
1216
  async function bumpDeps(site, opts = {}) {
1233
- const label = siteLabel7(site);
1217
+ const label = siteLabel(site);
1234
1218
  const group = opts.group ?? "minor";
1235
1219
  const spawn2 = opts.spawn ?? defaultSpawn;
1236
1220
  const hasPnpmLock = await exists(join8(site.path, "pnpm-lock.yaml"));
@@ -1507,6 +1491,34 @@ import { glob as glob2 } from "tinyglobby";
1507
1491
  // src/recipes/svelte-5/codemods/on-event-to-handler.ts
1508
1492
  var SCRIPT_BLOCK = /<script\b[^>]*>[\s\S]*?<\/script>/g;
1509
1493
  var SIMPLE_ON_EVENT = /\bon:([a-z]+)(?=\s*=)/g;
1494
+ var MODIFIER_EVENT = /\bon:[a-z]+\|[a-zA-Z]+(?=\s*=)/g;
1495
+ function flagEventModifiers(source) {
1496
+ const insertions = [];
1497
+ let m;
1498
+ MODIFIER_EVENT.lastIndex = 0;
1499
+ while ((m = MODIFIER_EVENT.exec(source)) !== null) {
1500
+ const tagStart = source.lastIndexOf("<", m.index);
1501
+ if (tagStart === -1) continue;
1502
+ const prevLineEnd = tagStart - 1;
1503
+ if (prevLineEnd >= 0) {
1504
+ const prevLineStart = source.lastIndexOf("\n", prevLineEnd - 1) + 1;
1505
+ const prevLine = source.slice(prevLineStart, prevLineEnd + 1);
1506
+ if (/<!--\s*@migration-task/.test(prevLine)) continue;
1507
+ }
1508
+ const lineStart = source.lastIndexOf("\n", tagStart - 1) + 1;
1509
+ const indent = source.slice(lineStart, tagStart);
1510
+ const safeIndent = /^[ \t]*$/.test(indent) ? indent : "";
1511
+ insertions.push({ tagStart, indent: safeIndent, modifier: m[0] });
1512
+ }
1513
+ let out = source;
1514
+ for (let i = insertions.length - 1; i >= 0; i--) {
1515
+ const { tagStart, indent, modifier } = insertions[i];
1516
+ const comment = `<!-- @migration-task: Svelte 5 removed event modifier syntax (\`${modifier}\`). Rewrite inline, e.g. onclick={(e) => { e.preventDefault(); ... }}. -->
1517
+ ${indent}`;
1518
+ out = out.slice(0, tagStart) + comment + out.slice(tagStart);
1519
+ }
1520
+ return out;
1521
+ }
1510
1522
  function onEventToHandler(source) {
1511
1523
  const masked = [];
1512
1524
  const placeholder = (i) => ` SCRIPT_${i} `;
@@ -1514,8 +1526,9 @@ function onEventToHandler(source) {
1514
1526
  masked.push(match);
1515
1527
  return placeholder(masked.length - 1);
1516
1528
  });
1517
- const rewritten = intermediate.replace(SIMPLE_ON_EVENT, (_full, name) => `on${name}`);
1518
- let out = rewritten;
1529
+ let processed = intermediate.replace(SIMPLE_ON_EVENT, (_full, name) => `on${name}`);
1530
+ processed = flagEventModifiers(processed);
1531
+ let out = processed;
1519
1532
  masked.forEach((blk, i) => {
1520
1533
  out = out.replace(placeholder(i), blk);
1521
1534
  });
@@ -1565,6 +1578,22 @@ function exportLetToProps(source) {
1565
1578
  return source.replace(SCRIPT_BLOCK2, (full) => full.replace(inner, body));
1566
1579
  }
1567
1580
 
1581
+ // src/util/svelte-source.ts
1582
+ function findStringEnd(source, openIdx) {
1583
+ const quote = source[openIdx];
1584
+ let i = openIdx + 1;
1585
+ while (i < source.length) {
1586
+ const ch = source[i];
1587
+ if (ch === "\\") {
1588
+ i += 2;
1589
+ continue;
1590
+ }
1591
+ if (ch === quote) return i;
1592
+ i++;
1593
+ }
1594
+ return -1;
1595
+ }
1596
+
1568
1597
  // src/recipes/svelte-5/codemods/dollar-restprops.ts
1569
1598
  function removeInterfaceBlock(source) {
1570
1599
  const re = /^\s*interface\s+\$\$Props\s*\{/m;
@@ -1588,20 +1617,6 @@ function removeInterfaceBlock(source) {
1588
1617
  out = out.slice(0, match.index) + out.slice(endIdx);
1589
1618
  }
1590
1619
  }
1591
- function findStringEnd(source, openIdx) {
1592
- const quote = source[openIdx];
1593
- let i = openIdx + 1;
1594
- while (i < source.length) {
1595
- const ch = source[i];
1596
- if (ch === "\\") {
1597
- i += 2;
1598
- continue;
1599
- }
1600
- if (ch === quote) return i;
1601
- i++;
1602
- }
1603
- return -1;
1604
- }
1605
1620
  function maskStringLiterals(source) {
1606
1621
  const strings = [];
1607
1622
  let out = "";
@@ -1678,7 +1693,8 @@ function stateEffectSyncToDerived(source) {
1678
1693
 
1679
1694
  // src/recipes/svelte-5/codemods/dollar-props-class.ts
1680
1695
  var PROPS_DESTRUCTURE = /let\s*\{([\s\S]*?)\}(\s*:\s*\{([\s\S]*?)\})?\s*=\s*\$props\(\)/;
1681
- var DOLLAR_PROPS_CLASS = /\$\$props\.class\b/g;
1696
+ var HAS_DOLLAR_PROPS_CLASS = /\$\$props\.class\b/;
1697
+ var DOLLAR_PROPS_CLASS_GLOBAL = /\$\$props\.class\b/g;
1682
1698
  var DOLLAR_PROPS_ANY = /\$\$props\b/;
1683
1699
  var SCRIPT_BLOCK4 = /<script\b[^>]*>[\s\S]*?<\/script>/g;
1684
1700
  var MIGRATION_TASK = /^<!--\s*@migration-task[\s\S]*?-->\s*\n?/gm;
@@ -1700,8 +1716,7 @@ function restoreScripts(masked, blocks) {
1700
1716
  }
1701
1717
  function dollarPropsClass(source) {
1702
1718
  const { masked } = maskScripts(source);
1703
- if (!DOLLAR_PROPS_CLASS.test(masked)) return source;
1704
- DOLLAR_PROPS_CLASS.lastIndex = 0;
1719
+ if (!HAS_DOLLAR_PROPS_CLASS.test(masked)) return source;
1705
1720
  if (!PROPS_DESTRUCTURE.test(source)) return source;
1706
1721
  let updated = source.replace(PROPS_DESTRUCTURE, (full, body, typeAnno, typeBody) => {
1707
1722
  if (/\bclass\s*:/.test(body)) return full;
@@ -1715,7 +1730,7 @@ function dollarPropsClass(source) {
1715
1730
  return `let { ${newBody} } = $props()`;
1716
1731
  });
1717
1732
  const reMasked = maskScripts(updated);
1718
- const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS, IDENT);
1733
+ const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS_GLOBAL, IDENT);
1719
1734
  updated = restoreScripts(templateRewritten, reMasked.blocks);
1720
1735
  const stripped = updated.replace(MIGRATION_TASK, "");
1721
1736
  if (!DOLLAR_PROPS_ANY.test(stripped)) {
@@ -1734,7 +1749,7 @@ function findMatchingClose(source, openIdx) {
1734
1749
  while (i < source.length) {
1735
1750
  const ch = source[i];
1736
1751
  if (ch === '"' || ch === "'" || ch === "`") {
1737
- const closeStr = findStringClose(source, i);
1752
+ const closeStr = findStringEnd(source, i);
1738
1753
  if (closeStr === -1) return -1;
1739
1754
  i = closeStr + 1;
1740
1755
  continue;
@@ -1748,20 +1763,6 @@ function findMatchingClose(source, openIdx) {
1748
1763
  }
1749
1764
  return -1;
1750
1765
  }
1751
- function findStringClose(source, openIdx) {
1752
- const quote = source[openIdx];
1753
- let i = openIdx + 1;
1754
- while (i < source.length) {
1755
- const ch = source[i];
1756
- if (ch === "\\") {
1757
- i += 2;
1758
- continue;
1759
- }
1760
- if (ch === quote) return i;
1761
- i++;
1762
- }
1763
- return -1;
1764
- }
1765
1766
  var MIGRATION_MARKER = "// @migration-task: $effect won't trigger UI updates on plain `let` bindings \u2014 refine mutated locals to $state or split into per-variable $derived.";
1766
1767
  function transformBlocks(body) {
1767
1768
  const out = [];
@@ -1873,9 +1874,6 @@ async function writeMigrationSummary(input) {
1873
1874
  }
1874
1875
 
1875
1876
  // src/recipes/svelte-5/index.ts
1876
- function siteLabel8(site) {
1877
- return site.name ?? site.path;
1878
- }
1879
1877
  async function alreadyOnSvelte5(cwd) {
1880
1878
  try {
1881
1879
  const pkg = await readPackageJson(join14(cwd, "package.json"));
@@ -1886,7 +1884,7 @@ async function alreadyOnSvelte5(cwd) {
1886
1884
  }
1887
1885
  }
1888
1886
  async function upgradeSvelte4to5(site, opts = {}) {
1889
- const label = siteLabel8(site);
1887
+ const label = siteLabel(site);
1890
1888
  const spawn2 = opts.spawn ?? defaultSpawn;
1891
1889
  if (await alreadyOnSvelte5(site.path)) {
1892
1890
  return {
@@ -2026,11 +2024,8 @@ async function exists2(path) {
2026
2024
  return false;
2027
2025
  }
2028
2026
  }
2029
- function siteLabel9(site) {
2030
- return site.name ?? site.path;
2031
- }
2032
2027
  async function convertToPnpm(site, opts = {}) {
2033
- const label = siteLabel9(site);
2028
+ const label = siteLabel(site);
2034
2029
  const spawn2 = opts.spawn ?? defaultSpawn;
2035
2030
  const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
2036
2031
  const pnpmLockPath = join15(site.path, "pnpm-lock.yaml");
@@ -2184,14 +2179,11 @@ async function exists3(path) {
2184
2179
  return false;
2185
2180
  }
2186
2181
  }
2187
- function siteLabel10(site) {
2188
- return site.name ?? site.path;
2189
- }
2190
2182
  function isDeclared(pkg, name) {
2191
2183
  return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
2192
2184
  }
2193
2185
  async function onboard(site, opts = {}) {
2194
- const label = siteLabel10(site);
2186
+ const label = siteLabel(site);
2195
2187
  const spawn2 = opts.spawn ?? defaultSpawn;
2196
2188
  const audits = opts.audits ?? ["lighthouse", "a11y"];
2197
2189
  const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);
@@ -2309,11 +2301,8 @@ import { resolve as resolve8 } from "path";
2309
2301
  // src/recipes/svelte-codemods.ts
2310
2302
  import { writeFile as writeFile8 } from "fs/promises";
2311
2303
  import { join as join18 } from "path";
2312
- function siteLabel11(site) {
2313
- return site.name ?? site.path;
2314
- }
2315
2304
  async function svelteCodemods(site) {
2316
- const label = siteLabel11(site);
2305
+ const label = siteLabel(site);
2317
2306
  const changes = await planGotchaCodemods(site.path);
2318
2307
  if (changes.length === 0) {
2319
2308
  return {
@@ -2401,6 +2390,17 @@ var RECIPE_DESCRIPTIONS = {
2401
2390
  "convert-to-pnpm": "Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts).",
2402
2391
  onboard: "Install @reddoorla/maintenance + audit deps on a site (preferred first step)."
2403
2392
  };
2393
+ async function runOrExit(fn, opts) {
2394
+ try {
2395
+ const { output, code } = await fn();
2396
+ console.log(output);
2397
+ process.exit(code);
2398
+ } catch (err) {
2399
+ const e = err;
2400
+ console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2401
+ process.exit(e.exitCode ?? 1);
2402
+ }
2403
+ }
2404
2404
  var cli = cac("reddoor-maint");
2405
2405
  cli.option("--cwd <path>", "Override working directory (default: process.cwd())");
2406
2406
  cli.option("--verbose", "Verbose output (full stack on errors)");
@@ -2415,101 +2415,31 @@ cli.command("list-recipes", "Print the available recipes.").action(() => {
2415
2415
  }
2416
2416
  });
2417
2417
  cli.command("audit [site]", "Run audits against a site (default: cwd).").option("--only <names>", "Comma-separated audit names (e.g. deps,lighthouse)").option("--json", "Machine-readable JSON output").option("--fleet <inventory>", "Inventory file (.json or .mjs/.js); aggregates across sites").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2418
- async (site, opts) => {
2419
- try {
2420
- const { output, code } = await runAuditCommand(site, opts);
2421
- console.log(output);
2422
- process.exit(code);
2423
- } catch (err) {
2424
- const e = err;
2425
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2426
- process.exit(e.exitCode ?? 1);
2427
- }
2428
- }
2418
+ async (site, opts) => runOrExit(() => runAuditCommand(site, opts), opts)
2429
2419
  );
2430
2420
  cli.command("sync-configs [site]", "Sync canonical configs into a site.").option("--only <names>", "Comma-separated config names (e.g. eslint,prettier)").option("--dry", "Print diff without writing").option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2431
- async (site, opts) => {
2432
- try {
2433
- const { output, code } = await runSyncConfigsCommand(site, opts);
2434
- console.log(output);
2435
- process.exit(code);
2436
- } catch (err) {
2437
- const e = err;
2438
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2439
- process.exit(e.exitCode ?? 1);
2440
- }
2441
- }
2421
+ async (site, opts) => runOrExit(() => runSyncConfigsCommand(site, opts), opts)
2442
2422
  );
2443
2423
  cli.command("bump-deps [site]", "Bump dependencies.").option("--group <group>", "patch | minor | major", { default: "minor" }).option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2444
- async (site, opts) => {
2445
- try {
2446
- const { output, code } = await runBumpDepsCommand(site, opts);
2447
- console.log(output);
2448
- process.exit(code);
2449
- } catch (err) {
2450
- const e = err;
2451
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2452
- process.exit(e.exitCode ?? 1);
2453
- }
2454
- }
2424
+ async (site, opts) => runOrExit(() => runBumpDepsCommand(site, opts), opts)
2455
2425
  );
2456
2426
  cli.command("upgrade <upgrade> [site]", "Run a named upgrade recipe (svelte-4-to-5).").example("reddoor-maint upgrade svelte-4-to-5 ./my-site").option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2457
- async (upgrade, site, opts) => {
2458
- try {
2459
- const { output, code } = await runUpgradeCommand(upgrade, site, opts);
2460
- console.log(output);
2461
- process.exit(code);
2462
- } catch (err) {
2463
- const e = err;
2464
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2465
- process.exit(e.exitCode ?? 1);
2466
- }
2467
- }
2427
+ async (upgrade, site, opts) => runOrExit(() => runUpgradeCommand(upgrade, site, opts), opts)
2468
2428
  );
2469
2429
  cli.command(
2470
2430
  "convert-to-pnpm [site]",
2471
2431
  "Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts)."
2472
2432
  ).option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2473
- async (site, opts) => {
2474
- try {
2475
- const { output, code } = await runConvertToPnpmCommand(site, opts);
2476
- console.log(output);
2477
- process.exit(code);
2478
- } catch (err) {
2479
- const e = err;
2480
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2481
- process.exit(e.exitCode ?? 1);
2482
- }
2483
- }
2433
+ async (site, opts) => runOrExit(() => runConvertToPnpmCommand(site, opts), opts)
2484
2434
  );
2485
2435
  cli.command("svelte-codemods [site]", "Apply Svelte 5 gotcha codemods to an already-migrated site.").option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2486
- async (site, opts) => {
2487
- try {
2488
- const { output, code } = await runSvelteCodemodsCommand(site, opts);
2489
- console.log(output);
2490
- process.exit(code);
2491
- } catch (err) {
2492
- const e = err;
2493
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2494
- process.exit(e.exitCode ?? 1);
2495
- }
2496
- }
2436
+ async (site, opts) => runOrExit(() => runSvelteCodemodsCommand(site, opts), opts)
2497
2437
  );
2498
2438
  cli.command(
2499
2439
  "onboard [site]",
2500
2440
  "Install @reddoorla/maintenance + audit deps on a site (run after convert-to-pnpm)."
2501
2441
  ).option("--audits <names>", "Comma-separated audit subset: lighthouse,a11y (default: both)").option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
2502
- async (site, opts) => {
2503
- try {
2504
- const { output, code } = await runOnboardCommand(site, opts);
2505
- console.log(output);
2506
- process.exit(code);
2507
- } catch (err) {
2508
- const e = err;
2509
- console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
2510
- process.exit(e.exitCode ?? 1);
2511
- }
2512
- }
2442
+ async (site, opts) => runOrExit(() => runOnboardCommand(site, opts), opts)
2513
2443
  );
2514
2444
  cli.help();
2515
2445
  cli.version(version);