@reddoorla/maintenance 0.8.0 → 0.10.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/README.md +28 -10
- package/dist/cli/bin.js +298 -85
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +65 -25
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/index.d.ts +60 -3
- package/dist/index.js +229 -74
- package/dist/index.js.map +1 -1
- package/dist/recipes/sync-configs.d.ts +1 -1
- package/dist/{sync-configs-pMPW4wTq.d.ts → sync-configs-Dn6KhjrW.d.ts} +1 -1
- package/package.json +1 -1
package/dist/cli/bin.js
CHANGED
|
@@ -138,8 +138,8 @@ __export(lighthouse_airtable_exports, {
|
|
|
138
138
|
lighthouseScoresFromResult: () => lighthouseScoresFromResult,
|
|
139
139
|
resolveSlugFromCwd: () => resolveSlugFromCwd
|
|
140
140
|
});
|
|
141
|
-
import { readFile as
|
|
142
|
-
import { join as
|
|
141
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
142
|
+
import { join as join7 } from "path";
|
|
143
143
|
function lighthouseScoresFromResult(result) {
|
|
144
144
|
if (result.audit !== "lighthouse") {
|
|
145
145
|
throw new Error(`Expected a 'lighthouse' AuditResult, got '${result.audit}'`);
|
|
@@ -156,8 +156,8 @@ function lighthouseScoresFromResult(result) {
|
|
|
156
156
|
}
|
|
157
157
|
async function resolveSlugFromCwd(cwd) {
|
|
158
158
|
try {
|
|
159
|
-
const pkgPath =
|
|
160
|
-
const raw = await
|
|
159
|
+
const pkgPath = join7(cwd, "package.json");
|
|
160
|
+
const raw = await readFile7(pkgPath, "utf-8");
|
|
161
161
|
const pkg = JSON.parse(raw);
|
|
162
162
|
if (!pkg.name) throw new Error("package.json has no 'name' field");
|
|
163
163
|
return siteSlug(pkg.name);
|
|
@@ -283,13 +283,13 @@ var init_reports = __esm({
|
|
|
283
283
|
});
|
|
284
284
|
|
|
285
285
|
// src/reports/maintenance-email/assets/index.ts
|
|
286
|
-
import { readFile as
|
|
287
|
-
import { dirname as dirname2, join as
|
|
286
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
287
|
+
import { dirname as dirname2, join as join21 } from "path";
|
|
288
288
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
289
289
|
async function loadBundledImages() {
|
|
290
290
|
const [check, blurred] = await Promise.all([
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
readFile13(join21(here, "check.png")),
|
|
292
|
+
readFile13(join21(here, "blurredTests.jpg"))
|
|
293
293
|
]);
|
|
294
294
|
return {
|
|
295
295
|
check: {
|
|
@@ -735,7 +735,7 @@ var init_orchestrate = __esm({
|
|
|
735
735
|
});
|
|
736
736
|
|
|
737
737
|
// src/cli/bin.ts
|
|
738
|
-
import { dirname as
|
|
738
|
+
import { dirname as dirname5 } from "path";
|
|
739
739
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
740
740
|
import { cac } from "cac";
|
|
741
741
|
|
|
@@ -744,7 +744,7 @@ import { resolve as resolve2 } from "path";
|
|
|
744
744
|
|
|
745
745
|
// src/audits/util/spawn.ts
|
|
746
746
|
import { spawn } from "child_process";
|
|
747
|
-
var defaultSpawn = (cmd, args, opts = {}) => new Promise((
|
|
747
|
+
var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve10, reject) => {
|
|
748
748
|
const streaming = opts.streaming === true;
|
|
749
749
|
const child = spawn(cmd, [...args], {
|
|
750
750
|
cwd: opts.cwd,
|
|
@@ -767,7 +767,7 @@ var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
|
|
|
767
767
|
});
|
|
768
768
|
child.on("close", (code) => {
|
|
769
769
|
if (timer) clearTimeout(timer);
|
|
770
|
-
|
|
770
|
+
resolve10({ code: code ?? -1, stdout, stderr });
|
|
771
771
|
});
|
|
772
772
|
});
|
|
773
773
|
|
|
@@ -1079,9 +1079,9 @@ async function securityAudit(ctx) {
|
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
1081
|
// src/audits/lighthouse.ts
|
|
1082
|
-
import { readFile as
|
|
1082
|
+
import { readFile as readFile4, writeFile, mkdtemp, rm } from "fs/promises";
|
|
1083
1083
|
import { tmpdir } from "os";
|
|
1084
|
-
import { join as
|
|
1084
|
+
import { join as join4 } from "path";
|
|
1085
1085
|
|
|
1086
1086
|
// src/configs/lighthouse.ts
|
|
1087
1087
|
var lighthouseConfig = {
|
|
@@ -1114,10 +1114,37 @@ var lighthouseConfig = {
|
|
|
1114
1114
|
}
|
|
1115
1115
|
};
|
|
1116
1116
|
|
|
1117
|
+
// src/audits/util/site-config.ts
|
|
1118
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1119
|
+
import { join as join3 } from "path";
|
|
1120
|
+
async function readSiteConfig(sitePath) {
|
|
1121
|
+
let raw;
|
|
1122
|
+
try {
|
|
1123
|
+
raw = await readFile3(join3(sitePath, "package.json"), "utf-8");
|
|
1124
|
+
} catch {
|
|
1125
|
+
return {};
|
|
1126
|
+
}
|
|
1127
|
+
let pkg;
|
|
1128
|
+
try {
|
|
1129
|
+
pkg = JSON.parse(raw);
|
|
1130
|
+
} catch {
|
|
1131
|
+
return {};
|
|
1132
|
+
}
|
|
1133
|
+
if (!pkg || typeof pkg !== "object") return {};
|
|
1134
|
+
const cfg = pkg.reddoor;
|
|
1135
|
+
if (!cfg || typeof cfg !== "object") return {};
|
|
1136
|
+
const out = {};
|
|
1137
|
+
const url = cfg.lighthouseUrl;
|
|
1138
|
+
if (typeof url === "string" && url.length > 0) {
|
|
1139
|
+
out.lighthouseUrl = url;
|
|
1140
|
+
}
|
|
1141
|
+
return out;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1117
1144
|
// src/audits/lighthouse.ts
|
|
1118
1145
|
async function readJsonMaybe(path) {
|
|
1119
1146
|
try {
|
|
1120
|
-
const raw = await
|
|
1147
|
+
const raw = await readFile4(path, "utf-8");
|
|
1121
1148
|
return JSON.parse(raw);
|
|
1122
1149
|
} catch {
|
|
1123
1150
|
return null;
|
|
@@ -1153,15 +1180,28 @@ async function lighthouseAudit(ctx) {
|
|
|
1153
1180
|
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
1154
1181
|
const site = ctx.site;
|
|
1155
1182
|
const label = siteLabel(site);
|
|
1156
|
-
const
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1183
|
+
const siteCfg = await readSiteConfig(site.path);
|
|
1184
|
+
const resolvedConfig = siteCfg.lighthouseUrl ? {
|
|
1185
|
+
...lighthouseConfig,
|
|
1186
|
+
ci: {
|
|
1187
|
+
...lighthouseConfig.ci,
|
|
1188
|
+
collect: { ...lighthouseConfig.ci.collect, url: [siteCfg.lighthouseUrl] }
|
|
1189
|
+
}
|
|
1190
|
+
} : lighthouseConfig;
|
|
1191
|
+
const configDir = await mkdtemp(join4(tmpdir(), "reddoor-lhci-"));
|
|
1192
|
+
const configPath = join4(configDir, "lighthouserc.json");
|
|
1193
|
+
await writeFile(configPath, JSON.stringify(resolvedConfig), "utf-8");
|
|
1194
|
+
const resultsDir = join4(site.path, ".lighthouseci");
|
|
1160
1195
|
await rm(resultsDir, { recursive: true, force: true });
|
|
1161
1196
|
let raw;
|
|
1162
1197
|
try {
|
|
1163
1198
|
raw = await spawn2("npx", ["--yes", "@lhci/cli", "autorun", `--config=${configPath}`], {
|
|
1164
|
-
cwd: site.path
|
|
1199
|
+
cwd: site.path,
|
|
1200
|
+
// lhci autorun boots the site's dev server, downloads Chrome on first
|
|
1201
|
+
// use, and runs the audit — easily 2–3 min on a cold tree. The shared
|
|
1202
|
+
// 30 s default in runAudits is fine for deps/lint/security but starves
|
|
1203
|
+
// lhci.
|
|
1204
|
+
timeoutMs: 5 * 6e4
|
|
1165
1205
|
});
|
|
1166
1206
|
} catch (err) {
|
|
1167
1207
|
await rm(configDir, { recursive: true, force: true });
|
|
@@ -1177,7 +1217,7 @@ async function lighthouseAudit(ctx) {
|
|
|
1177
1217
|
throw err;
|
|
1178
1218
|
}
|
|
1179
1219
|
await rm(configDir, { recursive: true, force: true });
|
|
1180
|
-
const manifest = await readJsonMaybe(
|
|
1220
|
+
const manifest = await readJsonMaybe(join4(resultsDir, "manifest.json"));
|
|
1181
1221
|
if (!manifest || manifest.length === 0) {
|
|
1182
1222
|
return {
|
|
1183
1223
|
audit: "lighthouse",
|
|
@@ -1186,7 +1226,7 @@ async function lighthouseAudit(ctx) {
|
|
|
1186
1226
|
summary: `lighthouse: no manifest written (exit ${raw.code})${raw.stderr ? ` \u2014 ${raw.stderr.slice(0, 200)}` : ""}`
|
|
1187
1227
|
};
|
|
1188
1228
|
}
|
|
1189
|
-
const assertionResults = await readJsonMaybe(
|
|
1229
|
+
const assertionResults = await readJsonMaybe(join4(resultsDir, "assertion-results.json")) ?? [];
|
|
1190
1230
|
const failed = assertionResults.filter((a) => !a.passed);
|
|
1191
1231
|
const assertions = failed.map((a) => ({
|
|
1192
1232
|
category: categoryFromAssertion(a),
|
|
@@ -1212,9 +1252,9 @@ async function lighthouseAudit(ctx) {
|
|
|
1212
1252
|
}
|
|
1213
1253
|
|
|
1214
1254
|
// src/audits/a11y.ts
|
|
1215
|
-
import { readFile as
|
|
1255
|
+
import { readFile as readFile5, writeFile as writeFile2, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
1216
1256
|
import { tmpdir as tmpdir2 } from "os";
|
|
1217
|
-
import { join as
|
|
1257
|
+
import { join as join5 } from "path";
|
|
1218
1258
|
|
|
1219
1259
|
// src/configs/playwright-a11y.ts
|
|
1220
1260
|
import { defineConfig, devices } from "@playwright/test";
|
|
@@ -1252,7 +1292,7 @@ var playwrightA11yConfig = defineConfig({
|
|
|
1252
1292
|
var RESULTS_REL = ".reddoor-a11y/results.json";
|
|
1253
1293
|
async function readJsonMaybe2(path) {
|
|
1254
1294
|
try {
|
|
1255
|
-
const raw = await
|
|
1295
|
+
const raw = await readFile5(path, "utf-8");
|
|
1256
1296
|
return JSON.parse(raw);
|
|
1257
1297
|
} catch {
|
|
1258
1298
|
return null;
|
|
@@ -1308,11 +1348,11 @@ async function a11yAudit(ctx) {
|
|
|
1308
1348
|
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
1309
1349
|
const site = ctx.site;
|
|
1310
1350
|
const label = siteLabel(site);
|
|
1311
|
-
const specDir = await mkdtemp2(
|
|
1312
|
-
const specPath =
|
|
1351
|
+
const specDir = await mkdtemp2(join5(tmpdir2(), "reddoor-a11y-spec-"));
|
|
1352
|
+
const specPath = join5(specDir, "a11y.spec.ts");
|
|
1313
1353
|
await writeFile2(specPath, buildSpec(), "utf-8");
|
|
1314
|
-
const resultsPath =
|
|
1315
|
-
await rm2(
|
|
1354
|
+
const resultsPath = join5(site.path, RESULTS_REL);
|
|
1355
|
+
await rm2(join5(site.path, ".reddoor-a11y"), { recursive: true, force: true });
|
|
1316
1356
|
let raw;
|
|
1317
1357
|
try {
|
|
1318
1358
|
raw = await spawn2("npx", ["--yes", "playwright", "test", "--reporter=line", specPath], {
|
|
@@ -1405,7 +1445,7 @@ function localPath(path, opts = {}) {
|
|
|
1405
1445
|
}
|
|
1406
1446
|
|
|
1407
1447
|
// src/inventory/json.ts
|
|
1408
|
-
import { readFile as
|
|
1448
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
1409
1449
|
import { isAbsolute } from "path";
|
|
1410
1450
|
function validate(raw) {
|
|
1411
1451
|
if (!Array.isArray(raw)) {
|
|
@@ -1435,7 +1475,7 @@ function validate(raw) {
|
|
|
1435
1475
|
}
|
|
1436
1476
|
function fromJsonFile(path) {
|
|
1437
1477
|
return async () => {
|
|
1438
|
-
const raw = JSON.parse(await
|
|
1478
|
+
const raw = JSON.parse(await readFile6(path, "utf-8"));
|
|
1439
1479
|
return validate(raw);
|
|
1440
1480
|
};
|
|
1441
1481
|
}
|
|
@@ -1481,7 +1521,7 @@ async function resolveSites(input) {
|
|
|
1481
1521
|
|
|
1482
1522
|
// src/cli/fleet/clone-if-needed.ts
|
|
1483
1523
|
import { stat, readdir, mkdir } from "fs/promises";
|
|
1484
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1524
|
+
import { isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
1485
1525
|
function deriveNameFromRepoUrl(repoUrl) {
|
|
1486
1526
|
const slash = repoUrl.split("/").pop() ?? repoUrl;
|
|
1487
1527
|
return slash.replace(/\.git$/, "");
|
|
@@ -1522,7 +1562,7 @@ async function cloneIfNeeded(site, opts) {
|
|
|
1522
1562
|
const name = site.name ?? deriveNameFromRepoUrl(site.repoUrl);
|
|
1523
1563
|
assertSafeName(name);
|
|
1524
1564
|
assertSafeRepoUrl(site.repoUrl);
|
|
1525
|
-
const target =
|
|
1565
|
+
const target = join6(opts.workdir, name);
|
|
1526
1566
|
await mkdir(opts.workdir, { recursive: true });
|
|
1527
1567
|
if (await isNonEmptyDir(target)) {
|
|
1528
1568
|
return { ...site, name, path: target };
|
|
@@ -1609,12 +1649,12 @@ async function runAuditCommand(site, opts) {
|
|
|
1609
1649
|
}
|
|
1610
1650
|
|
|
1611
1651
|
// src/cli/commands/sync-configs.ts
|
|
1612
|
-
import { readFile as
|
|
1613
|
-
import { join as
|
|
1652
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
1653
|
+
import { join as join9, resolve as resolve3 } from "path";
|
|
1614
1654
|
|
|
1615
1655
|
// src/recipes/sync-configs.ts
|
|
1616
|
-
import { readFile as
|
|
1617
|
-
import { join as
|
|
1656
|
+
import { readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
|
|
1657
|
+
import { join as join8 } from "path";
|
|
1618
1658
|
|
|
1619
1659
|
// src/recipes/sync-configs/templates.ts
|
|
1620
1660
|
var eslint = {
|
|
@@ -1878,7 +1918,7 @@ function isConfigName(value) {
|
|
|
1878
1918
|
}
|
|
1879
1919
|
async function readMaybe(path) {
|
|
1880
1920
|
try {
|
|
1881
|
-
return await
|
|
1921
|
+
return await readFile8(path, "utf-8");
|
|
1882
1922
|
} catch {
|
|
1883
1923
|
return null;
|
|
1884
1924
|
}
|
|
@@ -1886,13 +1926,13 @@ async function readMaybe(path) {
|
|
|
1886
1926
|
async function planTemplateDiffs(cwd, templates) {
|
|
1887
1927
|
const diffs = [];
|
|
1888
1928
|
for (const t of templates) {
|
|
1889
|
-
const existing = await readMaybe(
|
|
1929
|
+
const existing = await readMaybe(join8(cwd, t.path));
|
|
1890
1930
|
if (existing !== t.contents) diffs.push(t);
|
|
1891
1931
|
}
|
|
1892
1932
|
return diffs;
|
|
1893
1933
|
}
|
|
1894
1934
|
async function planGitignore(cwd) {
|
|
1895
|
-
const existing = await readMaybe(
|
|
1935
|
+
const existing = await readMaybe(join8(cwd, ".gitignore"));
|
|
1896
1936
|
const merge = mergeGitignore(existing, CANONICAL_GITIGNORE_ENTRIES);
|
|
1897
1937
|
const tracked = await listTrackedFiles(cwd);
|
|
1898
1938
|
const toUntrack = findTrackedArtifacts(tracked, CANONICAL_GITIGNORE_ENTRIES);
|
|
@@ -1900,7 +1940,7 @@ async function planGitignore(cwd) {
|
|
|
1900
1940
|
return { kind: "apply", content: merge.content, toUntrack, added: merge.added };
|
|
1901
1941
|
}
|
|
1902
1942
|
async function applyGitignore(cwd, plan) {
|
|
1903
|
-
await writeFile3(
|
|
1943
|
+
await writeFile3(join8(cwd, ".gitignore"), plan.content, "utf-8");
|
|
1904
1944
|
if (plan.toUntrack.length > 0) {
|
|
1905
1945
|
await removeFromIndex(cwd, plan.toUntrack);
|
|
1906
1946
|
}
|
|
@@ -1923,7 +1963,7 @@ async function syncConfigs(site, opts = {}) {
|
|
|
1923
1963
|
},
|
|
1924
1964
|
apply: async ({ templateDiffs, gitignorePlan }, { commit: commit2 }) => {
|
|
1925
1965
|
for (const t of templateDiffs) {
|
|
1926
|
-
await writeFile3(
|
|
1966
|
+
await writeFile3(join8(site.path, t.path), t.contents, "utf-8");
|
|
1927
1967
|
await commit2(`chore: sync ${t.config} config from @reddoorla/maintenance`);
|
|
1928
1968
|
}
|
|
1929
1969
|
if (gitignorePlan.kind === "apply") {
|
|
@@ -1952,7 +1992,7 @@ function parseOnly2(value) {
|
|
|
1952
1992
|
async function dryPlanGitignore(cwd) {
|
|
1953
1993
|
let existing;
|
|
1954
1994
|
try {
|
|
1955
|
-
existing = await
|
|
1995
|
+
existing = await readFile9(join9(cwd, ".gitignore"), "utf-8");
|
|
1956
1996
|
} catch {
|
|
1957
1997
|
return "would create .gitignore";
|
|
1958
1998
|
}
|
|
@@ -1967,7 +2007,7 @@ async function dryPlan(cwd, which) {
|
|
|
1967
2007
|
for (const t of templateTargets) {
|
|
1968
2008
|
let existing = "";
|
|
1969
2009
|
try {
|
|
1970
|
-
existing = await
|
|
2010
|
+
existing = await readFile9(join9(cwd, t.path), "utf-8");
|
|
1971
2011
|
} catch {
|
|
1972
2012
|
}
|
|
1973
2013
|
if (existing !== t.contents) lines.push(`would update ${t.path} (config: ${t.config})`);
|
|
@@ -2015,7 +2055,7 @@ import { resolve as resolve4 } from "path";
|
|
|
2015
2055
|
|
|
2016
2056
|
// src/recipes/bump-deps.ts
|
|
2017
2057
|
import { stat as stat2 } from "fs/promises";
|
|
2018
|
-
import { join as
|
|
2058
|
+
import { join as join10 } from "path";
|
|
2019
2059
|
async function exists(path) {
|
|
2020
2060
|
try {
|
|
2021
2061
|
await stat2(path);
|
|
@@ -2044,10 +2084,10 @@ async function bumpDeps(site, opts = {}) {
|
|
|
2044
2084
|
// land on top of whatever else was in the tree.
|
|
2045
2085
|
checkTreeFirst: true,
|
|
2046
2086
|
plan: async () => {
|
|
2047
|
-
const hasPnpmLock = await exists(
|
|
2087
|
+
const hasPnpmLock = await exists(join10(site.path, "pnpm-lock.yaml"));
|
|
2048
2088
|
if (!hasPnpmLock) {
|
|
2049
|
-
const hasNpmLock = await exists(
|
|
2050
|
-
const hasYarnLock = await exists(
|
|
2089
|
+
const hasNpmLock = await exists(join10(site.path, "package-lock.json"));
|
|
2090
|
+
const hasYarnLock = await exists(join10(site.path, "yarn.lock"));
|
|
2051
2091
|
if (hasNpmLock || hasYarnLock) {
|
|
2052
2092
|
const competing = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
2053
2093
|
return {
|
|
@@ -2117,12 +2157,12 @@ async function runBumpDepsCommand(site, opts) {
|
|
|
2117
2157
|
import { resolve as resolve5 } from "path";
|
|
2118
2158
|
|
|
2119
2159
|
// src/recipes/svelte-5/index.ts
|
|
2120
|
-
import { join as
|
|
2160
|
+
import { join as join16 } from "path";
|
|
2121
2161
|
|
|
2122
2162
|
// src/util/pkg.ts
|
|
2123
|
-
import { readFile as
|
|
2163
|
+
import { readFile as readFile10, writeFile as writeFile4 } from "fs/promises";
|
|
2124
2164
|
async function readPackageJson(path) {
|
|
2125
|
-
const raw = await
|
|
2165
|
+
const raw = await readFile10(path, "utf-8");
|
|
2126
2166
|
return JSON.parse(raw);
|
|
2127
2167
|
}
|
|
2128
2168
|
function detectIndentFromContent(raw) {
|
|
@@ -2132,7 +2172,7 @@ function detectIndentFromContent(raw) {
|
|
|
2132
2172
|
async function writePackageJson(path, pkg) {
|
|
2133
2173
|
let indent = " ";
|
|
2134
2174
|
try {
|
|
2135
|
-
const existing = await
|
|
2175
|
+
const existing = await readFile10(path, "utf-8");
|
|
2136
2176
|
indent = detectIndentFromContent(existing);
|
|
2137
2177
|
} catch {
|
|
2138
2178
|
}
|
|
@@ -2166,7 +2206,7 @@ function bumpDep(pkg, name, version2, opts = {}) {
|
|
|
2166
2206
|
}
|
|
2167
2207
|
|
|
2168
2208
|
// src/recipes/svelte-5/step-bump-versions.ts
|
|
2169
|
-
import { join as
|
|
2209
|
+
import { join as join11 } from "path";
|
|
2170
2210
|
var SVELTE_5_VERSIONS = {
|
|
2171
2211
|
svelte: "^5.55.5",
|
|
2172
2212
|
"@sveltejs/kit": "^2.59.0",
|
|
@@ -2179,7 +2219,7 @@ var SVELTE_5_VERSIONS = {
|
|
|
2179
2219
|
"typescript-svelte-plugin": "^0.3.52"
|
|
2180
2220
|
};
|
|
2181
2221
|
async function bumpToSvelte5Versions(cwd) {
|
|
2182
|
-
const pkgPath =
|
|
2222
|
+
const pkgPath = join11(cwd, "package.json");
|
|
2183
2223
|
const pkg = await readPackageJson(pkgPath);
|
|
2184
2224
|
let next = pkg;
|
|
2185
2225
|
for (const [name, version2] of Object.entries(SVELTE_5_VERSIONS)) {
|
|
@@ -2191,8 +2231,8 @@ async function bumpToSvelte5Versions(cwd) {
|
|
|
2191
2231
|
}
|
|
2192
2232
|
|
|
2193
2233
|
// src/recipes/svelte-5/step-svelte-config.ts
|
|
2194
|
-
import { readFile as
|
|
2195
|
-
import { join as
|
|
2234
|
+
import { readFile as readFile11, writeFile as writeFile5 } from "fs/promises";
|
|
2235
|
+
import { join as join12 } from "path";
|
|
2196
2236
|
var VITE_PLUGIN_PKG = "@sveltejs/vite-plugin-svelte";
|
|
2197
2237
|
var IMPORT_FROM_VITE_PLUGIN = new RegExp(
|
|
2198
2238
|
String.raw`^import\s+\{\s*([^}]+?)\s*\}\s+from\s+["']` + VITE_PLUGIN_PKG.replace(/[/]/g, "\\/") + String.raw`["'];?[ \t]*\n`,
|
|
@@ -2233,10 +2273,10 @@ function dropPreprocessKey(source) {
|
|
|
2233
2273
|
return source.slice(0, m.index) + source.slice(tailIdx).replace(new RegExp(`^${indent}\\n`), "");
|
|
2234
2274
|
}
|
|
2235
2275
|
async function migrateSvelteConfig(cwd) {
|
|
2236
|
-
const path =
|
|
2276
|
+
const path = join12(cwd, "svelte.config.js");
|
|
2237
2277
|
let src;
|
|
2238
2278
|
try {
|
|
2239
|
-
src = await
|
|
2279
|
+
src = await readFile11(path, "utf-8");
|
|
2240
2280
|
} catch {
|
|
2241
2281
|
return false;
|
|
2242
2282
|
}
|
|
@@ -2270,9 +2310,9 @@ async function runSvelteMigrate(cwd, spawn2 = defaultSpawn) {
|
|
|
2270
2310
|
}
|
|
2271
2311
|
|
|
2272
2312
|
// src/recipes/svelte-5/step-tailwind-upgrade.ts
|
|
2273
|
-
import { join as
|
|
2313
|
+
import { join as join13 } from "path";
|
|
2274
2314
|
async function upgradeTailwind(cwd, spawn2 = defaultSpawn) {
|
|
2275
|
-
const pkg = await readPackageJson(
|
|
2315
|
+
const pkg = await readPackageJson(join13(cwd, "package.json"));
|
|
2276
2316
|
const tailwindVersion = pkg.devDependencies?.tailwindcss ?? pkg.dependencies?.tailwindcss;
|
|
2277
2317
|
if (!tailwindVersion) return { ran: false, reason: "tailwindcss not installed" };
|
|
2278
2318
|
if (/^\^?4\./.test(tailwindVersion)) return { ran: false, reason: "already on tailwind 4.x" };
|
|
@@ -2293,8 +2333,8 @@ async function upgradeTailwind(cwd, spawn2 = defaultSpawn) {
|
|
|
2293
2333
|
}
|
|
2294
2334
|
|
|
2295
2335
|
// src/recipes/svelte-5/step-gotchas.ts
|
|
2296
|
-
import { readFile as
|
|
2297
|
-
import { join as
|
|
2336
|
+
import { readFile as readFile12, writeFile as writeFile6 } from "fs/promises";
|
|
2337
|
+
import { join as join14 } from "path";
|
|
2298
2338
|
import { glob as glob2 } from "tinyglobby";
|
|
2299
2339
|
|
|
2300
2340
|
// src/recipes/svelte-5/codemods/on-event-to-handler.ts
|
|
@@ -2626,8 +2666,8 @@ async function planGotchaCodemods(cwd) {
|
|
|
2626
2666
|
const changes = [];
|
|
2627
2667
|
const relPaths = await glob2(SVELTE_GLOBS, { cwd, ignore: IGNORE2, absolute: false });
|
|
2628
2668
|
for (const rel of relPaths) {
|
|
2629
|
-
const path =
|
|
2630
|
-
const before = await
|
|
2669
|
+
const path = join14(cwd, rel);
|
|
2670
|
+
const before = await readFile12(path, "utf-8");
|
|
2631
2671
|
const after = CODEMODS.reduce((s, fn) => fn(s), before);
|
|
2632
2672
|
if (after !== before) changes.push({ rel, after });
|
|
2633
2673
|
}
|
|
@@ -2636,7 +2676,7 @@ async function planGotchaCodemods(cwd) {
|
|
|
2636
2676
|
async function applyGotchaCodemods(cwd) {
|
|
2637
2677
|
const changes = await planGotchaCodemods(cwd);
|
|
2638
2678
|
for (const c of changes) {
|
|
2639
|
-
await writeFile6(
|
|
2679
|
+
await writeFile6(join14(cwd, c.rel), c.after, "utf-8");
|
|
2640
2680
|
}
|
|
2641
2681
|
return { filesChanged: changes.length };
|
|
2642
2682
|
}
|
|
@@ -2660,7 +2700,7 @@ async function verifyMigration(cwd, spawn2 = defaultSpawn) {
|
|
|
2660
2700
|
|
|
2661
2701
|
// src/recipes/svelte-5/step-summary.ts
|
|
2662
2702
|
import { writeFile as writeFile7 } from "fs/promises";
|
|
2663
|
-
import { join as
|
|
2703
|
+
import { join as join15 } from "path";
|
|
2664
2704
|
async function writeMigrationSummary(input) {
|
|
2665
2705
|
const lines = [
|
|
2666
2706
|
`# Svelte 4 \u2192 5 migration summary`,
|
|
@@ -2677,7 +2717,7 @@ async function writeMigrationSummary(input) {
|
|
|
2677
2717
|
`- Verify Playwright a11y tests still pass.`
|
|
2678
2718
|
];
|
|
2679
2719
|
const content = lines.join("\n") + "\n";
|
|
2680
|
-
const path =
|
|
2720
|
+
const path = join15(input.cwd, "MIGRATION_SVELTE_5.md");
|
|
2681
2721
|
await writeFile7(path, content, "utf-8");
|
|
2682
2722
|
return path;
|
|
2683
2723
|
}
|
|
@@ -2685,7 +2725,7 @@ async function writeMigrationSummary(input) {
|
|
|
2685
2725
|
// src/recipes/svelte-5/index.ts
|
|
2686
2726
|
async function alreadyOnSvelte5(cwd) {
|
|
2687
2727
|
try {
|
|
2688
|
-
const pkg = await readPackageJson(
|
|
2728
|
+
const pkg = await readPackageJson(join16(cwd, "package.json"));
|
|
2689
2729
|
const v = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte;
|
|
2690
2730
|
return !!v && /^\^?5\./.test(v);
|
|
2691
2731
|
} catch {
|
|
@@ -2780,7 +2820,7 @@ import { resolve as resolve6 } from "path";
|
|
|
2780
2820
|
|
|
2781
2821
|
// src/recipes/convert-to-pnpm.ts
|
|
2782
2822
|
import { rm as rm3, stat as stat3 } from "fs/promises";
|
|
2783
|
-
import { join as
|
|
2823
|
+
import { join as join17 } from "path";
|
|
2784
2824
|
|
|
2785
2825
|
// src/recipes/convert-to-pnpm/script-rewrites.ts
|
|
2786
2826
|
function rewriteScriptForPnpm(script) {
|
|
@@ -2813,9 +2853,9 @@ async function exists2(path) {
|
|
|
2813
2853
|
async function convertToPnpm(site, opts = {}) {
|
|
2814
2854
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
2815
2855
|
const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
|
|
2816
|
-
const pnpmLockPath =
|
|
2817
|
-
const npmLockPath =
|
|
2818
|
-
const yarnLockPath =
|
|
2856
|
+
const pnpmLockPath = join17(site.path, "pnpm-lock.yaml");
|
|
2857
|
+
const npmLockPath = join17(site.path, "package-lock.json");
|
|
2858
|
+
const yarnLockPath = join17(site.path, "yarn.lock");
|
|
2819
2859
|
return withRecipe({
|
|
2820
2860
|
name: "convert-to-pnpm",
|
|
2821
2861
|
site,
|
|
@@ -2838,7 +2878,7 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
2838
2878
|
if (hasYarnLock) await rm3(yarnLockPath, { force: true });
|
|
2839
2879
|
const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
2840
2880
|
await commit2(`chore(pnpm): remove ${sourceLock}`);
|
|
2841
|
-
const pkgPath =
|
|
2881
|
+
const pkgPath = join17(cwd, "package.json");
|
|
2842
2882
|
const pkg = await readPackageJson(pkgPath);
|
|
2843
2883
|
const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
|
|
2844
2884
|
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
@@ -2851,7 +2891,7 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
2851
2891
|
}
|
|
2852
2892
|
await writePackageJson(pkgPath, next);
|
|
2853
2893
|
await commit2("chore(pnpm): pin packageManager + rewrite npm scripts");
|
|
2854
|
-
await rm3(
|
|
2894
|
+
await rm3(join17(cwd, "node_modules"), { recursive: true, force: true });
|
|
2855
2895
|
const installResult = await spawn2("pnpm", ["install"], { cwd, streaming: true });
|
|
2856
2896
|
if (installResult.code !== 0) {
|
|
2857
2897
|
return { kind: "failed", notes: `pnpm install failed (exit ${installResult.code})` };
|
|
@@ -2892,16 +2932,16 @@ import { resolve as resolve7 } from "path";
|
|
|
2892
2932
|
|
|
2893
2933
|
// src/recipes/onboard.ts
|
|
2894
2934
|
import { stat as stat4 } from "fs/promises";
|
|
2895
|
-
import { join as
|
|
2935
|
+
import { join as join19 } from "path";
|
|
2896
2936
|
|
|
2897
2937
|
// src/util/self-version.ts
|
|
2898
2938
|
import { readFileSync } from "fs";
|
|
2899
2939
|
import { fileURLToPath } from "url";
|
|
2900
|
-
import { dirname, join as
|
|
2940
|
+
import { dirname, join as join18 } from "path";
|
|
2901
2941
|
function selfPackageVersion(callerImportMetaUrl) {
|
|
2902
2942
|
try {
|
|
2903
2943
|
const here3 = dirname(fileURLToPath(callerImportMetaUrl));
|
|
2904
|
-
const raw = readFileSync(
|
|
2944
|
+
const raw = readFileSync(join18(here3, "..", "..", "package.json"), "utf-8");
|
|
2905
2945
|
const pkg = JSON.parse(raw);
|
|
2906
2946
|
return pkg.version ?? "0.0.0";
|
|
2907
2947
|
} catch {
|
|
@@ -2951,13 +2991,13 @@ async function onboard(site, opts = {}) {
|
|
|
2951
2991
|
name: "onboard",
|
|
2952
2992
|
site,
|
|
2953
2993
|
plan: async () => {
|
|
2954
|
-
if (!await exists3(
|
|
2994
|
+
if (!await exists3(join19(site.path, "pnpm-lock.yaml"))) {
|
|
2955
2995
|
return {
|
|
2956
2996
|
kind: "failed",
|
|
2957
2997
|
notes: "no pnpm-lock.yaml at site root \u2014 run convert-to-pnpm first"
|
|
2958
2998
|
};
|
|
2959
2999
|
}
|
|
2960
|
-
const pkgPath =
|
|
3000
|
+
const pkgPath = join19(site.path, "package.json");
|
|
2961
3001
|
const pkg = await readPackageJson(pkgPath);
|
|
2962
3002
|
const toAdd = [];
|
|
2963
3003
|
if (!isDeclared(pkg, PACKAGE_NAME)) {
|
|
@@ -2977,7 +3017,7 @@ async function onboard(site, opts = {}) {
|
|
|
2977
3017
|
return { kind: "apply", plan: { pkg, toAdd } };
|
|
2978
3018
|
},
|
|
2979
3019
|
apply: async ({ pkg, toAdd }, { commit: commit2, cwd }) => {
|
|
2980
|
-
const pkgPath =
|
|
3020
|
+
const pkgPath = join19(cwd, "package.json");
|
|
2981
3021
|
let next = pkg;
|
|
2982
3022
|
for (const dep of toAdd) {
|
|
2983
3023
|
next = bumpDep(next, dep.name, dep.version);
|
|
@@ -3046,7 +3086,7 @@ import { resolve as resolve8 } from "path";
|
|
|
3046
3086
|
|
|
3047
3087
|
// src/recipes/svelte-codemods.ts
|
|
3048
3088
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
3049
|
-
import { join as
|
|
3089
|
+
import { join as join20 } from "path";
|
|
3050
3090
|
async function svelteCodemods(site) {
|
|
3051
3091
|
return withRecipe({
|
|
3052
3092
|
name: "svelte-codemods",
|
|
@@ -3060,7 +3100,7 @@ async function svelteCodemods(site) {
|
|
|
3060
3100
|
},
|
|
3061
3101
|
apply: async (changes, { commit: commit2, cwd }) => {
|
|
3062
3102
|
for (const c of changes) {
|
|
3063
|
-
await writeFile8(
|
|
3103
|
+
await writeFile8(join20(cwd, c.rel), c.after, "utf-8");
|
|
3064
3104
|
}
|
|
3065
3105
|
await commit2(`refactor(svelte5): apply codemods (${changes.length} files)`);
|
|
3066
3106
|
return { kind: "ok" };
|
|
@@ -3282,12 +3322,174 @@ async function runSingleSiteDraft(slug, opts) {
|
|
|
3282
3322
|
return { output: `Draft created: ${result.reportRow?.reportId}`, code: 0 };
|
|
3283
3323
|
}
|
|
3284
3324
|
|
|
3325
|
+
// src/cli/commands/init.ts
|
|
3326
|
+
import { resolve as resolve9 } from "path";
|
|
3327
|
+
|
|
3328
|
+
// src/recipes/a11y-fixtures-page/index.ts
|
|
3329
|
+
import { access, mkdir as mkdir3, writeFile as writeFile10 } from "fs/promises";
|
|
3330
|
+
import { dirname as dirname4, join as join22 } from "path";
|
|
3331
|
+
|
|
3332
|
+
// src/recipes/a11y-fixtures-page/template.ts
|
|
3333
|
+
var A11Y_FIXTURES_PAGE_RELATIVE = "src/routes/dev/a11y-fixtures/+page.svelte";
|
|
3334
|
+
var A11Y_FIXTURES_PAGE_TEMPLATE = `<svelte:head>
|
|
3335
|
+
<title>a11y fixtures \u2014 Reddoor</title>
|
|
3336
|
+
<meta
|
|
3337
|
+
name="description"
|
|
3338
|
+
content="Reddoor accessibility fixtures \u2014 semantic landmarks, heading hierarchy, and a stable target for @lhci/cli and Playwright + axe-core coverage. Not linked from the public site."
|
|
3339
|
+
/>
|
|
3340
|
+
</svelte:head>
|
|
3341
|
+
|
|
3342
|
+
<main>
|
|
3343
|
+
<header>
|
|
3344
|
+
<h1>Accessibility fixtures</h1>
|
|
3345
|
+
<p>
|
|
3346
|
+
This page exists so <code>@lhci/cli</code> and Playwright + axe-core have a
|
|
3347
|
+
stable target with predictable a11y characteristics. It is not linked from
|
|
3348
|
+
the public site.
|
|
3349
|
+
</p>
|
|
3350
|
+
</header>
|
|
3351
|
+
|
|
3352
|
+
<section aria-labelledby="landmarks-heading">
|
|
3353
|
+
<h2 id="landmarks-heading">Landmarks</h2>
|
|
3354
|
+
<p>
|
|
3355
|
+
A single <code>main</code> wraps the page; sections each declare
|
|
3356
|
+
<code>aria-labelledby</code> matched to their heading id so screen readers
|
|
3357
|
+
and axe both see a clean outline.
|
|
3358
|
+
</p>
|
|
3359
|
+
</section>
|
|
3360
|
+
|
|
3361
|
+
<section aria-labelledby="links-heading">
|
|
3362
|
+
<h2 id="links-heading">Links</h2>
|
|
3363
|
+
<p>
|
|
3364
|
+
<a href="/">Back to home</a> \u2014 relative link with descriptive visible text,
|
|
3365
|
+
so no <code>aria-label</code> override is needed.
|
|
3366
|
+
</p>
|
|
3367
|
+
</section>
|
|
3368
|
+
</main>
|
|
3369
|
+
`;
|
|
3370
|
+
|
|
3371
|
+
// src/recipes/a11y-fixtures-page/index.ts
|
|
3372
|
+
async function fileExists(path) {
|
|
3373
|
+
try {
|
|
3374
|
+
await access(path);
|
|
3375
|
+
return true;
|
|
3376
|
+
} catch {
|
|
3377
|
+
return false;
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
async function a11yFixturesPage(site) {
|
|
3381
|
+
const target = join22(site.path, A11Y_FIXTURES_PAGE_RELATIVE);
|
|
3382
|
+
return withRecipe({
|
|
3383
|
+
name: "a11y-fixtures-page",
|
|
3384
|
+
site,
|
|
3385
|
+
plan: async () => {
|
|
3386
|
+
if (await fileExists(target)) {
|
|
3387
|
+
return { kind: "noop", notes: `${A11Y_FIXTURES_PAGE_RELATIVE} already exists` };
|
|
3388
|
+
}
|
|
3389
|
+
return { kind: "apply", plan: { target } };
|
|
3390
|
+
},
|
|
3391
|
+
apply: async (planned, { commit: commit2 }) => {
|
|
3392
|
+
await mkdir3(dirname4(planned.target), { recursive: true });
|
|
3393
|
+
await writeFile10(planned.target, A11Y_FIXTURES_PAGE_TEMPLATE, "utf-8");
|
|
3394
|
+
await commit2("feat: add /dev/a11y-fixtures starter route");
|
|
3395
|
+
return { kind: "ok" };
|
|
3396
|
+
}
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
// src/recipes/init.ts
|
|
3401
|
+
function recipeStep(name, fn) {
|
|
3402
|
+
return {
|
|
3403
|
+
name,
|
|
3404
|
+
run: async (site) => ({ kind: "recipe", result: await fn(site) })
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
var DEFAULT_INIT_STEPS = [
|
|
3408
|
+
recipeStep("convert-to-pnpm", convertToPnpm),
|
|
3409
|
+
recipeStep("onboard", onboard),
|
|
3410
|
+
recipeStep("sync-configs", syncConfigs),
|
|
3411
|
+
recipeStep("svelte-codemods", svelteCodemods),
|
|
3412
|
+
recipeStep("a11y-fixtures-page", a11yFixturesPage),
|
|
3413
|
+
{
|
|
3414
|
+
name: "audit",
|
|
3415
|
+
run: async (site) => ({ kind: "audit", results: await runAudits(site) })
|
|
3416
|
+
}
|
|
3417
|
+
];
|
|
3418
|
+
async function init(site, opts = {}) {
|
|
3419
|
+
const steps = opts.steps ?? DEFAULT_INIT_STEPS;
|
|
3420
|
+
const out = [];
|
|
3421
|
+
for (const step of steps) {
|
|
3422
|
+
let result;
|
|
3423
|
+
try {
|
|
3424
|
+
result = await step.run(site);
|
|
3425
|
+
} catch (err) {
|
|
3426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3427
|
+
out.push({ name: step.name, result: { kind: "error", message } });
|
|
3428
|
+
return { site: siteLabel(site), steps: out, complete: false };
|
|
3429
|
+
}
|
|
3430
|
+
out.push({ name: step.name, result });
|
|
3431
|
+
if (result.kind === "recipe" && result.result.status === "failed") {
|
|
3432
|
+
return { site: siteLabel(site), steps: out, complete: false };
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
return { site: siteLabel(site), steps: out, complete: true };
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
// src/cli/commands/init.ts
|
|
3439
|
+
function formatStep(name, r) {
|
|
3440
|
+
if (r.kind === "error") return `${name.padEnd(20)} error: ${r.message}`;
|
|
3441
|
+
if (r.kind === "audit") {
|
|
3442
|
+
const lines = r.results.map(
|
|
3443
|
+
(a) => ` ${a.audit.padEnd(12)} ${a.status.padEnd(5)} ${a.summary}`
|
|
3444
|
+
);
|
|
3445
|
+
return `${name.padEnd(20)} ${r.results.length} audit(s):
|
|
3446
|
+
${lines.join("\n")}`;
|
|
3447
|
+
}
|
|
3448
|
+
const rec = r.result;
|
|
3449
|
+
if (rec.status === "noop") return `${name.padEnd(20)} noop${rec.notes ? ` \u2014 ${rec.notes}` : ""}`;
|
|
3450
|
+
if (rec.status === "failed")
|
|
3451
|
+
return `${name.padEnd(20)} failed${rec.notes ? ` \u2014 ${rec.notes}` : ""}`;
|
|
3452
|
+
return `${name.padEnd(20)} applied (${rec.commits.length} commit${rec.commits.length === 1 ? "" : "s"})${rec.notes ? ` \u2014 ${rec.notes}` : ""}`;
|
|
3453
|
+
}
|
|
3454
|
+
function formatResult7(r) {
|
|
3455
|
+
const header = `[${r.site}] init \u2014 ${r.complete ? "complete" : "STOPPED"}`;
|
|
3456
|
+
const body = r.steps.map((s) => formatStep(s.name, s.result)).join("\n");
|
|
3457
|
+
return `${header}
|
|
3458
|
+
${body}`;
|
|
3459
|
+
}
|
|
3460
|
+
function exitCodeFor(r) {
|
|
3461
|
+
if (!r.complete) return 1;
|
|
3462
|
+
for (const step of r.steps) {
|
|
3463
|
+
if (step.result.kind === "audit" && step.result.results.some((a) => a.status === "fail")) {
|
|
3464
|
+
return 1;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
return 0;
|
|
3468
|
+
}
|
|
3469
|
+
async function runInitCommand(site, opts) {
|
|
3470
|
+
const cwd = opts.cwd ? resolve9(opts.cwd) : process.cwd();
|
|
3471
|
+
let sites = await resolveSites({
|
|
3472
|
+
...site !== void 0 ? { site } : {},
|
|
3473
|
+
...opts.fleet !== void 0 ? { fleet: opts.fleet } : {},
|
|
3474
|
+
cwd
|
|
3475
|
+
});
|
|
3476
|
+
if (opts.fleet) {
|
|
3477
|
+
const workdir = opts.workdir ?? `${process.env.HOME ?? ""}/.reddoor-maint/sites`;
|
|
3478
|
+
sites = await Promise.all(sites.map((s) => cloneIfNeeded(s, { workdir })));
|
|
3479
|
+
}
|
|
3480
|
+
const results = [];
|
|
3481
|
+
for (const s of sites) results.push(await init(s));
|
|
3482
|
+
const output = results.map(formatResult7).join("\n\n");
|
|
3483
|
+
const code = results.some((r) => exitCodeFor(r) !== 0) ? 1 : 0;
|
|
3484
|
+
return { output, code };
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3285
3487
|
// src/cli/version.ts
|
|
3286
3488
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3287
|
-
import { join as
|
|
3489
|
+
import { join as join23 } from "path";
|
|
3288
3490
|
function resolvePackageVersion(fromDir) {
|
|
3289
3491
|
try {
|
|
3290
|
-
const raw = readFileSync2(
|
|
3492
|
+
const raw = readFileSync2(join23(fromDir, "..", "..", "package.json"), "utf-8");
|
|
3291
3493
|
const pkg = JSON.parse(raw);
|
|
3292
3494
|
return pkg.version ?? "unknown";
|
|
3293
3495
|
} catch {
|
|
@@ -3296,7 +3498,7 @@ function resolvePackageVersion(fromDir) {
|
|
|
3296
3498
|
}
|
|
3297
3499
|
|
|
3298
3500
|
// src/cli/bin.ts
|
|
3299
|
-
var here2 =
|
|
3501
|
+
var here2 = dirname5(fileURLToPath3(import.meta.url));
|
|
3300
3502
|
var version = resolvePackageVersion(here2);
|
|
3301
3503
|
var AUDIT_DESCRIPTIONS = {
|
|
3302
3504
|
deps: "Diff site package.json against the bundled baseline version map.",
|
|
@@ -3311,7 +3513,9 @@ var RECIPE_DESCRIPTIONS = {
|
|
|
3311
3513
|
"svelte-4-to-5": "Run the 7-commit Svelte 4 \u2192 5 upgrade recipe.",
|
|
3312
3514
|
"svelte-codemods": "Apply Svelte 5 gotcha codemods to an already-migrated site (state_referenced_locally, etc.).",
|
|
3313
3515
|
"convert-to-pnpm": "Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts).",
|
|
3314
|
-
onboard: "Install @reddoorla/maintenance + audit deps on a site (preferred first step)."
|
|
3516
|
+
onboard: "Install @reddoorla/maintenance + audit deps on a site (preferred first step).",
|
|
3517
|
+
"a11y-fixtures-page": "Write src/routes/dev/a11y-fixtures/+page.svelte (stub for lhci + axe targets).",
|
|
3518
|
+
init: "Run the full onboarding chain (convert-to-pnpm \u2192 onboard \u2192 sync-configs \u2192 svelte-codemods \u2192 a11y-fixtures-page \u2192 audit)."
|
|
3315
3519
|
};
|
|
3316
3520
|
async function runOrExit(fn, opts) {
|
|
3317
3521
|
try {
|
|
@@ -3388,6 +3592,15 @@ cli.command(
|
|
|
3388
3592
|
).option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
|
|
3389
3593
|
async (site, opts) => runOrExit(() => runOnboardCommand(site, opts), opts)
|
|
3390
3594
|
);
|
|
3595
|
+
cli.command(
|
|
3596
|
+
"init [site]",
|
|
3597
|
+
"One-shot guided onboarding: convert-to-pnpm \u2192 onboard \u2192 sync-configs \u2192 svelte-codemods \u2192 a11y-fixtures-page \u2192 audit."
|
|
3598
|
+
).option(
|
|
3599
|
+
"--fleet <inventory>",
|
|
3600
|
+
'Inventory file (.json or .mjs/.js), or "airtable" to read from Websites table'
|
|
3601
|
+
).option("--workdir <path>", "Clone target for fleet mode (default ~/.reddoor-maint/sites)").action(
|
|
3602
|
+
async (site, opts) => runOrExit(() => runInitCommand(site, opts), opts)
|
|
3603
|
+
);
|
|
3391
3604
|
cli.command("report [site]", "Draft or send maintenance/testing reports.").option("--due", "Scan all Websites and draft overdue reports.").option(
|
|
3392
3605
|
"--preview",
|
|
3393
3606
|
"Single-site dry run; writes reports/<slug>/draft.html, never touches Airtable."
|