@reddoorla/maintenance 0.6.5 → 0.6.6

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
@@ -1040,6 +1040,17 @@ async function commit(cwd, message) {
1040
1040
 
1041
1041
  // src/recipes/sync-configs.ts
1042
1042
  var GITIGNORE_CONFIG = "gitignore";
1043
+ var ALL_CONFIG_NAMES = [
1044
+ "lighthouse",
1045
+ "eslint",
1046
+ "prettier",
1047
+ "playwright-a11y",
1048
+ "svelte",
1049
+ "gitignore"
1050
+ ];
1051
+ function isConfigName(value) {
1052
+ return ALL_CONFIG_NAMES.includes(value);
1053
+ }
1043
1054
  function siteLabel6(site) {
1044
1055
  return site.name ?? site.path;
1045
1056
  }
@@ -1119,12 +1130,34 @@ async function syncConfigs(site, opts = {}) {
1119
1130
 
1120
1131
  // src/cli/commands/sync-configs.ts
1121
1132
  function parseOnly2(value) {
1122
- return value ? value.split(",").map((s) => s.trim()) : void 0;
1133
+ if (!value) return void 0;
1134
+ const names = value.split(",").map((s) => s.trim());
1135
+ for (const n of names) {
1136
+ if (!isConfigName(n)) {
1137
+ throw Object.assign(
1138
+ new Error(`unknown config in --only: "${n}". Valid: ${ALL_CONFIG_NAMES.join(", ")}`),
1139
+ { exitCode: 2 }
1140
+ );
1141
+ }
1142
+ }
1143
+ return names;
1144
+ }
1145
+ async function dryPlanGitignore(cwd) {
1146
+ let existing;
1147
+ try {
1148
+ existing = await readFile7(join7(cwd, ".gitignore"), "utf-8");
1149
+ } catch {
1150
+ return "would create .gitignore";
1151
+ }
1152
+ const merge = mergeGitignore(existing, CANONICAL_GITIGNORE_ENTRIES);
1153
+ if (merge.added.length === 0) return null;
1154
+ return `would update .gitignore (${merge.added.length} canonical entries to add)`;
1123
1155
  }
1124
1156
  async function dryPlan(cwd, which) {
1125
- const targets = which ? templatesByName(which) : ALL_TEMPLATES;
1157
+ const includeGitignore = which ? which.includes("gitignore") : true;
1158
+ const templateTargets = which ? templatesByName(which.filter((c) => c !== "gitignore")) : ALL_TEMPLATES;
1126
1159
  const lines = [];
1127
- for (const t of targets) {
1160
+ for (const t of templateTargets) {
1128
1161
  let existing = "";
1129
1162
  try {
1130
1163
  existing = await readFile7(join7(cwd, t.path), "utf-8");
@@ -1132,6 +1165,10 @@ async function dryPlan(cwd, which) {
1132
1165
  }
1133
1166
  if (existing !== t.contents) lines.push(`would update ${t.path} (config: ${t.config})`);
1134
1167
  }
1168
+ if (includeGitignore) {
1169
+ const gi = await dryPlanGitignore(cwd);
1170
+ if (gi) lines.push(gi);
1171
+ }
1135
1172
  return lines.length === 0 ? "no changes needed" : lines.join("\n");
1136
1173
  }
1137
1174
  function formatResult(r) {
@@ -1170,9 +1207,19 @@ async function runSyncConfigsCommand(site, opts) {
1170
1207
  import { resolve as resolve4 } from "path";
1171
1208
 
1172
1209
  // src/recipes/bump-deps.ts
1210
+ import { stat as stat2 } from "fs/promises";
1211
+ import { join as join8 } from "path";
1173
1212
  function siteLabel7(site) {
1174
1213
  return site.name ?? site.path;
1175
1214
  }
1215
+ async function exists(path) {
1216
+ try {
1217
+ await stat2(path);
1218
+ return true;
1219
+ } catch {
1220
+ return false;
1221
+ }
1222
+ }
1176
1223
  function outdatedFlagsForGroup(group) {
1177
1224
  if (group === "major") return ["--latest"];
1178
1225
  if (group === "minor") return [];
@@ -1186,6 +1233,24 @@ async function bumpDeps(site, opts = {}) {
1186
1233
  const label = siteLabel7(site);
1187
1234
  const group = opts.group ?? "minor";
1188
1235
  const spawn2 = opts.spawn ?? defaultSpawn;
1236
+ const hasPnpmLock = await exists(join8(site.path, "pnpm-lock.yaml"));
1237
+ if (!hasPnpmLock) {
1238
+ const hasNpmLock = await exists(join8(site.path, "package-lock.json"));
1239
+ const hasYarnLock = await exists(join8(site.path, "yarn.lock"));
1240
+ if (hasNpmLock || hasYarnLock) {
1241
+ const competing = hasNpmLock ? "package-lock.json" : "yarn.lock";
1242
+ return {
1243
+ recipe: "bump-deps",
1244
+ site: label,
1245
+ status: "failed",
1246
+ commits: [],
1247
+ notes: `site has ${competing} but no pnpm-lock.yaml \u2014 run convert-to-pnpm first`
1248
+ };
1249
+ }
1250
+ }
1251
+ if (!await isWorkingTreeClean(site.path)) {
1252
+ throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
1253
+ }
1189
1254
  await spawn2("pnpm", ["install"], { cwd: site.path, streaming: true });
1190
1255
  const outdated = await spawn2("pnpm", ["outdated", "--json", ...outdatedFlagsForGroup(group)], {
1191
1256
  cwd: site.path
@@ -1206,9 +1271,6 @@ async function bumpDeps(site, opts = {}) {
1206
1271
  notes: `pnpm outdated reported nothing for group=${group}`
1207
1272
  };
1208
1273
  }
1209
- if (!await isWorkingTreeClean(site.path)) {
1210
- throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
1211
- }
1212
1274
  const branch = branchName("bump-deps");
1213
1275
  await createBranch(site.path, branch);
1214
1276
  await spawn2("pnpm", ["up", ...upFlagsForGroup(group)], {
@@ -1262,7 +1324,7 @@ async function runBumpDepsCommand(site, opts) {
1262
1324
  import { resolve as resolve5 } from "path";
1263
1325
 
1264
1326
  // src/recipes/svelte-5/index.ts
1265
- import { join as join13 } from "path";
1327
+ import { join as join14 } from "path";
1266
1328
 
1267
1329
  // src/util/pkg.ts
1268
1330
  import { readFile as readFile8, writeFile as writeFile4 } from "fs/promises";
@@ -1270,8 +1332,18 @@ async function readPackageJson(path) {
1270
1332
  const raw = await readFile8(path, "utf-8");
1271
1333
  return JSON.parse(raw);
1272
1334
  }
1335
+ function detectIndentFromContent(raw) {
1336
+ const match = raw.match(/\n([ \t]+)"/);
1337
+ return match ? match[1] ?? " " : " ";
1338
+ }
1273
1339
  async function writePackageJson(path, pkg) {
1274
- const content = JSON.stringify(pkg, null, 2) + "\n";
1340
+ let indent = " ";
1341
+ try {
1342
+ const existing = await readFile8(path, "utf-8");
1343
+ indent = detectIndentFromContent(existing);
1344
+ } catch {
1345
+ }
1346
+ const content = JSON.stringify(pkg, null, indent) + "\n";
1275
1347
  await writeFile4(path, content, "utf-8");
1276
1348
  }
1277
1349
  function bumpDep(pkg, name, version2, opts = {}) {
@@ -1301,7 +1373,7 @@ function bumpDep(pkg, name, version2, opts = {}) {
1301
1373
  }
1302
1374
 
1303
1375
  // src/recipes/svelte-5/step-bump-versions.ts
1304
- import { join as join8 } from "path";
1376
+ import { join as join9 } from "path";
1305
1377
  var SVELTE_5_VERSIONS = {
1306
1378
  svelte: "^5.55.5",
1307
1379
  "@sveltejs/kit": "^2.59.0",
@@ -1314,7 +1386,7 @@ var SVELTE_5_VERSIONS = {
1314
1386
  "typescript-svelte-plugin": "^0.3.52"
1315
1387
  };
1316
1388
  async function bumpToSvelte5Versions(cwd) {
1317
- const pkgPath = join8(cwd, "package.json");
1389
+ const pkgPath = join9(cwd, "package.json");
1318
1390
  const pkg = await readPackageJson(pkgPath);
1319
1391
  let next = pkg;
1320
1392
  for (const [name, version2] of Object.entries(SVELTE_5_VERSIONS)) {
@@ -1327,7 +1399,7 @@ async function bumpToSvelte5Versions(cwd) {
1327
1399
 
1328
1400
  // src/recipes/svelte-5/step-svelte-config.ts
1329
1401
  import { readFile as readFile9, writeFile as writeFile5 } from "fs/promises";
1330
- import { join as join9 } from "path";
1402
+ import { join as join10 } from "path";
1331
1403
  var VITE_PLUGIN_PKG = "@sveltejs/vite-plugin-svelte";
1332
1404
  var IMPORT_FROM_VITE_PLUGIN = new RegExp(
1333
1405
  String.raw`^import\s+\{\s*([^}]+?)\s*\}\s+from\s+["']` + VITE_PLUGIN_PKG.replace(/[/]/g, "\\/") + String.raw`["'];?[ \t]*\n`,
@@ -1368,7 +1440,7 @@ function dropPreprocessKey(source) {
1368
1440
  return source.slice(0, m.index) + source.slice(tailIdx).replace(new RegExp(`^${indent}\\n`), "");
1369
1441
  }
1370
1442
  async function migrateSvelteConfig(cwd) {
1371
- const path = join9(cwd, "svelte.config.js");
1443
+ const path = join10(cwd, "svelte.config.js");
1372
1444
  let src;
1373
1445
  try {
1374
1446
  src = await readFile9(path, "utf-8");
@@ -1405,9 +1477,9 @@ async function runSvelteMigrate(cwd, spawn2 = defaultSpawn) {
1405
1477
  }
1406
1478
 
1407
1479
  // src/recipes/svelte-5/step-tailwind-upgrade.ts
1408
- import { join as join10 } from "path";
1480
+ import { join as join11 } from "path";
1409
1481
  async function upgradeTailwind(cwd, spawn2 = defaultSpawn) {
1410
- const pkg = await readPackageJson(join10(cwd, "package.json"));
1482
+ const pkg = await readPackageJson(join11(cwd, "package.json"));
1411
1483
  const tailwindVersion = pkg.devDependencies?.tailwindcss ?? pkg.dependencies?.tailwindcss;
1412
1484
  if (!tailwindVersion) return { ran: false, reason: "tailwindcss not installed" };
1413
1485
  if (/^\^?4\./.test(tailwindVersion)) return { ran: false, reason: "already on tailwind 4.x" };
@@ -1429,7 +1501,7 @@ async function upgradeTailwind(cwd, spawn2 = defaultSpawn) {
1429
1501
 
1430
1502
  // src/recipes/svelte-5/step-gotchas.ts
1431
1503
  import { readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
1432
- import { join as join11 } from "path";
1504
+ import { join as join12 } from "path";
1433
1505
  import { glob as glob2 } from "tinyglobby";
1434
1506
 
1435
1507
  // src/recipes/svelte-5/codemods/on-event-to-handler.ts
@@ -1744,7 +1816,7 @@ async function planGotchaCodemods(cwd) {
1744
1816
  const changes = [];
1745
1817
  const relPaths = await glob2(SVELTE_GLOBS, { cwd, ignore: IGNORE2, absolute: false });
1746
1818
  for (const rel of relPaths) {
1747
- const path = join11(cwd, rel);
1819
+ const path = join12(cwd, rel);
1748
1820
  const before = await readFile10(path, "utf-8");
1749
1821
  const after = CODEMODS.reduce((s, fn) => fn(s), before);
1750
1822
  if (after !== before) changes.push({ rel, after });
@@ -1754,7 +1826,7 @@ async function planGotchaCodemods(cwd) {
1754
1826
  async function applyGotchaCodemods(cwd) {
1755
1827
  const changes = await planGotchaCodemods(cwd);
1756
1828
  for (const c of changes) {
1757
- await writeFile6(join11(cwd, c.rel), c.after, "utf-8");
1829
+ await writeFile6(join12(cwd, c.rel), c.after, "utf-8");
1758
1830
  }
1759
1831
  return { filesChanged: changes.length };
1760
1832
  }
@@ -1778,7 +1850,7 @@ async function verifyMigration(cwd, spawn2 = defaultSpawn) {
1778
1850
 
1779
1851
  // src/recipes/svelte-5/step-summary.ts
1780
1852
  import { writeFile as writeFile7 } from "fs/promises";
1781
- import { join as join12 } from "path";
1853
+ import { join as join13 } from "path";
1782
1854
  async function writeMigrationSummary(input) {
1783
1855
  const lines = [
1784
1856
  `# Svelte 4 \u2192 5 migration summary`,
@@ -1795,7 +1867,7 @@ async function writeMigrationSummary(input) {
1795
1867
  `- Verify Playwright a11y tests still pass.`
1796
1868
  ];
1797
1869
  const content = lines.join("\n") + "\n";
1798
- const path = join12(input.cwd, "MIGRATION_SVELTE_5.md");
1870
+ const path = join13(input.cwd, "MIGRATION_SVELTE_5.md");
1799
1871
  await writeFile7(path, content, "utf-8");
1800
1872
  return path;
1801
1873
  }
@@ -1806,7 +1878,7 @@ function siteLabel8(site) {
1806
1878
  }
1807
1879
  async function alreadyOnSvelte5(cwd) {
1808
1880
  try {
1809
- const pkg = await readPackageJson(join13(cwd, "package.json"));
1881
+ const pkg = await readPackageJson(join14(cwd, "package.json"));
1810
1882
  const v = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte;
1811
1883
  return !!v && /^\^?5\./.test(v);
1812
1884
  } catch {
@@ -1923,8 +1995,8 @@ async function runUpgradeCommand(upgradeName, site, opts = {}) {
1923
1995
  import { resolve as resolve6 } from "path";
1924
1996
 
1925
1997
  // src/recipes/convert-to-pnpm.ts
1926
- import { rm as rm3, stat as stat2 } from "fs/promises";
1927
- import { join as join14 } from "path";
1998
+ import { rm as rm3, stat as stat3 } from "fs/promises";
1999
+ import { join as join15 } from "path";
1928
2000
 
1929
2001
  // src/recipes/convert-to-pnpm/script-rewrites.ts
1930
2002
  function rewriteScriptForPnpm(script) {
@@ -1946,9 +2018,9 @@ function rewriteScriptsForPnpm(scripts) {
1946
2018
 
1947
2019
  // src/recipes/convert-to-pnpm.ts
1948
2020
  var DEFAULT_PNPM_VERSION = "10.33.1";
1949
- async function exists(path) {
2021
+ async function exists2(path) {
1950
2022
  try {
1951
- await stat2(path);
2023
+ await stat3(path);
1952
2024
  return true;
1953
2025
  } catch {
1954
2026
  return false;
@@ -1961,10 +2033,10 @@ async function convertToPnpm(site, opts = {}) {
1961
2033
  const label = siteLabel9(site);
1962
2034
  const spawn2 = opts.spawn ?? defaultSpawn;
1963
2035
  const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
1964
- const pnpmLockPath = join14(site.path, "pnpm-lock.yaml");
1965
- const npmLockPath = join14(site.path, "package-lock.json");
1966
- const yarnLockPath = join14(site.path, "yarn.lock");
1967
- if (await exists(pnpmLockPath)) {
2036
+ const pnpmLockPath = join15(site.path, "pnpm-lock.yaml");
2037
+ const npmLockPath = join15(site.path, "package-lock.json");
2038
+ const yarnLockPath = join15(site.path, "yarn.lock");
2039
+ if (await exists2(pnpmLockPath)) {
1968
2040
  return {
1969
2041
  recipe: "convert-to-pnpm",
1970
2042
  site: label,
@@ -1973,8 +2045,8 @@ async function convertToPnpm(site, opts = {}) {
1973
2045
  notes: "site already has pnpm-lock.yaml"
1974
2046
  };
1975
2047
  }
1976
- const hasNpmLock = await exists(npmLockPath);
1977
- const hasYarnLock = await exists(yarnLockPath);
2048
+ const hasNpmLock = await exists2(npmLockPath);
2049
+ const hasYarnLock = await exists2(yarnLockPath);
1978
2050
  if (!hasNpmLock && !hasYarnLock) {
1979
2051
  return {
1980
2052
  recipe: "convert-to-pnpm",
@@ -1995,7 +2067,7 @@ async function convertToPnpm(site, opts = {}) {
1995
2067
  const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
1996
2068
  const lockSha = await commit(site.path, `chore(pnpm): remove ${sourceLock}`);
1997
2069
  if (lockSha) shas.push(lockSha);
1998
- const pkgPath = join14(site.path, "package.json");
2070
+ const pkgPath = join15(site.path, "package.json");
1999
2071
  const pkg = await readPackageJson(pkgPath);
2000
2072
  const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
2001
2073
  if (pkg.scripts && typeof pkg.scripts === "object") {
@@ -2009,7 +2081,7 @@ async function convertToPnpm(site, opts = {}) {
2009
2081
  await writePackageJson(pkgPath, next);
2010
2082
  const pkgSha = await commit(site.path, "chore(pnpm): pin packageManager + rewrite npm scripts");
2011
2083
  if (pkgSha) shas.push(pkgSha);
2012
- await rm3(join14(site.path, "node_modules"), { recursive: true, force: true });
2084
+ await rm3(join15(site.path, "node_modules"), { recursive: true, force: true });
2013
2085
  const installResult = await spawn2("pnpm", ["install"], {
2014
2086
  cwd: site.path,
2015
2087
  streaming: true
@@ -2063,17 +2135,17 @@ async function runConvertToPnpmCommand(site, opts) {
2063
2135
  import { resolve as resolve7 } from "path";
2064
2136
 
2065
2137
  // src/recipes/onboard.ts
2066
- import { stat as stat3 } from "fs/promises";
2067
- import { join as join16 } from "path";
2138
+ import { stat as stat4 } from "fs/promises";
2139
+ import { join as join17 } from "path";
2068
2140
 
2069
2141
  // src/util/self-version.ts
2070
2142
  import { readFileSync } from "fs";
2071
2143
  import { fileURLToPath } from "url";
2072
- import { dirname, join as join15 } from "path";
2144
+ import { dirname, join as join16 } from "path";
2073
2145
  function selfPackageVersion(callerImportMetaUrl) {
2074
2146
  try {
2075
2147
  const here2 = dirname(fileURLToPath(callerImportMetaUrl));
2076
- const raw = readFileSync(join15(here2, "..", "..", "package.json"), "utf-8");
2148
+ const raw = readFileSync(join16(here2, "..", "..", "package.json"), "utf-8");
2077
2149
  const pkg = JSON.parse(raw);
2078
2150
  return pkg.version ?? "0.0.0";
2079
2151
  } catch {
@@ -2086,16 +2158,27 @@ function selfCaretRange(callerImportMetaUrl) {
2086
2158
 
2087
2159
  // src/recipes/onboard.ts
2088
2160
  var PACKAGE_NAME = "@reddoorla/maintenance";
2089
- var AUDIT_DEPS = {
2090
- lighthouse: [{ name: "@lhci/cli", version: "^0.15.1" }],
2091
- a11y: [
2092
- { name: "@playwright/test", version: "^1.59.1" },
2093
- { name: "@axe-core/playwright", version: "^4.11.3" }
2094
- ]
2161
+ var AUDIT_DEP_NAMES = {
2162
+ lighthouse: ["@lhci/cli"],
2163
+ a11y: ["@playwright/test", "@axe-core/playwright"]
2095
2164
  };
2096
- async function exists2(path) {
2165
+ var AUDIT_DEPS = Object.fromEntries(
2166
+ Object.entries(AUDIT_DEP_NAMES).map(([audit, names]) => [
2167
+ audit,
2168
+ names.map((name) => {
2169
+ const version2 = baselineVersions[name];
2170
+ if (!version2) {
2171
+ throw new Error(
2172
+ `baseline-versions is missing audit dep "${name}" \u2014 add it to src/configs/baseline-versions.ts`
2173
+ );
2174
+ }
2175
+ return { name, version: version2 };
2176
+ })
2177
+ ])
2178
+ );
2179
+ async function exists3(path) {
2097
2180
  try {
2098
- await stat3(path);
2181
+ await stat4(path);
2099
2182
  return true;
2100
2183
  } catch {
2101
2184
  return false;
@@ -2112,7 +2195,7 @@ async function onboard(site, opts = {}) {
2112
2195
  const spawn2 = opts.spawn ?? defaultSpawn;
2113
2196
  const audits = opts.audits ?? ["lighthouse", "a11y"];
2114
2197
  const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);
2115
- if (!await exists2(join16(site.path, "pnpm-lock.yaml"))) {
2198
+ if (!await exists3(join17(site.path, "pnpm-lock.yaml"))) {
2116
2199
  return {
2117
2200
  recipe: "onboard",
2118
2201
  site: label,
@@ -2121,7 +2204,7 @@ async function onboard(site, opts = {}) {
2121
2204
  notes: "no pnpm-lock.yaml at site root \u2014 run convert-to-pnpm first"
2122
2205
  };
2123
2206
  }
2124
- const pkgPath = join16(site.path, "package.json");
2207
+ const pkgPath = join17(site.path, "package.json");
2125
2208
  const pkg = await readPackageJson(pkgPath);
2126
2209
  const toAdd = [];
2127
2210
  if (!isDeclared(pkg, PACKAGE_NAME)) {
@@ -2225,7 +2308,7 @@ import { resolve as resolve8 } from "path";
2225
2308
 
2226
2309
  // src/recipes/svelte-codemods.ts
2227
2310
  import { writeFile as writeFile8 } from "fs/promises";
2228
- import { join as join17 } from "path";
2311
+ import { join as join18 } from "path";
2229
2312
  function siteLabel11(site) {
2230
2313
  return site.name ?? site.path;
2231
2314
  }
@@ -2247,7 +2330,7 @@ async function svelteCodemods(site) {
2247
2330
  const branch = branchName("svelte-codemods");
2248
2331
  await createBranch(site.path, branch);
2249
2332
  for (const c of changes) {
2250
- await writeFile8(join17(site.path, c.rel), c.after, "utf-8");
2333
+ await writeFile8(join18(site.path, c.rel), c.after, "utf-8");
2251
2334
  }
2252
2335
  const sha = await commit(
2253
2336
  site.path,
@@ -2289,10 +2372,10 @@ async function runSvelteCodemodsCommand(site, opts) {
2289
2372
 
2290
2373
  // src/cli/version.ts
2291
2374
  import { readFileSync as readFileSync2 } from "fs";
2292
- import { join as join18 } from "path";
2375
+ import { join as join19 } from "path";
2293
2376
  function resolvePackageVersion(fromDir) {
2294
2377
  try {
2295
- const raw = readFileSync2(join18(fromDir, "..", "..", "package.json"), "utf-8");
2378
+ const raw = readFileSync2(join19(fromDir, "..", "..", "package.json"), "utf-8");
2296
2379
  const pkg = JSON.parse(raw);
2297
2380
  return pkg.version ?? "unknown";
2298
2381
  } catch {