@reddoorla/maintenance 0.6.0 → 0.6.2
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 +184 -26
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +9 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.js +217 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1177,9 +1177,9 @@ function onEventToHandler(source) {
|
|
|
1177
1177
|
}
|
|
1178
1178
|
|
|
1179
1179
|
// src/recipes/svelte-5/codemods/dollar-props.ts
|
|
1180
|
-
var
|
|
1180
|
+
var SCRIPT_BLOCK2 = /<script\b([^>]*)>([\s\S]*?)<\/script>/;
|
|
1181
1181
|
var EXPORT_LET = /^\s*export\s+let\s+(\w+)\s*(?::\s*([^=;\n]+))?\s*(?:=\s*([^;\n]+))?;?\s*$/gm;
|
|
1182
|
-
function transformScript(scriptBody) {
|
|
1182
|
+
function transformScript(scriptBody, isTs) {
|
|
1183
1183
|
const props = [];
|
|
1184
1184
|
const cleaned = scriptBody.replace(
|
|
1185
1185
|
EXPORT_LET,
|
|
@@ -1193,23 +1193,30 @@ function transformScript(scriptBody) {
|
|
|
1193
1193
|
}
|
|
1194
1194
|
);
|
|
1195
1195
|
if (props.length === 0) return { body: scriptBody, changed: false };
|
|
1196
|
-
const typeSig = props.map((p) => {
|
|
1197
|
-
const optional = p.defaultExpr ? "?" : "";
|
|
1198
|
-
return `${p.name}${optional}: ${p.type ?? "unknown"}`;
|
|
1199
|
-
}).join("; ");
|
|
1200
1196
|
const destructured = props.map((p) => p.defaultExpr ? `${p.name} = ${p.defaultExpr}` : p.name).join(", ");
|
|
1201
|
-
|
|
1197
|
+
let decl;
|
|
1198
|
+
if (isTs) {
|
|
1199
|
+
const typeSig = props.map((p) => {
|
|
1200
|
+
const optional = p.defaultExpr ? "?" : "";
|
|
1201
|
+
return `${p.name}${optional}: ${p.type ?? "unknown"}`;
|
|
1202
|
+
}).join("; ");
|
|
1203
|
+
decl = ` let { ${destructured} }: { ${typeSig} } = $props();`;
|
|
1204
|
+
} else {
|
|
1205
|
+
decl = ` let { ${destructured} } = $props();`;
|
|
1206
|
+
}
|
|
1202
1207
|
const next = cleaned.replace(/^(\s*)/, (m) => `${m}${decl}
|
|
1203
1208
|
`);
|
|
1204
1209
|
return { body: next, changed: true };
|
|
1205
1210
|
}
|
|
1206
1211
|
function exportLetToProps(source) {
|
|
1207
|
-
const match = source.match(
|
|
1212
|
+
const match = source.match(SCRIPT_BLOCK2);
|
|
1208
1213
|
if (!match) return source;
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1214
|
+
const attrs = match[1] ?? "";
|
|
1215
|
+
const inner = match[2] ?? "";
|
|
1216
|
+
const isTs = /\blang=["']ts["']/.test(attrs);
|
|
1217
|
+
const { body, changed } = transformScript(inner, isTs);
|
|
1211
1218
|
if (!changed) return source;
|
|
1212
|
-
return source.replace(
|
|
1219
|
+
return source.replace(SCRIPT_BLOCK2, (full) => full.replace(inner, body));
|
|
1213
1220
|
}
|
|
1214
1221
|
|
|
1215
1222
|
// src/recipes/svelte-5/codemods/dollar-restprops.ts
|
|
@@ -1251,6 +1258,128 @@ function stateEffectSyncToDerived(source) {
|
|
|
1251
1258
|
});
|
|
1252
1259
|
}
|
|
1253
1260
|
|
|
1261
|
+
// src/recipes/svelte-5/codemods/dollar-props-class.ts
|
|
1262
|
+
var PROPS_DESTRUCTURE = /let\s*\{([\s\S]*?)\}(\s*:\s*\{([\s\S]*?)\})?\s*=\s*\$props\(\)/;
|
|
1263
|
+
var DOLLAR_PROPS_CLASS = /\$\$props\.class\b/g;
|
|
1264
|
+
var DOLLAR_PROPS_ANY = /\$\$props\b/;
|
|
1265
|
+
var SCRIPT_BLOCK3 = /<script\b[^>]*>[\s\S]*?<\/script>/g;
|
|
1266
|
+
var MIGRATION_TASK = /^<!--\s*@migration-task[\s\S]*?-->\s*\n?/gm;
|
|
1267
|
+
var IDENT = "className";
|
|
1268
|
+
function maskScripts(source) {
|
|
1269
|
+
const blocks = [];
|
|
1270
|
+
const masked = source.replace(SCRIPT_BLOCK3, (m) => {
|
|
1271
|
+
blocks.push(m);
|
|
1272
|
+
return `__SCRIPT_${blocks.length - 1}__`;
|
|
1273
|
+
});
|
|
1274
|
+
return { masked, blocks };
|
|
1275
|
+
}
|
|
1276
|
+
function restoreScripts(masked, blocks) {
|
|
1277
|
+
let out = masked;
|
|
1278
|
+
blocks.forEach((blk, i) => {
|
|
1279
|
+
out = out.replace(`__SCRIPT_${i}__`, blk);
|
|
1280
|
+
});
|
|
1281
|
+
return out;
|
|
1282
|
+
}
|
|
1283
|
+
function dollarPropsClass(source) {
|
|
1284
|
+
const { masked } = maskScripts(source);
|
|
1285
|
+
if (!DOLLAR_PROPS_CLASS.test(masked)) return source;
|
|
1286
|
+
DOLLAR_PROPS_CLASS.lastIndex = 0;
|
|
1287
|
+
if (!PROPS_DESTRUCTURE.test(source)) return source;
|
|
1288
|
+
let updated = source.replace(PROPS_DESTRUCTURE, (full, body, typeAnno, typeBody) => {
|
|
1289
|
+
if (/\bclass\s*:/.test(body)) return full;
|
|
1290
|
+
const cleanBody = body.trim().replace(/,\s*$/, "").trim();
|
|
1291
|
+
const newBody = cleanBody ? `${cleanBody}, class: ${IDENT} = ""` : `class: ${IDENT} = ""`;
|
|
1292
|
+
if (typeAnno) {
|
|
1293
|
+
const cleanType = (typeBody ?? "").trim().replace(/;\s*$/, "").trim();
|
|
1294
|
+
const newType = cleanType ? `${cleanType}; class?: string` : `class?: string`;
|
|
1295
|
+
return `let { ${newBody} }: { ${newType} } = $props()`;
|
|
1296
|
+
}
|
|
1297
|
+
return `let { ${newBody} } = $props()`;
|
|
1298
|
+
});
|
|
1299
|
+
const reMasked = maskScripts(updated);
|
|
1300
|
+
const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS, IDENT);
|
|
1301
|
+
updated = restoreScripts(templateRewritten, reMasked.blocks);
|
|
1302
|
+
const stripped = updated.replace(MIGRATION_TASK, "");
|
|
1303
|
+
if (!DOLLAR_PROPS_ANY.test(stripped)) {
|
|
1304
|
+
updated = stripped;
|
|
1305
|
+
}
|
|
1306
|
+
return updated;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// src/recipes/svelte-5/codemods/legacy-reactive.ts
|
|
1310
|
+
var SCRIPT_BLOCK4 = /<script\b([^>]*)>([\s\S]*?)<\/script>/g;
|
|
1311
|
+
var SIMPLE_REACTIVE = /^([ \t]*)\$:\s*(\w+)\s*=\s*([^;\n]+);?[ \t]*$/gm;
|
|
1312
|
+
var BLOCK_REACTIVE_HEAD = /(^|\n)([ \t]*)\$:\s*\{/g;
|
|
1313
|
+
function findMatchingClose(source, openIdx) {
|
|
1314
|
+
let depth = 0;
|
|
1315
|
+
let i = openIdx;
|
|
1316
|
+
while (i < source.length) {
|
|
1317
|
+
const ch = source[i];
|
|
1318
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
1319
|
+
const closeStr = findStringClose(source, i);
|
|
1320
|
+
if (closeStr === -1) return -1;
|
|
1321
|
+
i = closeStr + 1;
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
if (ch === "{") depth++;
|
|
1325
|
+
else if (ch === "}") {
|
|
1326
|
+
depth--;
|
|
1327
|
+
if (depth === 0) return i;
|
|
1328
|
+
}
|
|
1329
|
+
i++;
|
|
1330
|
+
}
|
|
1331
|
+
return -1;
|
|
1332
|
+
}
|
|
1333
|
+
function findStringClose(source, openIdx) {
|
|
1334
|
+
const quote = source[openIdx];
|
|
1335
|
+
let i = openIdx + 1;
|
|
1336
|
+
while (i < source.length) {
|
|
1337
|
+
const ch = source[i];
|
|
1338
|
+
if (ch === "\\") {
|
|
1339
|
+
i += 2;
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (ch === quote) return i;
|
|
1343
|
+
i++;
|
|
1344
|
+
}
|
|
1345
|
+
return -1;
|
|
1346
|
+
}
|
|
1347
|
+
function transformBlocks(body) {
|
|
1348
|
+
const out = [];
|
|
1349
|
+
let last = 0;
|
|
1350
|
+
BLOCK_REACTIVE_HEAD.lastIndex = 0;
|
|
1351
|
+
let m;
|
|
1352
|
+
while ((m = BLOCK_REACTIVE_HEAD.exec(body)) !== null) {
|
|
1353
|
+
const leadingNewline = m[1] ?? "";
|
|
1354
|
+
const indent = m[2] ?? "";
|
|
1355
|
+
const headEnd = m.index + m[0].length;
|
|
1356
|
+
const openBraceIdx = headEnd - 1;
|
|
1357
|
+
const closeBraceIdx = findMatchingClose(body, openBraceIdx);
|
|
1358
|
+
if (closeBraceIdx === -1) continue;
|
|
1359
|
+
out.push(body.slice(last, m.index));
|
|
1360
|
+
out.push(leadingNewline);
|
|
1361
|
+
const blockBody = body.slice(openBraceIdx + 1, closeBraceIdx);
|
|
1362
|
+
out.push(`${indent}$effect(() => {${blockBody}});`);
|
|
1363
|
+
last = closeBraceIdx + 1;
|
|
1364
|
+
BLOCK_REACTIVE_HEAD.lastIndex = last;
|
|
1365
|
+
}
|
|
1366
|
+
out.push(body.slice(last));
|
|
1367
|
+
return out.join("");
|
|
1368
|
+
}
|
|
1369
|
+
function transformSimple(body) {
|
|
1370
|
+
return body.replace(SIMPLE_REACTIVE, (_full, indent, name, expr) => {
|
|
1371
|
+
return `${indent}let ${name} = $derived(${expr.trim()});`;
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
function legacyReactiveToRunes(source) {
|
|
1375
|
+
return source.replace(SCRIPT_BLOCK4, (full, _attrs, body) => {
|
|
1376
|
+
let next = transformBlocks(body);
|
|
1377
|
+
next = transformSimple(next);
|
|
1378
|
+
if (next === body) return full;
|
|
1379
|
+
return full.replace(body, next);
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1254
1383
|
// src/recipes/svelte-5/step-gotchas.ts
|
|
1255
1384
|
var SVELTE_GLOBS = ["src/**/*.svelte"];
|
|
1256
1385
|
var IGNORE2 = ["node_modules/**", ".svelte-kit/**", "build/**"];
|
|
@@ -1258,7 +1387,9 @@ var CODEMODS = [
|
|
|
1258
1387
|
onEventToHandler,
|
|
1259
1388
|
exportLetToProps,
|
|
1260
1389
|
removeDollarRestProps,
|
|
1261
|
-
stateEffectSyncToDerived
|
|
1390
|
+
stateEffectSyncToDerived,
|
|
1391
|
+
dollarPropsClass,
|
|
1392
|
+
legacyReactiveToRunes
|
|
1262
1393
|
];
|
|
1263
1394
|
async function planGotchaCodemods(cwd) {
|
|
1264
1395
|
const changes = [];
|
|
@@ -1402,9 +1533,48 @@ async function upgradeSvelte4to5(site, opts = {}) {
|
|
|
1402
1533
|
};
|
|
1403
1534
|
}
|
|
1404
1535
|
|
|
1536
|
+
// src/recipes/svelte-codemods.ts
|
|
1537
|
+
import { writeFile as writeFile8 } from "fs/promises";
|
|
1538
|
+
import { join as join12 } from "path";
|
|
1539
|
+
function siteLabel9(site) {
|
|
1540
|
+
return site.name ?? site.path;
|
|
1541
|
+
}
|
|
1542
|
+
async function svelteCodemods(site) {
|
|
1543
|
+
const label = siteLabel9(site);
|
|
1544
|
+
const changes = await planGotchaCodemods(site.path);
|
|
1545
|
+
if (changes.length === 0) {
|
|
1546
|
+
return {
|
|
1547
|
+
recipe: "svelte-codemods",
|
|
1548
|
+
site: label,
|
|
1549
|
+
status: "noop",
|
|
1550
|
+
commits: [],
|
|
1551
|
+
notes: "no codemod targets matched"
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
if (!await isWorkingTreeClean(site.path)) {
|
|
1555
|
+
throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
|
|
1556
|
+
}
|
|
1557
|
+
const branch = branchName("svelte-codemods");
|
|
1558
|
+
await createBranch(site.path, branch);
|
|
1559
|
+
for (const c of changes) {
|
|
1560
|
+
await writeFile8(join12(site.path, c.rel), c.after, "utf-8");
|
|
1561
|
+
}
|
|
1562
|
+
const sha = await commit(
|
|
1563
|
+
site.path,
|
|
1564
|
+
`refactor(svelte5): apply codemods (${changes.length} files)`
|
|
1565
|
+
);
|
|
1566
|
+
return {
|
|
1567
|
+
recipe: "svelte-codemods",
|
|
1568
|
+
site: label,
|
|
1569
|
+
status: "applied",
|
|
1570
|
+
commits: sha ? [sha] : [],
|
|
1571
|
+
notes: `branch: ${branch}`
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1405
1575
|
// src/recipes/convert-to-pnpm.ts
|
|
1406
1576
|
import { rm as rm3, stat } from "fs/promises";
|
|
1407
|
-
import { join as
|
|
1577
|
+
import { join as join13 } from "path";
|
|
1408
1578
|
|
|
1409
1579
|
// src/recipes/convert-to-pnpm/script-rewrites.ts
|
|
1410
1580
|
function rewriteScriptForPnpm(script) {
|
|
@@ -1434,16 +1604,16 @@ async function exists(path) {
|
|
|
1434
1604
|
return false;
|
|
1435
1605
|
}
|
|
1436
1606
|
}
|
|
1437
|
-
function
|
|
1607
|
+
function siteLabel10(site) {
|
|
1438
1608
|
return site.name ?? site.path;
|
|
1439
1609
|
}
|
|
1440
1610
|
async function convertToPnpm(site, opts = {}) {
|
|
1441
|
-
const label =
|
|
1611
|
+
const label = siteLabel10(site);
|
|
1442
1612
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1443
1613
|
const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
|
|
1444
|
-
const pnpmLockPath =
|
|
1445
|
-
const npmLockPath =
|
|
1446
|
-
const yarnLockPath =
|
|
1614
|
+
const pnpmLockPath = join13(site.path, "pnpm-lock.yaml");
|
|
1615
|
+
const npmLockPath = join13(site.path, "package-lock.json");
|
|
1616
|
+
const yarnLockPath = join13(site.path, "yarn.lock");
|
|
1447
1617
|
if (await exists(pnpmLockPath)) {
|
|
1448
1618
|
return {
|
|
1449
1619
|
recipe: "convert-to-pnpm",
|
|
@@ -1475,7 +1645,7 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
1475
1645
|
const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
1476
1646
|
const lockSha = await commit(site.path, `chore(pnpm): remove ${sourceLock}`);
|
|
1477
1647
|
if (lockSha) shas.push(lockSha);
|
|
1478
|
-
const pkgPath =
|
|
1648
|
+
const pkgPath = join13(site.path, "package.json");
|
|
1479
1649
|
const pkg = await readPackageJson(pkgPath);
|
|
1480
1650
|
const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
|
|
1481
1651
|
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
@@ -1515,9 +1685,28 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
1515
1685
|
|
|
1516
1686
|
// src/recipes/onboard.ts
|
|
1517
1687
|
import { stat as stat2 } from "fs/promises";
|
|
1518
|
-
import { join as
|
|
1688
|
+
import { join as join15 } from "path";
|
|
1689
|
+
|
|
1690
|
+
// src/util/self-version.ts
|
|
1691
|
+
import { readFileSync } from "fs";
|
|
1692
|
+
import { fileURLToPath } from "url";
|
|
1693
|
+
import { dirname, join as join14 } from "path";
|
|
1694
|
+
function selfPackageVersion(callerImportMetaUrl) {
|
|
1695
|
+
try {
|
|
1696
|
+
const here = dirname(fileURLToPath(callerImportMetaUrl));
|
|
1697
|
+
const raw = readFileSync(join14(here, "..", "..", "package.json"), "utf-8");
|
|
1698
|
+
const pkg = JSON.parse(raw);
|
|
1699
|
+
return pkg.version ?? "0.0.0";
|
|
1700
|
+
} catch {
|
|
1701
|
+
return "0.0.0";
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
function selfCaretRange(callerImportMetaUrl) {
|
|
1705
|
+
return `^${selfPackageVersion(callerImportMetaUrl)}`;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// src/recipes/onboard.ts
|
|
1519
1709
|
var PACKAGE_NAME = "@reddoorla/maintenance";
|
|
1520
|
-
var DEFAULT_PACKAGE_VERSION = "^0.2.0";
|
|
1521
1710
|
var AUDIT_DEPS = {
|
|
1522
1711
|
lighthouse: [{ name: "@lhci/cli", version: "^0.15.1" }],
|
|
1523
1712
|
a11y: [
|
|
@@ -1533,18 +1722,18 @@ async function exists2(path) {
|
|
|
1533
1722
|
return false;
|
|
1534
1723
|
}
|
|
1535
1724
|
}
|
|
1536
|
-
function
|
|
1725
|
+
function siteLabel11(site) {
|
|
1537
1726
|
return site.name ?? site.path;
|
|
1538
1727
|
}
|
|
1539
1728
|
function isDeclared(pkg, name) {
|
|
1540
1729
|
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
1541
1730
|
}
|
|
1542
1731
|
async function onboard(site, opts = {}) {
|
|
1543
|
-
const label =
|
|
1732
|
+
const label = siteLabel11(site);
|
|
1544
1733
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1545
1734
|
const audits = opts.audits ?? ["lighthouse", "a11y"];
|
|
1546
|
-
const packageVersion = opts.packageVersion ??
|
|
1547
|
-
if (!await exists2(
|
|
1735
|
+
const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);
|
|
1736
|
+
if (!await exists2(join15(site.path, "pnpm-lock.yaml"))) {
|
|
1548
1737
|
return {
|
|
1549
1738
|
recipe: "onboard",
|
|
1550
1739
|
site: label,
|
|
@@ -1553,7 +1742,7 @@ async function onboard(site, opts = {}) {
|
|
|
1553
1742
|
notes: "no pnpm-lock.yaml at site root \u2014 run convert-to-pnpm first"
|
|
1554
1743
|
};
|
|
1555
1744
|
}
|
|
1556
|
-
const pkgPath =
|
|
1745
|
+
const pkgPath = join15(site.path, "package.json");
|
|
1557
1746
|
const pkg = await readPackageJson(pkgPath);
|
|
1558
1747
|
const toAdd = [];
|
|
1559
1748
|
if (!isDeclared(pkg, PACKAGE_NAME)) {
|
|
@@ -1615,6 +1804,7 @@ var ALL_RECIPE_NAMES = [
|
|
|
1615
1804
|
"sync-configs",
|
|
1616
1805
|
"bump-deps",
|
|
1617
1806
|
"svelte-4-to-5",
|
|
1807
|
+
"svelte-codemods",
|
|
1618
1808
|
"convert-to-pnpm",
|
|
1619
1809
|
"onboard"
|
|
1620
1810
|
];
|
|
@@ -1680,6 +1870,7 @@ export {
|
|
|
1680
1870
|
runAudits,
|
|
1681
1871
|
runAuditsAcross,
|
|
1682
1872
|
securityAudit,
|
|
1873
|
+
svelteCodemods,
|
|
1683
1874
|
syncConfigs,
|
|
1684
1875
|
upgradeSvelte4to5
|
|
1685
1876
|
};
|