@reddoorla/maintenance 0.1.2 → 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 +213 -48
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +52 -43
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/configs/lighthouse.d.ts +1 -1
- package/dist/configs/lighthouse.js +4 -1
- package/dist/configs/lighthouse.js.map +1 -1
- package/dist/configs/playwright-a11y.js +2 -1
- package/dist/configs/playwright-a11y.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +170 -44
- package/dist/index.js.map +1 -1
- package/dist/recipes/sync-configs.d.ts +1 -1
- package/dist/{sync-configs-Z5a0iaDd.d.ts → sync-configs-7XIzzN_L.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -260,59 +260,64 @@ function extractAdvisoriesFromNpm(parsed) {
|
|
|
260
260
|
}
|
|
261
261
|
return [...roots.values()];
|
|
262
262
|
}
|
|
263
|
-
async function
|
|
263
|
+
async function runAuditTool(spawn2, cmd, args, cwd) {
|
|
264
|
+
let raw;
|
|
264
265
|
try {
|
|
265
|
-
|
|
266
|
+
raw = await spawn2(cmd, args, { cwd });
|
|
266
267
|
} catch (err) {
|
|
267
268
|
const e = err;
|
|
268
|
-
if (e.code === "ENOENT" || /ENOENT/.test(String(err))) return {
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
async function securityAudit(ctx) {
|
|
273
|
-
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
274
|
-
const site = ctx.site;
|
|
275
|
-
const label = siteLabel3(site);
|
|
276
|
-
let used = "pnpm audit";
|
|
277
|
-
let raw = await tryRun(
|
|
278
|
-
spawn2,
|
|
279
|
-
"pnpm",
|
|
280
|
-
["audit", "--json", "--prod"],
|
|
281
|
-
site.path
|
|
282
|
-
);
|
|
283
|
-
if ("missing" in raw) {
|
|
284
|
-
used = "npm audit";
|
|
285
|
-
raw = await tryRun(spawn2, "npm", ["audit", "--json", "--omit=dev"], site.path);
|
|
286
|
-
}
|
|
287
|
-
if ("missing" in raw) {
|
|
288
|
-
return {
|
|
289
|
-
audit: "security",
|
|
290
|
-
site: label,
|
|
291
|
-
status: "skip",
|
|
292
|
-
summary: "neither pnpm nor npm is available on PATH"
|
|
293
|
-
};
|
|
269
|
+
if (e.code === "ENOENT" || /ENOENT/.test(String(err))) return { kind: "missing" };
|
|
270
|
+
return { kind: "error", reason: `spawn failed: ${String(err).slice(0, 200)}` };
|
|
294
271
|
}
|
|
295
272
|
if (raw.code !== 0 && raw.code !== 1) {
|
|
296
273
|
return {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
status: "skip",
|
|
300
|
-
summary: `${used} exited with code ${raw.code}`,
|
|
301
|
-
details: { stderr: raw.stderr }
|
|
274
|
+
kind: "error",
|
|
275
|
+
reason: `exit ${raw.code}${raw.stderr ? `: ${raw.stderr.slice(0, 150)}` : ""}`
|
|
302
276
|
};
|
|
303
277
|
}
|
|
304
278
|
let parsed;
|
|
305
279
|
try {
|
|
306
|
-
parsed = JSON.parse(raw.stdout);
|
|
280
|
+
parsed = JSON.parse(raw.stdout || "{}");
|
|
307
281
|
} catch (err) {
|
|
308
|
-
return {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
details: { error: String(err), stdout: raw.stdout.slice(0, 500) }
|
|
314
|
-
};
|
|
282
|
+
return { kind: "error", reason: `unparseable JSON: ${String(err).slice(0, 100)}` };
|
|
283
|
+
}
|
|
284
|
+
const errEnvelope = parsed.error;
|
|
285
|
+
if (errEnvelope && typeof errEnvelope === "object") {
|
|
286
|
+
return { kind: "error", reason: errEnvelope.code ?? "error envelope returned" };
|
|
315
287
|
}
|
|
288
|
+
if (!parsed.metadata?.vulnerabilities) {
|
|
289
|
+
return { kind: "error", reason: "no metadata.vulnerabilities in output" };
|
|
290
|
+
}
|
|
291
|
+
return { kind: "ok", parsed };
|
|
292
|
+
}
|
|
293
|
+
async function securityAudit(ctx) {
|
|
294
|
+
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
295
|
+
const site = ctx.site;
|
|
296
|
+
const label = siteLabel3(site);
|
|
297
|
+
let used = "pnpm audit";
|
|
298
|
+
let result = await runAuditTool(spawn2, "pnpm", ["audit", "--json", "--prod"], site.path);
|
|
299
|
+
if (result.kind !== "ok") {
|
|
300
|
+
const pnpmReason = result.kind === "missing" ? "not installed" : result.reason;
|
|
301
|
+
const npmResult = await runAuditTool(
|
|
302
|
+
spawn2,
|
|
303
|
+
"npm",
|
|
304
|
+
["audit", "--json", "--omit=dev"],
|
|
305
|
+
site.path
|
|
306
|
+
);
|
|
307
|
+
if (npmResult.kind === "ok") {
|
|
308
|
+
result = npmResult;
|
|
309
|
+
used = "npm audit";
|
|
310
|
+
} else {
|
|
311
|
+
const npmReason = npmResult.kind === "missing" ? "not installed" : npmResult.reason;
|
|
312
|
+
return {
|
|
313
|
+
audit: "security",
|
|
314
|
+
site: label,
|
|
315
|
+
status: "skip",
|
|
316
|
+
summary: `cannot run audit \u2014 pnpm: ${pnpmReason}; npm: ${npmReason}`
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const parsed = result.parsed;
|
|
316
321
|
const counts = {
|
|
317
322
|
low: parsed.metadata?.vulnerabilities?.low ?? 0,
|
|
318
323
|
moderate: parsed.metadata?.vulnerabilities?.moderate ?? 0,
|
|
@@ -342,7 +347,10 @@ var lighthouseConfig = {
|
|
|
342
347
|
ci: {
|
|
343
348
|
collect: {
|
|
344
349
|
url: ["http://localhost:5173/dev/a11y-fixtures"],
|
|
345
|
-
|
|
350
|
+
// `npm run vite:dev` works on both pnpm and npm sites — pnpm respects
|
|
351
|
+
// the `run` form too. Keeps this config portable across the fleet
|
|
352
|
+
// while sites transition to pnpm.
|
|
353
|
+
startServerCommand: "npm run vite:dev",
|
|
346
354
|
startServerReadyPattern: "ready in",
|
|
347
355
|
startServerReadyTimeout: 12e4,
|
|
348
356
|
numberOfRuns: 1,
|
|
@@ -494,7 +502,8 @@ var playwrightA11yConfig = defineConfig({
|
|
|
494
502
|
}
|
|
495
503
|
],
|
|
496
504
|
webServer: {
|
|
497
|
-
|
|
505
|
+
// Portable across pnpm and npm sites — pnpm respects `npm run` too.
|
|
506
|
+
command: "npm run vite:dev",
|
|
498
507
|
url: "http://localhost:5173/dev/a11y-fixtures",
|
|
499
508
|
reuseExistingServer: !process.env.CI,
|
|
500
509
|
timeout: 12e4
|
|
@@ -1238,8 +1247,124 @@ async function upgradeSvelte4to5(site, opts = {}) {
|
|
|
1238
1247
|
};
|
|
1239
1248
|
}
|
|
1240
1249
|
|
|
1250
|
+
// src/recipes/convert-to-pnpm.ts
|
|
1251
|
+
import { rm as rm3, stat } from "fs/promises";
|
|
1252
|
+
import { join as join12 } from "path";
|
|
1253
|
+
|
|
1254
|
+
// src/recipes/convert-to-pnpm/script-rewrites.ts
|
|
1255
|
+
function rewriteScriptForPnpm(script) {
|
|
1256
|
+
let out = script;
|
|
1257
|
+
out = out.replace(/\bnpm run(?=\s)/g, "pnpm run");
|
|
1258
|
+
out = out.replace(/\bnpx(?=\s)/g, "pnpm dlx");
|
|
1259
|
+
return out;
|
|
1260
|
+
}
|
|
1261
|
+
function rewriteScriptsForPnpm(scripts) {
|
|
1262
|
+
const next = {};
|
|
1263
|
+
let changedCount = 0;
|
|
1264
|
+
for (const [name, value] of Object.entries(scripts)) {
|
|
1265
|
+
const rewritten = rewriteScriptForPnpm(value);
|
|
1266
|
+
next[name] = rewritten;
|
|
1267
|
+
if (rewritten !== value) changedCount++;
|
|
1268
|
+
}
|
|
1269
|
+
return { scripts: next, changedCount };
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// src/recipes/convert-to-pnpm.ts
|
|
1273
|
+
var DEFAULT_PNPM_VERSION = "10.33.1";
|
|
1274
|
+
async function exists(path) {
|
|
1275
|
+
try {
|
|
1276
|
+
await stat(path);
|
|
1277
|
+
return true;
|
|
1278
|
+
} catch {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
function siteLabel9(site) {
|
|
1283
|
+
return site.name ?? site.path;
|
|
1284
|
+
}
|
|
1285
|
+
async function convertToPnpm(site, opts = {}) {
|
|
1286
|
+
const label = siteLabel9(site);
|
|
1287
|
+
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1288
|
+
const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
|
|
1289
|
+
const pnpmLockPath = join12(site.path, "pnpm-lock.yaml");
|
|
1290
|
+
const npmLockPath = join12(site.path, "package-lock.json");
|
|
1291
|
+
const yarnLockPath = join12(site.path, "yarn.lock");
|
|
1292
|
+
if (await exists(pnpmLockPath)) {
|
|
1293
|
+
return {
|
|
1294
|
+
recipe: "convert-to-pnpm",
|
|
1295
|
+
site: label,
|
|
1296
|
+
status: "noop",
|
|
1297
|
+
commits: [],
|
|
1298
|
+
notes: "site already has pnpm-lock.yaml"
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
const hasNpmLock = await exists(npmLockPath);
|
|
1302
|
+
const hasYarnLock = await exists(yarnLockPath);
|
|
1303
|
+
if (!hasNpmLock && !hasYarnLock) {
|
|
1304
|
+
return {
|
|
1305
|
+
recipe: "convert-to-pnpm",
|
|
1306
|
+
site: label,
|
|
1307
|
+
status: "noop",
|
|
1308
|
+
commits: [],
|
|
1309
|
+
notes: "no convertible lockfile (package-lock.json or yarn.lock) at site root"
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
if (!await isWorkingTreeClean(site.path)) {
|
|
1313
|
+
throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
|
|
1314
|
+
}
|
|
1315
|
+
const branch = branchName("convert-to-pnpm");
|
|
1316
|
+
await createBranch(site.path, branch);
|
|
1317
|
+
const shas = [];
|
|
1318
|
+
if (hasNpmLock) await rm3(npmLockPath, { force: true });
|
|
1319
|
+
if (hasYarnLock) await rm3(yarnLockPath, { force: true });
|
|
1320
|
+
const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
1321
|
+
const lockSha = await commit(site.path, `chore(pnpm): remove ${sourceLock}`);
|
|
1322
|
+
if (lockSha) shas.push(lockSha);
|
|
1323
|
+
const pkgPath = join12(site.path, "package.json");
|
|
1324
|
+
const pkg = await readPackageJson(pkgPath);
|
|
1325
|
+
const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
|
|
1326
|
+
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
1327
|
+
const { scripts: rewritten, changedCount } = rewriteScriptsForPnpm(
|
|
1328
|
+
pkg.scripts
|
|
1329
|
+
);
|
|
1330
|
+
if (changedCount > 0) {
|
|
1331
|
+
next.scripts = rewritten;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
await writePackageJson(pkgPath, next);
|
|
1335
|
+
const pkgSha = await commit(site.path, "chore(pnpm): pin packageManager + rewrite npm scripts");
|
|
1336
|
+
if (pkgSha) shas.push(pkgSha);
|
|
1337
|
+
const installResult = await spawn2("pnpm", ["install"], {
|
|
1338
|
+
cwd: site.path,
|
|
1339
|
+
streaming: true
|
|
1340
|
+
});
|
|
1341
|
+
if (installResult.code !== 0) {
|
|
1342
|
+
return {
|
|
1343
|
+
recipe: "convert-to-pnpm",
|
|
1344
|
+
site: label,
|
|
1345
|
+
status: "failed",
|
|
1346
|
+
commits: shas,
|
|
1347
|
+
notes: `pnpm install failed (exit ${installResult.code}). branch ${branch} left for inspection.`
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
const installSha = await commit(site.path, "chore(pnpm): add pnpm-lock.yaml");
|
|
1351
|
+
if (installSha) shas.push(installSha);
|
|
1352
|
+
return {
|
|
1353
|
+
recipe: "convert-to-pnpm",
|
|
1354
|
+
site: label,
|
|
1355
|
+
status: "applied",
|
|
1356
|
+
commits: shas,
|
|
1357
|
+
notes: `branch: ${branch}`
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1241
1361
|
// src/recipes/index.ts
|
|
1242
|
-
var ALL_RECIPE_NAMES = [
|
|
1362
|
+
var ALL_RECIPE_NAMES = [
|
|
1363
|
+
"sync-configs",
|
|
1364
|
+
"bump-deps",
|
|
1365
|
+
"svelte-4-to-5",
|
|
1366
|
+
"convert-to-pnpm"
|
|
1367
|
+
];
|
|
1243
1368
|
function isRecipeName(value) {
|
|
1244
1369
|
return ALL_RECIPE_NAMES.includes(value);
|
|
1245
1370
|
}
|
|
@@ -1291,6 +1416,7 @@ export {
|
|
|
1291
1416
|
ALL_RECIPE_NAMES,
|
|
1292
1417
|
a11yAudit,
|
|
1293
1418
|
bumpDeps,
|
|
1419
|
+
convertToPnpm,
|
|
1294
1420
|
depsAudit,
|
|
1295
1421
|
fromJsonFile,
|
|
1296
1422
|
isRecipeName,
|