@reddoorla/maintenance 0.1.3 → 0.2.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
@@ -10,7 +10,7 @@ import { resolve as resolve2 } from "path";
10
10
 
11
11
  // src/audits/util/spawn.ts
12
12
  import { spawn } from "child_process";
13
- var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve6, reject) => {
13
+ var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve7, reject) => {
14
14
  const streaming = opts.streaming === true;
15
15
  const child = spawn(cmd, [...args], {
16
16
  cwd: opts.cwd,
@@ -33,7 +33,7 @@ var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve6, reject) => {
33
33
  });
34
34
  child.on("close", (code) => {
35
35
  if (timer) clearTimeout(timer);
36
- resolve6({ code: code ?? -1, stdout, stderr });
36
+ resolve7({ code: code ?? -1, stdout, stderr });
37
37
  });
38
38
  });
39
39
 
@@ -357,7 +357,10 @@ var lighthouseConfig = {
357
357
  ci: {
358
358
  collect: {
359
359
  url: ["http://localhost:5173/dev/a11y-fixtures"],
360
- startServerCommand: "pnpm vite:dev",
360
+ // `npm run vite:dev` works on both pnpm and npm sites — pnpm respects
361
+ // the `run` form too. Keeps this config portable across the fleet
362
+ // while sites transition to pnpm.
363
+ startServerCommand: "npm run vite:dev",
361
364
  startServerReadyPattern: "ready in",
362
365
  startServerReadyTimeout: 12e4,
363
366
  numberOfRuns: 1,
@@ -509,7 +512,8 @@ var playwrightA11yConfig = defineConfig({
509
512
  }
510
513
  ],
511
514
  webServer: {
512
- command: "pnpm vite:dev",
515
+ // Portable across pnpm and npm sites — pnpm respects `npm run` too.
516
+ command: "npm run vite:dev",
513
517
  url: "http://localhost:5173/dev/a11y-fixtures",
514
518
  reuseExistingServer: !process.env.CI,
515
519
  timeout: 12e4
@@ -1546,12 +1550,151 @@ async function runUpgradeCommand(upgradeName, site, opts = {}) {
1546
1550
  return { output, code };
1547
1551
  }
1548
1552
 
1553
+ // src/cli/commands/convert-to-pnpm.ts
1554
+ import { resolve as resolve6 } from "path";
1555
+
1556
+ // src/recipes/convert-to-pnpm.ts
1557
+ import { rm as rm3, stat as stat2 } from "fs/promises";
1558
+ import { join as join14 } from "path";
1559
+
1560
+ // src/recipes/convert-to-pnpm/script-rewrites.ts
1561
+ function rewriteScriptForPnpm(script) {
1562
+ let out = script;
1563
+ out = out.replace(/\bnpm run(?=\s)/g, "pnpm run");
1564
+ out = out.replace(/\bnpx(?=\s)/g, "pnpm dlx");
1565
+ return out;
1566
+ }
1567
+ function rewriteScriptsForPnpm(scripts) {
1568
+ const next = {};
1569
+ let changedCount = 0;
1570
+ for (const [name, value] of Object.entries(scripts)) {
1571
+ const rewritten = rewriteScriptForPnpm(value);
1572
+ next[name] = rewritten;
1573
+ if (rewritten !== value) changedCount++;
1574
+ }
1575
+ return { scripts: next, changedCount };
1576
+ }
1577
+
1578
+ // src/recipes/convert-to-pnpm.ts
1579
+ var DEFAULT_PNPM_VERSION = "10.33.1";
1580
+ async function exists(path) {
1581
+ try {
1582
+ await stat2(path);
1583
+ return true;
1584
+ } catch {
1585
+ return false;
1586
+ }
1587
+ }
1588
+ function siteLabel9(site) {
1589
+ return site.name ?? site.path;
1590
+ }
1591
+ async function convertToPnpm(site, opts = {}) {
1592
+ const label = siteLabel9(site);
1593
+ const spawn2 = opts.spawn ?? defaultSpawn;
1594
+ const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
1595
+ const pnpmLockPath = join14(site.path, "pnpm-lock.yaml");
1596
+ const npmLockPath = join14(site.path, "package-lock.json");
1597
+ const yarnLockPath = join14(site.path, "yarn.lock");
1598
+ if (await exists(pnpmLockPath)) {
1599
+ return {
1600
+ recipe: "convert-to-pnpm",
1601
+ site: label,
1602
+ status: "noop",
1603
+ commits: [],
1604
+ notes: "site already has pnpm-lock.yaml"
1605
+ };
1606
+ }
1607
+ const hasNpmLock = await exists(npmLockPath);
1608
+ const hasYarnLock = await exists(yarnLockPath);
1609
+ if (!hasNpmLock && !hasYarnLock) {
1610
+ return {
1611
+ recipe: "convert-to-pnpm",
1612
+ site: label,
1613
+ status: "noop",
1614
+ commits: [],
1615
+ notes: "no convertible lockfile (package-lock.json or yarn.lock) at site root"
1616
+ };
1617
+ }
1618
+ if (!await isWorkingTreeClean(site.path)) {
1619
+ throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
1620
+ }
1621
+ const branch = branchName("convert-to-pnpm");
1622
+ await createBranch(site.path, branch);
1623
+ const shas = [];
1624
+ if (hasNpmLock) await rm3(npmLockPath, { force: true });
1625
+ if (hasYarnLock) await rm3(yarnLockPath, { force: true });
1626
+ const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
1627
+ const lockSha = await commit(site.path, `chore(pnpm): remove ${sourceLock}`);
1628
+ if (lockSha) shas.push(lockSha);
1629
+ const pkgPath = join14(site.path, "package.json");
1630
+ const pkg = await readPackageJson(pkgPath);
1631
+ const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
1632
+ if (pkg.scripts && typeof pkg.scripts === "object") {
1633
+ const { scripts: rewritten, changedCount } = rewriteScriptsForPnpm(
1634
+ pkg.scripts
1635
+ );
1636
+ if (changedCount > 0) {
1637
+ next.scripts = rewritten;
1638
+ }
1639
+ }
1640
+ await writePackageJson(pkgPath, next);
1641
+ const pkgSha = await commit(site.path, "chore(pnpm): pin packageManager + rewrite npm scripts");
1642
+ if (pkgSha) shas.push(pkgSha);
1643
+ const installResult = await spawn2("pnpm", ["install"], {
1644
+ cwd: site.path,
1645
+ streaming: true
1646
+ });
1647
+ if (installResult.code !== 0) {
1648
+ return {
1649
+ recipe: "convert-to-pnpm",
1650
+ site: label,
1651
+ status: "failed",
1652
+ commits: shas,
1653
+ notes: `pnpm install failed (exit ${installResult.code}). branch ${branch} left for inspection.`
1654
+ };
1655
+ }
1656
+ const installSha = await commit(site.path, "chore(pnpm): add pnpm-lock.yaml");
1657
+ if (installSha) shas.push(installSha);
1658
+ return {
1659
+ recipe: "convert-to-pnpm",
1660
+ site: label,
1661
+ status: "applied",
1662
+ commits: shas,
1663
+ notes: `branch: ${branch}`
1664
+ };
1665
+ }
1666
+
1667
+ // src/cli/commands/convert-to-pnpm.ts
1668
+ function formatResult4(r) {
1669
+ if (r.status === "noop") return `[${r.site}] noop: ${r.notes ?? ""}`;
1670
+ if (r.status === "failed") return `[${r.site}] failed: ${r.notes ?? ""}`;
1671
+ return `[${r.site}] applied: ${r.commits.length} commit(s)
1672
+ ${r.notes ?? ""}`;
1673
+ }
1674
+ async function runConvertToPnpmCommand(site, opts) {
1675
+ const cwd = opts.cwd ? resolve6(opts.cwd) : process.cwd();
1676
+ let sites = await resolveSites({
1677
+ ...site !== void 0 ? { site } : {},
1678
+ ...opts.fleet !== void 0 ? { fleet: opts.fleet } : {},
1679
+ cwd
1680
+ });
1681
+ if (opts.fleet) {
1682
+ const workdir = opts.workdir ?? `${process.env.HOME ?? ""}/.reddoor-maint/sites`;
1683
+ sites = await Promise.all(sites.map((s) => cloneIfNeeded(s, { workdir })));
1684
+ }
1685
+ const results = [];
1686
+ for (const s of sites) results.push(await convertToPnpm(s));
1687
+ const output = results.map(formatResult4).join("\n");
1688
+ const code = results.some((r) => r.status === "failed") ? 1 : 0;
1689
+ return { output, code };
1690
+ }
1691
+
1549
1692
  // src/cli/version.ts
1550
1693
  import { readFileSync } from "fs";
1551
- import { join as join14 } from "path";
1694
+ import { join as join15 } from "path";
1552
1695
  function resolvePackageVersion(fromDir) {
1553
1696
  try {
1554
- const raw = readFileSync(join14(fromDir, "..", "..", "package.json"), "utf-8");
1697
+ const raw = readFileSync(join15(fromDir, "..", "..", "package.json"), "utf-8");
1555
1698
  const pkg = JSON.parse(raw);
1556
1699
  return pkg.version ?? "unknown";
1557
1700
  } catch {
@@ -1572,7 +1715,8 @@ var AUDIT_DESCRIPTIONS = {
1572
1715
  var RECIPE_DESCRIPTIONS = {
1573
1716
  "sync-configs": "Overwrite a site's canonical configs to match @reddoorla/maintenance.",
1574
1717
  "bump-deps": "Bump dependencies and commit the lockfile change.",
1575
- "svelte-4-to-5": "Run the 7-commit Svelte 4 \u2192 5 upgrade recipe."
1718
+ "svelte-4-to-5": "Run the 7-commit Svelte 4 \u2192 5 upgrade recipe.",
1719
+ "convert-to-pnpm": "Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts)."
1576
1720
  };
1577
1721
  var cli = cac("reddoor-maint");
1578
1722
  cli.option("--cwd <path>", "Override working directory (default: process.cwd())");
@@ -1639,6 +1783,22 @@ cli.command("upgrade <upgrade> [site]", "Run a named upgrade recipe (svelte-4-to
1639
1783
  }
1640
1784
  }
1641
1785
  );
1786
+ cli.command(
1787
+ "convert-to-pnpm [site]",
1788
+ "Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts)."
1789
+ ).option("--fleet <inventory>", "Inventory file (.json or .mjs/.js)").option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
1790
+ async (site, opts) => {
1791
+ try {
1792
+ const { output, code } = await runConvertToPnpmCommand(site, opts);
1793
+ console.log(output);
1794
+ process.exit(code);
1795
+ } catch (err) {
1796
+ const e = err;
1797
+ console.error(opts.verbose ? e.stack ?? e.message : e.message ?? String(err));
1798
+ process.exit(e.exitCode ?? 1);
1799
+ }
1800
+ }
1801
+ );
1642
1802
  cli.help();
1643
1803
  cli.version(version);
1644
1804
  cli.parse();