@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/cli/bin.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/bin.ts
|
|
4
|
-
import { dirname } from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname as dirname2 } from "path";
|
|
5
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6
6
|
import { cac } from "cac";
|
|
7
7
|
|
|
8
8
|
// src/cli/commands/audit.ts
|
|
@@ -761,6 +761,13 @@ function assertSafeName(name) {
|
|
|
761
761
|
throw new Error(`unsafe site name (traversal segment not allowed): ${name}`);
|
|
762
762
|
}
|
|
763
763
|
}
|
|
764
|
+
function assertSafeRepoUrl(repoUrl) {
|
|
765
|
+
if (!/^(https?|ssh|git|file):\/\//.test(repoUrl) && !/^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+:/.test(repoUrl)) {
|
|
766
|
+
throw new Error(
|
|
767
|
+
`unsafe repoUrl: must start with a scheme (https://, ssh://, git://, file://) or use scp-style "user@host:path" (got: ${JSON.stringify(repoUrl)})`
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
764
771
|
async function isNonEmptyDir(path) {
|
|
765
772
|
try {
|
|
766
773
|
const s = await stat(path);
|
|
@@ -778,13 +785,14 @@ async function cloneIfNeeded(site, opts) {
|
|
|
778
785
|
}
|
|
779
786
|
const name = site.name ?? deriveNameFromRepoUrl(site.repoUrl);
|
|
780
787
|
assertSafeName(name);
|
|
788
|
+
assertSafeRepoUrl(site.repoUrl);
|
|
781
789
|
const target = join5(opts.workdir, name);
|
|
782
790
|
await mkdir(opts.workdir, { recursive: true });
|
|
783
791
|
if (await isNonEmptyDir(target)) {
|
|
784
792
|
return { ...site, name, path: target };
|
|
785
793
|
}
|
|
786
794
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
787
|
-
const result = await spawn2("git", ["clone", site.repoUrl, target], {
|
|
795
|
+
const result = await spawn2("git", ["clone", "--", site.repoUrl, target], {
|
|
788
796
|
cwd: opts.workdir,
|
|
789
797
|
timeoutMs: 5 * 6e4
|
|
790
798
|
});
|
|
@@ -1443,9 +1451,9 @@ function onEventToHandler(source) {
|
|
|
1443
1451
|
}
|
|
1444
1452
|
|
|
1445
1453
|
// src/recipes/svelte-5/codemods/dollar-props.ts
|
|
1446
|
-
var
|
|
1454
|
+
var SCRIPT_BLOCK2 = /<script\b([^>]*)>([\s\S]*?)<\/script>/;
|
|
1447
1455
|
var EXPORT_LET = /^\s*export\s+let\s+(\w+)\s*(?::\s*([^=;\n]+))?\s*(?:=\s*([^;\n]+))?;?\s*$/gm;
|
|
1448
|
-
function transformScript(scriptBody) {
|
|
1456
|
+
function transformScript(scriptBody, isTs) {
|
|
1449
1457
|
const props = [];
|
|
1450
1458
|
const cleaned = scriptBody.replace(
|
|
1451
1459
|
EXPORT_LET,
|
|
@@ -1459,23 +1467,30 @@ function transformScript(scriptBody) {
|
|
|
1459
1467
|
}
|
|
1460
1468
|
);
|
|
1461
1469
|
if (props.length === 0) return { body: scriptBody, changed: false };
|
|
1462
|
-
const typeSig = props.map((p) => {
|
|
1463
|
-
const optional = p.defaultExpr ? "?" : "";
|
|
1464
|
-
return `${p.name}${optional}: ${p.type ?? "unknown"}`;
|
|
1465
|
-
}).join("; ");
|
|
1466
1470
|
const destructured = props.map((p) => p.defaultExpr ? `${p.name} = ${p.defaultExpr}` : p.name).join(", ");
|
|
1467
|
-
|
|
1471
|
+
let decl;
|
|
1472
|
+
if (isTs) {
|
|
1473
|
+
const typeSig = props.map((p) => {
|
|
1474
|
+
const optional = p.defaultExpr ? "?" : "";
|
|
1475
|
+
return `${p.name}${optional}: ${p.type ?? "unknown"}`;
|
|
1476
|
+
}).join("; ");
|
|
1477
|
+
decl = ` let { ${destructured} }: { ${typeSig} } = $props();`;
|
|
1478
|
+
} else {
|
|
1479
|
+
decl = ` let { ${destructured} } = $props();`;
|
|
1480
|
+
}
|
|
1468
1481
|
const next = cleaned.replace(/^(\s*)/, (m) => `${m}${decl}
|
|
1469
1482
|
`);
|
|
1470
1483
|
return { body: next, changed: true };
|
|
1471
1484
|
}
|
|
1472
1485
|
function exportLetToProps(source) {
|
|
1473
|
-
const match = source.match(
|
|
1486
|
+
const match = source.match(SCRIPT_BLOCK2);
|
|
1474
1487
|
if (!match) return source;
|
|
1475
|
-
const
|
|
1476
|
-
const
|
|
1488
|
+
const attrs = match[1] ?? "";
|
|
1489
|
+
const inner = match[2] ?? "";
|
|
1490
|
+
const isTs = /\blang=["']ts["']/.test(attrs);
|
|
1491
|
+
const { body, changed } = transformScript(inner, isTs);
|
|
1477
1492
|
if (!changed) return source;
|
|
1478
|
-
return source.replace(
|
|
1493
|
+
return source.replace(SCRIPT_BLOCK2, (full) => full.replace(inner, body));
|
|
1479
1494
|
}
|
|
1480
1495
|
|
|
1481
1496
|
// src/recipes/svelte-5/codemods/dollar-restprops.ts
|
|
@@ -1517,6 +1532,128 @@ function stateEffectSyncToDerived(source) {
|
|
|
1517
1532
|
});
|
|
1518
1533
|
}
|
|
1519
1534
|
|
|
1535
|
+
// src/recipes/svelte-5/codemods/dollar-props-class.ts
|
|
1536
|
+
var PROPS_DESTRUCTURE = /let\s*\{([\s\S]*?)\}(\s*:\s*\{([\s\S]*?)\})?\s*=\s*\$props\(\)/;
|
|
1537
|
+
var DOLLAR_PROPS_CLASS = /\$\$props\.class\b/g;
|
|
1538
|
+
var DOLLAR_PROPS_ANY = /\$\$props\b/;
|
|
1539
|
+
var SCRIPT_BLOCK3 = /<script\b[^>]*>[\s\S]*?<\/script>/g;
|
|
1540
|
+
var MIGRATION_TASK = /^<!--\s*@migration-task[\s\S]*?-->\s*\n?/gm;
|
|
1541
|
+
var IDENT = "className";
|
|
1542
|
+
function maskScripts(source) {
|
|
1543
|
+
const blocks = [];
|
|
1544
|
+
const masked = source.replace(SCRIPT_BLOCK3, (m) => {
|
|
1545
|
+
blocks.push(m);
|
|
1546
|
+
return `__SCRIPT_${blocks.length - 1}__`;
|
|
1547
|
+
});
|
|
1548
|
+
return { masked, blocks };
|
|
1549
|
+
}
|
|
1550
|
+
function restoreScripts(masked, blocks) {
|
|
1551
|
+
let out = masked;
|
|
1552
|
+
blocks.forEach((blk, i) => {
|
|
1553
|
+
out = out.replace(`__SCRIPT_${i}__`, blk);
|
|
1554
|
+
});
|
|
1555
|
+
return out;
|
|
1556
|
+
}
|
|
1557
|
+
function dollarPropsClass(source) {
|
|
1558
|
+
const { masked } = maskScripts(source);
|
|
1559
|
+
if (!DOLLAR_PROPS_CLASS.test(masked)) return source;
|
|
1560
|
+
DOLLAR_PROPS_CLASS.lastIndex = 0;
|
|
1561
|
+
if (!PROPS_DESTRUCTURE.test(source)) return source;
|
|
1562
|
+
let updated = source.replace(PROPS_DESTRUCTURE, (full, body, typeAnno, typeBody) => {
|
|
1563
|
+
if (/\bclass\s*:/.test(body)) return full;
|
|
1564
|
+
const cleanBody = body.trim().replace(/,\s*$/, "").trim();
|
|
1565
|
+
const newBody = cleanBody ? `${cleanBody}, class: ${IDENT} = ""` : `class: ${IDENT} = ""`;
|
|
1566
|
+
if (typeAnno) {
|
|
1567
|
+
const cleanType = (typeBody ?? "").trim().replace(/;\s*$/, "").trim();
|
|
1568
|
+
const newType = cleanType ? `${cleanType}; class?: string` : `class?: string`;
|
|
1569
|
+
return `let { ${newBody} }: { ${newType} } = $props()`;
|
|
1570
|
+
}
|
|
1571
|
+
return `let { ${newBody} } = $props()`;
|
|
1572
|
+
});
|
|
1573
|
+
const reMasked = maskScripts(updated);
|
|
1574
|
+
const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS, IDENT);
|
|
1575
|
+
updated = restoreScripts(templateRewritten, reMasked.blocks);
|
|
1576
|
+
const stripped = updated.replace(MIGRATION_TASK, "");
|
|
1577
|
+
if (!DOLLAR_PROPS_ANY.test(stripped)) {
|
|
1578
|
+
updated = stripped;
|
|
1579
|
+
}
|
|
1580
|
+
return updated;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// src/recipes/svelte-5/codemods/legacy-reactive.ts
|
|
1584
|
+
var SCRIPT_BLOCK4 = /<script\b([^>]*)>([\s\S]*?)<\/script>/g;
|
|
1585
|
+
var SIMPLE_REACTIVE = /^([ \t]*)\$:\s*(\w+)\s*=\s*([^;\n]+);?[ \t]*$/gm;
|
|
1586
|
+
var BLOCK_REACTIVE_HEAD = /(^|\n)([ \t]*)\$:\s*\{/g;
|
|
1587
|
+
function findMatchingClose(source, openIdx) {
|
|
1588
|
+
let depth = 0;
|
|
1589
|
+
let i = openIdx;
|
|
1590
|
+
while (i < source.length) {
|
|
1591
|
+
const ch = source[i];
|
|
1592
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
1593
|
+
const closeStr = findStringClose(source, i);
|
|
1594
|
+
if (closeStr === -1) return -1;
|
|
1595
|
+
i = closeStr + 1;
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (ch === "{") depth++;
|
|
1599
|
+
else if (ch === "}") {
|
|
1600
|
+
depth--;
|
|
1601
|
+
if (depth === 0) return i;
|
|
1602
|
+
}
|
|
1603
|
+
i++;
|
|
1604
|
+
}
|
|
1605
|
+
return -1;
|
|
1606
|
+
}
|
|
1607
|
+
function findStringClose(source, openIdx) {
|
|
1608
|
+
const quote = source[openIdx];
|
|
1609
|
+
let i = openIdx + 1;
|
|
1610
|
+
while (i < source.length) {
|
|
1611
|
+
const ch = source[i];
|
|
1612
|
+
if (ch === "\\") {
|
|
1613
|
+
i += 2;
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
if (ch === quote) return i;
|
|
1617
|
+
i++;
|
|
1618
|
+
}
|
|
1619
|
+
return -1;
|
|
1620
|
+
}
|
|
1621
|
+
function transformBlocks(body) {
|
|
1622
|
+
const out = [];
|
|
1623
|
+
let last = 0;
|
|
1624
|
+
BLOCK_REACTIVE_HEAD.lastIndex = 0;
|
|
1625
|
+
let m;
|
|
1626
|
+
while ((m = BLOCK_REACTIVE_HEAD.exec(body)) !== null) {
|
|
1627
|
+
const leadingNewline = m[1] ?? "";
|
|
1628
|
+
const indent = m[2] ?? "";
|
|
1629
|
+
const headEnd = m.index + m[0].length;
|
|
1630
|
+
const openBraceIdx = headEnd - 1;
|
|
1631
|
+
const closeBraceIdx = findMatchingClose(body, openBraceIdx);
|
|
1632
|
+
if (closeBraceIdx === -1) continue;
|
|
1633
|
+
out.push(body.slice(last, m.index));
|
|
1634
|
+
out.push(leadingNewline);
|
|
1635
|
+
const blockBody = body.slice(openBraceIdx + 1, closeBraceIdx);
|
|
1636
|
+
out.push(`${indent}$effect(() => {${blockBody}});`);
|
|
1637
|
+
last = closeBraceIdx + 1;
|
|
1638
|
+
BLOCK_REACTIVE_HEAD.lastIndex = last;
|
|
1639
|
+
}
|
|
1640
|
+
out.push(body.slice(last));
|
|
1641
|
+
return out.join("");
|
|
1642
|
+
}
|
|
1643
|
+
function transformSimple(body) {
|
|
1644
|
+
return body.replace(SIMPLE_REACTIVE, (_full, indent, name, expr) => {
|
|
1645
|
+
return `${indent}let ${name} = $derived(${expr.trim()});`;
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
function legacyReactiveToRunes(source) {
|
|
1649
|
+
return source.replace(SCRIPT_BLOCK4, (full, _attrs, body) => {
|
|
1650
|
+
let next = transformBlocks(body);
|
|
1651
|
+
next = transformSimple(next);
|
|
1652
|
+
if (next === body) return full;
|
|
1653
|
+
return full.replace(body, next);
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1520
1657
|
// src/recipes/svelte-5/step-gotchas.ts
|
|
1521
1658
|
var SVELTE_GLOBS = ["src/**/*.svelte"];
|
|
1522
1659
|
var IGNORE2 = ["node_modules/**", ".svelte-kit/**", "build/**"];
|
|
@@ -1524,7 +1661,9 @@ var CODEMODS = [
|
|
|
1524
1661
|
onEventToHandler,
|
|
1525
1662
|
exportLetToProps,
|
|
1526
1663
|
removeDollarRestProps,
|
|
1527
|
-
stateEffectSyncToDerived
|
|
1664
|
+
stateEffectSyncToDerived,
|
|
1665
|
+
dollarPropsClass,
|
|
1666
|
+
legacyReactiveToRunes
|
|
1528
1667
|
];
|
|
1529
1668
|
async function planGotchaCodemods(cwd) {
|
|
1530
1669
|
const changes = [];
|
|
@@ -1849,9 +1988,28 @@ import { resolve as resolve7 } from "path";
|
|
|
1849
1988
|
|
|
1850
1989
|
// src/recipes/onboard.ts
|
|
1851
1990
|
import { stat as stat3 } from "fs/promises";
|
|
1852
|
-
import { join as
|
|
1991
|
+
import { join as join16 } from "path";
|
|
1992
|
+
|
|
1993
|
+
// src/util/self-version.ts
|
|
1994
|
+
import { readFileSync } from "fs";
|
|
1995
|
+
import { fileURLToPath } from "url";
|
|
1996
|
+
import { dirname, join as join15 } from "path";
|
|
1997
|
+
function selfPackageVersion(callerImportMetaUrl) {
|
|
1998
|
+
try {
|
|
1999
|
+
const here2 = dirname(fileURLToPath(callerImportMetaUrl));
|
|
2000
|
+
const raw = readFileSync(join15(here2, "..", "..", "package.json"), "utf-8");
|
|
2001
|
+
const pkg = JSON.parse(raw);
|
|
2002
|
+
return pkg.version ?? "0.0.0";
|
|
2003
|
+
} catch {
|
|
2004
|
+
return "0.0.0";
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
function selfCaretRange(callerImportMetaUrl) {
|
|
2008
|
+
return `^${selfPackageVersion(callerImportMetaUrl)}`;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// src/recipes/onboard.ts
|
|
1853
2012
|
var PACKAGE_NAME = "@reddoorla/maintenance";
|
|
1854
|
-
var DEFAULT_PACKAGE_VERSION = "^0.2.0";
|
|
1855
2013
|
var AUDIT_DEPS = {
|
|
1856
2014
|
lighthouse: [{ name: "@lhci/cli", version: "^0.15.1" }],
|
|
1857
2015
|
a11y: [
|
|
@@ -1877,8 +2035,8 @@ async function onboard(site, opts = {}) {
|
|
|
1877
2035
|
const label = siteLabel10(site);
|
|
1878
2036
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1879
2037
|
const audits = opts.audits ?? ["lighthouse", "a11y"];
|
|
1880
|
-
const packageVersion = opts.packageVersion ??
|
|
1881
|
-
if (!await exists2(
|
|
2038
|
+
const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);
|
|
2039
|
+
if (!await exists2(join16(site.path, "pnpm-lock.yaml"))) {
|
|
1882
2040
|
return {
|
|
1883
2041
|
recipe: "onboard",
|
|
1884
2042
|
site: label,
|
|
@@ -1887,7 +2045,7 @@ async function onboard(site, opts = {}) {
|
|
|
1887
2045
|
notes: "no pnpm-lock.yaml at site root \u2014 run convert-to-pnpm first"
|
|
1888
2046
|
};
|
|
1889
2047
|
}
|
|
1890
|
-
const pkgPath =
|
|
2048
|
+
const pkgPath = join16(site.path, "package.json");
|
|
1891
2049
|
const pkg = await readPackageJson(pkgPath);
|
|
1892
2050
|
const toAdd = [];
|
|
1893
2051
|
if (!isDeclared(pkg, PACKAGE_NAME)) {
|
|
@@ -1991,7 +2149,7 @@ import { resolve as resolve8 } from "path";
|
|
|
1991
2149
|
|
|
1992
2150
|
// src/recipes/svelte-codemods.ts
|
|
1993
2151
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
1994
|
-
import { join as
|
|
2152
|
+
import { join as join17 } from "path";
|
|
1995
2153
|
function siteLabel11(site) {
|
|
1996
2154
|
return site.name ?? site.path;
|
|
1997
2155
|
}
|
|
@@ -2013,7 +2171,7 @@ async function svelteCodemods(site) {
|
|
|
2013
2171
|
const branch = branchName("svelte-codemods");
|
|
2014
2172
|
await createBranch(site.path, branch);
|
|
2015
2173
|
for (const c of changes) {
|
|
2016
|
-
await writeFile8(
|
|
2174
|
+
await writeFile8(join17(site.path, c.rel), c.after, "utf-8");
|
|
2017
2175
|
}
|
|
2018
2176
|
const sha = await commit(
|
|
2019
2177
|
site.path,
|
|
@@ -2054,11 +2212,11 @@ async function runSvelteCodemodsCommand(site, opts) {
|
|
|
2054
2212
|
}
|
|
2055
2213
|
|
|
2056
2214
|
// src/cli/version.ts
|
|
2057
|
-
import { readFileSync } from "fs";
|
|
2058
|
-
import { join as
|
|
2215
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
2216
|
+
import { join as join18 } from "path";
|
|
2059
2217
|
function resolvePackageVersion(fromDir) {
|
|
2060
2218
|
try {
|
|
2061
|
-
const raw =
|
|
2219
|
+
const raw = readFileSync2(join18(fromDir, "..", "..", "package.json"), "utf-8");
|
|
2062
2220
|
const pkg = JSON.parse(raw);
|
|
2063
2221
|
return pkg.version ?? "unknown";
|
|
2064
2222
|
} catch {
|
|
@@ -2067,7 +2225,7 @@ function resolvePackageVersion(fromDir) {
|
|
|
2067
2225
|
}
|
|
2068
2226
|
|
|
2069
2227
|
// src/cli/bin.ts
|
|
2070
|
-
var here =
|
|
2228
|
+
var here = dirname2(fileURLToPath2(import.meta.url));
|
|
2071
2229
|
var version = resolvePackageVersion(here);
|
|
2072
2230
|
var AUDIT_DESCRIPTIONS = {
|
|
2073
2231
|
deps: "Diff site package.json against the bundled baseline version map.",
|