@reddoorla/maintenance 0.6.7 → 0.6.8
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 +272 -314
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.js +272 -314
- package/dist/index.js.map +1 -1
- package/dist/recipes/sync-configs.js +87 -43
- package/dist/recipes/sync-configs.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/bin.js
CHANGED
|
@@ -1028,6 +1028,65 @@ async function commit(cwd, message) {
|
|
|
1028
1028
|
return sha.trim();
|
|
1029
1029
|
}
|
|
1030
1030
|
|
|
1031
|
+
// src/recipes/_with-recipe.ts
|
|
1032
|
+
async function withRecipe(body) {
|
|
1033
|
+
const label = siteLabel(body.site);
|
|
1034
|
+
if (body.checkTreeFirst && !await isWorkingTreeClean(body.site.path)) {
|
|
1035
|
+
throw new Error(`refusing to run: working tree is not clean at ${body.site.path}`);
|
|
1036
|
+
}
|
|
1037
|
+
const planned = await body.plan();
|
|
1038
|
+
if (planned.kind === "noop") {
|
|
1039
|
+
return {
|
|
1040
|
+
recipe: body.name,
|
|
1041
|
+
site: label,
|
|
1042
|
+
status: "noop",
|
|
1043
|
+
commits: [],
|
|
1044
|
+
...planned.notes ? { notes: planned.notes } : {}
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
if (planned.kind === "failed") {
|
|
1048
|
+
return {
|
|
1049
|
+
recipe: body.name,
|
|
1050
|
+
site: label,
|
|
1051
|
+
status: "failed",
|
|
1052
|
+
commits: [],
|
|
1053
|
+
notes: planned.notes
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
if (!body.checkTreeFirst && !await isWorkingTreeClean(body.site.path)) {
|
|
1057
|
+
throw new Error(`refusing to run: working tree is not clean at ${body.site.path}`);
|
|
1058
|
+
}
|
|
1059
|
+
const branch = branchName(body.name);
|
|
1060
|
+
await createBranch(body.site.path, branch);
|
|
1061
|
+
const shas = [];
|
|
1062
|
+
const result = await body.apply(planned.plan, {
|
|
1063
|
+
cwd: body.site.path,
|
|
1064
|
+
branch,
|
|
1065
|
+
commit: async (msg) => {
|
|
1066
|
+
const sha = await commit(body.site.path, msg);
|
|
1067
|
+
if (sha) shas.push(sha);
|
|
1068
|
+
return sha;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
if (result.kind === "failed") {
|
|
1072
|
+
return {
|
|
1073
|
+
recipe: body.name,
|
|
1074
|
+
site: label,
|
|
1075
|
+
status: "failed",
|
|
1076
|
+
commits: shas,
|
|
1077
|
+
notes: result.notes
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
const notes = result.notes ? `${result.notes}; branch: ${branch}` : `branch: ${branch}`;
|
|
1081
|
+
return {
|
|
1082
|
+
recipe: body.name,
|
|
1083
|
+
site: label,
|
|
1084
|
+
status: shas.length > 0 ? "applied" : "noop",
|
|
1085
|
+
commits: shas,
|
|
1086
|
+
notes
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1031
1090
|
// src/recipes/sync-configs.ts
|
|
1032
1091
|
var GITIGNORE_CONFIG = "gitignore";
|
|
1033
1092
|
var ALL_CONFIG_NAMES = [
|
|
@@ -1071,48 +1130,33 @@ async function applyGitignore(cwd, plan) {
|
|
|
1071
1130
|
}
|
|
1072
1131
|
}
|
|
1073
1132
|
async function syncConfigs(site, opts = {}) {
|
|
1074
|
-
const label = siteLabel(site);
|
|
1075
1133
|
const requested = opts.which ?? ALL_TEMPLATES.map((t) => t.config).concat(GITIGNORE_CONFIG);
|
|
1076
1134
|
const templateNames = requested.filter((c) => c !== GITIGNORE_CONFIG);
|
|
1077
1135
|
const templates = templatesByName(templateNames);
|
|
1078
1136
|
const includeGitignore = requested.includes(GITIGNORE_CONFIG);
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
site:
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
if (sha) shas.push(sha);
|
|
1103
|
-
}
|
|
1104
|
-
if (gitignorePlan.kind === "apply") {
|
|
1105
|
-
await applyGitignore(site.path, gitignorePlan);
|
|
1106
|
-
const sha = await commit(site.path, `chore: sync gitignore from @reddoorla/maintenance`);
|
|
1107
|
-
if (sha) shas.push(sha);
|
|
1108
|
-
}
|
|
1109
|
-
return {
|
|
1110
|
-
recipe: "sync-configs",
|
|
1111
|
-
site: label,
|
|
1112
|
-
status: "applied",
|
|
1113
|
-
commits: shas,
|
|
1114
|
-
notes: `branch: ${branch}`
|
|
1115
|
-
};
|
|
1137
|
+
return withRecipe({
|
|
1138
|
+
name: "sync-configs",
|
|
1139
|
+
site,
|
|
1140
|
+
plan: async () => {
|
|
1141
|
+
const templateDiffs = await planTemplateDiffs(site.path, templates);
|
|
1142
|
+
const gitignorePlan = includeGitignore ? await planGitignore(site.path) : { kind: "noop" };
|
|
1143
|
+
if (templateDiffs.length === 0 && gitignorePlan.kind === "noop") {
|
|
1144
|
+
return { kind: "noop", notes: "all targeted configs already match" };
|
|
1145
|
+
}
|
|
1146
|
+
return { kind: "apply", plan: { templateDiffs, gitignorePlan } };
|
|
1147
|
+
},
|
|
1148
|
+
apply: async ({ templateDiffs, gitignorePlan }, { commit: commit2 }) => {
|
|
1149
|
+
for (const t of templateDiffs) {
|
|
1150
|
+
await writeFile3(join6(site.path, t.path), t.contents, "utf-8");
|
|
1151
|
+
await commit2(`chore: sync ${t.config} config from @reddoorla/maintenance`);
|
|
1152
|
+
}
|
|
1153
|
+
if (gitignorePlan.kind === "apply") {
|
|
1154
|
+
await applyGitignore(site.path, gitignorePlan);
|
|
1155
|
+
await commit2(`chore: sync gitignore from @reddoorla/maintenance`);
|
|
1156
|
+
}
|
|
1157
|
+
return { kind: "ok" };
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1116
1160
|
}
|
|
1117
1161
|
|
|
1118
1162
|
// src/cli/commands/sync-configs.ts
|
|
@@ -1214,62 +1258,51 @@ function upFlagsForGroup(group) {
|
|
|
1214
1258
|
return [];
|
|
1215
1259
|
}
|
|
1216
1260
|
async function bumpDeps(site, opts = {}) {
|
|
1217
|
-
const label = siteLabel(site);
|
|
1218
1261
|
const group = opts.group ?? "minor";
|
|
1219
1262
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1263
|
+
return withRecipe({
|
|
1264
|
+
name: "bump-deps",
|
|
1265
|
+
site,
|
|
1266
|
+
// pnpm install (in plan) mutates the lockfile, so the clean-tree check
|
|
1267
|
+
// MUST happen first — otherwise a desynced-lockfile resync would silently
|
|
1268
|
+
// land on top of whatever else was in the tree.
|
|
1269
|
+
checkTreeFirst: true,
|
|
1270
|
+
plan: async () => {
|
|
1271
|
+
const hasPnpmLock = await exists(join8(site.path, "pnpm-lock.yaml"));
|
|
1272
|
+
if (!hasPnpmLock) {
|
|
1273
|
+
const hasNpmLock = await exists(join8(site.path, "package-lock.json"));
|
|
1274
|
+
const hasYarnLock = await exists(join8(site.path, "yarn.lock"));
|
|
1275
|
+
if (hasNpmLock || hasYarnLock) {
|
|
1276
|
+
const competing = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
1277
|
+
return {
|
|
1278
|
+
kind: "failed",
|
|
1279
|
+
notes: `site has ${competing} but no pnpm-lock.yaml \u2014 run convert-to-pnpm first`
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
await spawn2("pnpm", ["install"], { cwd: site.path, streaming: true });
|
|
1284
|
+
const outdated = await spawn2(
|
|
1285
|
+
"pnpm",
|
|
1286
|
+
["outdated", "--json", ...outdatedFlagsForGroup(group)],
|
|
1287
|
+
{ cwd: site.path }
|
|
1288
|
+
);
|
|
1289
|
+
let parsed;
|
|
1290
|
+
try {
|
|
1291
|
+
parsed = JSON.parse(outdated.stdout || "{}");
|
|
1292
|
+
} catch {
|
|
1293
|
+
parsed = {};
|
|
1294
|
+
}
|
|
1295
|
+
if (Object.keys(parsed).length === 0) {
|
|
1296
|
+
return { kind: "noop", notes: `pnpm outdated reported nothing for group=${group}` };
|
|
1297
|
+
}
|
|
1298
|
+
return { kind: "apply", plan: { group } };
|
|
1299
|
+
},
|
|
1300
|
+
apply: async ({ group: g }, { commit: commit2, cwd }) => {
|
|
1301
|
+
await spawn2("pnpm", ["up", ...upFlagsForGroup(g)], { cwd, streaming: true });
|
|
1302
|
+
await commit2(`chore(deps): bump dependencies (${g})`);
|
|
1303
|
+
return { kind: "ok" };
|
|
1233
1304
|
}
|
|
1234
|
-
}
|
|
1235
|
-
if (!await isWorkingTreeClean(site.path)) {
|
|
1236
|
-
throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
|
|
1237
|
-
}
|
|
1238
|
-
await spawn2("pnpm", ["install"], { cwd: site.path, streaming: true });
|
|
1239
|
-
const outdated = await spawn2("pnpm", ["outdated", "--json", ...outdatedFlagsForGroup(group)], {
|
|
1240
|
-
cwd: site.path
|
|
1241
1305
|
});
|
|
1242
|
-
let parsed;
|
|
1243
|
-
try {
|
|
1244
|
-
parsed = JSON.parse(outdated.stdout || "{}");
|
|
1245
|
-
} catch {
|
|
1246
|
-
parsed = {};
|
|
1247
|
-
}
|
|
1248
|
-
const nothingToDo = Object.keys(parsed).length === 0;
|
|
1249
|
-
if (nothingToDo) {
|
|
1250
|
-
return {
|
|
1251
|
-
recipe: "bump-deps",
|
|
1252
|
-
site: label,
|
|
1253
|
-
status: "noop",
|
|
1254
|
-
commits: [],
|
|
1255
|
-
notes: `pnpm outdated reported nothing for group=${group}`
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
|
-
const branch = branchName("bump-deps");
|
|
1259
|
-
await createBranch(site.path, branch);
|
|
1260
|
-
await spawn2("pnpm", ["up", ...upFlagsForGroup(group)], {
|
|
1261
|
-
cwd: site.path,
|
|
1262
|
-
streaming: true
|
|
1263
|
-
});
|
|
1264
|
-
const sha = await commit(site.path, `chore(deps): bump dependencies (${group})`);
|
|
1265
|
-
const shas = sha ? [sha] : [];
|
|
1266
|
-
return {
|
|
1267
|
-
recipe: "bump-deps",
|
|
1268
|
-
site: label,
|
|
1269
|
-
status: shas.length > 0 ? "applied" : "noop",
|
|
1270
|
-
commits: shas,
|
|
1271
|
-
notes: `branch: ${branch}`
|
|
1272
|
-
};
|
|
1273
1306
|
}
|
|
1274
1307
|
|
|
1275
1308
|
// src/cli/commands/bump-deps.ts
|
|
@@ -1884,72 +1917,49 @@ async function alreadyOnSvelte5(cwd) {
|
|
|
1884
1917
|
}
|
|
1885
1918
|
}
|
|
1886
1919
|
async function upgradeSvelte4to5(site, opts = {}) {
|
|
1887
|
-
const label = siteLabel(site);
|
|
1888
1920
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
site.path,
|
|
1931
|
-
`refactor(svelte5): apply gotcha codemods (${codemods.filesChanged} files)`
|
|
1932
|
-
);
|
|
1933
|
-
if (sha) shas.push(sha);
|
|
1934
|
-
}
|
|
1935
|
-
await verifyMigration(site.path, spawn2);
|
|
1936
|
-
const verifySha = await commit(site.path, "chore(svelte5): pnpm install + check");
|
|
1937
|
-
if (verifySha) shas.push(verifySha);
|
|
1938
|
-
await writeMigrationSummary({
|
|
1939
|
-
cwd: site.path,
|
|
1940
|
-
filesChangedByCodemods: codemods.filesChanged,
|
|
1941
|
-
svelteMigrateRan: migrate.ran,
|
|
1942
|
-
tailwindUpgraded: tw.ran
|
|
1921
|
+
return withRecipe({
|
|
1922
|
+
name: "svelte-4-to-5",
|
|
1923
|
+
site,
|
|
1924
|
+
plan: async () => {
|
|
1925
|
+
if (await alreadyOnSvelte5(site.path)) {
|
|
1926
|
+
return { kind: "noop", notes: "site already declares svelte ^5.x" };
|
|
1927
|
+
}
|
|
1928
|
+
return { kind: "apply", plan: true };
|
|
1929
|
+
},
|
|
1930
|
+
apply: async (_plan, { commit: commit2, cwd }) => {
|
|
1931
|
+
const bumped = await bumpToSvelte5Versions(cwd);
|
|
1932
|
+
if (bumped) {
|
|
1933
|
+
await commit2("chore(svelte5): bump svelte/kit/vite/vite-plugin-svelte");
|
|
1934
|
+
}
|
|
1935
|
+
const configChanged = await migrateSvelteConfig(cwd);
|
|
1936
|
+
if (configChanged) {
|
|
1937
|
+
await commit2("refactor(svelte5): migrate svelte.config.js (drop vitePreprocess)");
|
|
1938
|
+
}
|
|
1939
|
+
const migrate = await runSvelteMigrate(cwd, spawn2);
|
|
1940
|
+
if (migrate.ran) {
|
|
1941
|
+
await commit2("refactor(svelte5): run official svelte-migrate codemod");
|
|
1942
|
+
}
|
|
1943
|
+
const tw = await upgradeTailwind(cwd, spawn2);
|
|
1944
|
+
if (tw.ran) {
|
|
1945
|
+
await commit2("chore(svelte5): tailwindcss 3 \u2192 4 upgrade");
|
|
1946
|
+
}
|
|
1947
|
+
const codemods = await applyGotchaCodemods(cwd);
|
|
1948
|
+
if (codemods.filesChanged > 0) {
|
|
1949
|
+
await commit2(`refactor(svelte5): apply gotcha codemods (${codemods.filesChanged} files)`);
|
|
1950
|
+
}
|
|
1951
|
+
await verifyMigration(cwd, spawn2);
|
|
1952
|
+
await commit2("chore(svelte5): pnpm install + check");
|
|
1953
|
+
await writeMigrationSummary({
|
|
1954
|
+
cwd,
|
|
1955
|
+
filesChangedByCodemods: codemods.filesChanged,
|
|
1956
|
+
svelteMigrateRan: migrate.ran,
|
|
1957
|
+
tailwindUpgraded: tw.ran
|
|
1958
|
+
});
|
|
1959
|
+
await commit2("docs(svelte5): add MIGRATION_SVELTE_5.md summary");
|
|
1960
|
+
return { kind: "ok" };
|
|
1961
|
+
}
|
|
1943
1962
|
});
|
|
1944
|
-
const summarySha = await commit(site.path, "docs(svelte5): add MIGRATION_SVELTE_5.md summary");
|
|
1945
|
-
if (summarySha) shas.push(summarySha);
|
|
1946
|
-
return {
|
|
1947
|
-
recipe: "svelte-4-to-5",
|
|
1948
|
-
site: label,
|
|
1949
|
-
status: shas.length > 0 ? "applied" : "noop",
|
|
1950
|
-
commits: shas,
|
|
1951
|
-
notes: `branch: ${branch}`
|
|
1952
|
-
};
|
|
1953
1963
|
}
|
|
1954
1964
|
|
|
1955
1965
|
// src/cli/commands/upgrade.ts
|
|
@@ -2025,80 +2035,55 @@ async function exists2(path) {
|
|
|
2025
2035
|
}
|
|
2026
2036
|
}
|
|
2027
2037
|
async function convertToPnpm(site, opts = {}) {
|
|
2028
|
-
const label = siteLabel(site);
|
|
2029
2038
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
2030
2039
|
const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
|
|
2031
2040
|
const pnpmLockPath = join15(site.path, "pnpm-lock.yaml");
|
|
2032
2041
|
const npmLockPath = join15(site.path, "package-lock.json");
|
|
2033
2042
|
const yarnLockPath = join15(site.path, "yarn.lock");
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2043
|
+
return withRecipe({
|
|
2044
|
+
name: "convert-to-pnpm",
|
|
2045
|
+
site,
|
|
2046
|
+
plan: async () => {
|
|
2047
|
+
if (await exists2(pnpmLockPath)) {
|
|
2048
|
+
return { kind: "noop", notes: "site already has pnpm-lock.yaml" };
|
|
2049
|
+
}
|
|
2050
|
+
const hasNpmLock = await exists2(npmLockPath);
|
|
2051
|
+
const hasYarnLock = await exists2(yarnLockPath);
|
|
2052
|
+
if (!hasNpmLock && !hasYarnLock) {
|
|
2053
|
+
return {
|
|
2054
|
+
kind: "noop",
|
|
2055
|
+
notes: "no convertible lockfile (package-lock.json or yarn.lock) at site root"
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
return { kind: "apply", plan: { hasNpmLock, hasYarnLock } };
|
|
2059
|
+
},
|
|
2060
|
+
apply: async ({ hasNpmLock, hasYarnLock }, { commit: commit2, cwd }) => {
|
|
2061
|
+
if (hasNpmLock) await rm3(npmLockPath, { force: true });
|
|
2062
|
+
if (hasYarnLock) await rm3(yarnLockPath, { force: true });
|
|
2063
|
+
const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
2064
|
+
await commit2(`chore(pnpm): remove ${sourceLock}`);
|
|
2065
|
+
const pkgPath = join15(cwd, "package.json");
|
|
2066
|
+
const pkg = await readPackageJson(pkgPath);
|
|
2067
|
+
const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
|
|
2068
|
+
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
2069
|
+
const { scripts: rewritten, changedCount } = rewriteScriptsForPnpm(
|
|
2070
|
+
pkg.scripts
|
|
2071
|
+
);
|
|
2072
|
+
if (changedCount > 0) {
|
|
2073
|
+
next.scripts = rewritten;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
await writePackageJson(pkgPath, next);
|
|
2077
|
+
await commit2("chore(pnpm): pin packageManager + rewrite npm scripts");
|
|
2078
|
+
await rm3(join15(cwd, "node_modules"), { recursive: true, force: true });
|
|
2079
|
+
const installResult = await spawn2("pnpm", ["install"], { cwd, streaming: true });
|
|
2080
|
+
if (installResult.code !== 0) {
|
|
2081
|
+
return { kind: "failed", notes: `pnpm install failed (exit ${installResult.code})` };
|
|
2082
|
+
}
|
|
2083
|
+
await commit2("chore(pnpm): add pnpm-lock.yaml");
|
|
2084
|
+
return { kind: "ok" };
|
|
2074
2085
|
}
|
|
2075
|
-
}
|
|
2076
|
-
await writePackageJson(pkgPath, next);
|
|
2077
|
-
const pkgSha = await commit(site.path, "chore(pnpm): pin packageManager + rewrite npm scripts");
|
|
2078
|
-
if (pkgSha) shas.push(pkgSha);
|
|
2079
|
-
await rm3(join15(site.path, "node_modules"), { recursive: true, force: true });
|
|
2080
|
-
const installResult = await spawn2("pnpm", ["install"], {
|
|
2081
|
-
cwd: site.path,
|
|
2082
|
-
streaming: true
|
|
2083
2086
|
});
|
|
2084
|
-
if (installResult.code !== 0) {
|
|
2085
|
-
return {
|
|
2086
|
-
recipe: "convert-to-pnpm",
|
|
2087
|
-
site: label,
|
|
2088
|
-
status: "failed",
|
|
2089
|
-
commits: shas,
|
|
2090
|
-
notes: `pnpm install failed (exit ${installResult.code}). branch ${branch} left for inspection.`
|
|
2091
|
-
};
|
|
2092
|
-
}
|
|
2093
|
-
const installSha = await commit(site.path, "chore(pnpm): add pnpm-lock.yaml");
|
|
2094
|
-
if (installSha) shas.push(installSha);
|
|
2095
|
-
return {
|
|
2096
|
-
recipe: "convert-to-pnpm",
|
|
2097
|
-
site: label,
|
|
2098
|
-
status: "applied",
|
|
2099
|
-
commits: shas,
|
|
2100
|
-
notes: `branch: ${branch}`
|
|
2101
|
-
};
|
|
2102
2087
|
}
|
|
2103
2088
|
|
|
2104
2089
|
// src/cli/commands/convert-to-pnpm.ts
|
|
@@ -2183,74 +2168,59 @@ function isDeclared(pkg, name) {
|
|
|
2183
2168
|
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
2184
2169
|
}
|
|
2185
2170
|
async function onboard(site, opts = {}) {
|
|
2186
|
-
const label = siteLabel(site);
|
|
2187
2171
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
2188
2172
|
const audits = opts.audits ?? ["lighthouse", "a11y"];
|
|
2189
2173
|
const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2174
|
+
return withRecipe({
|
|
2175
|
+
name: "onboard",
|
|
2176
|
+
site,
|
|
2177
|
+
plan: async () => {
|
|
2178
|
+
if (!await exists3(join17(site.path, "pnpm-lock.yaml"))) {
|
|
2179
|
+
return {
|
|
2180
|
+
kind: "failed",
|
|
2181
|
+
notes: "no pnpm-lock.yaml at site root \u2014 run convert-to-pnpm first"
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
const pkgPath = join17(site.path, "package.json");
|
|
2185
|
+
const pkg = await readPackageJson(pkgPath);
|
|
2186
|
+
const toAdd = [];
|
|
2187
|
+
if (!isDeclared(pkg, PACKAGE_NAME)) {
|
|
2188
|
+
toAdd.push({ name: PACKAGE_NAME, version: packageVersion });
|
|
2189
|
+
}
|
|
2190
|
+
for (const audit of audits) {
|
|
2191
|
+
for (const dep of AUDIT_DEPS[audit]) {
|
|
2192
|
+
if (!isDeclared(pkg, dep.name)) toAdd.push(dep);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
if (toAdd.length === 0) {
|
|
2196
|
+
return {
|
|
2197
|
+
kind: "noop",
|
|
2198
|
+
notes: `site already has ${PACKAGE_NAME} and audit deps (${audits.join("+")})`
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
return { kind: "apply", plan: { pkg, toAdd } };
|
|
2202
|
+
},
|
|
2203
|
+
apply: async ({ pkg, toAdd }, { commit: commit2, cwd }) => {
|
|
2204
|
+
const pkgPath = join17(cwd, "package.json");
|
|
2205
|
+
let next = pkg;
|
|
2206
|
+
for (const dep of toAdd) {
|
|
2207
|
+
next = bumpDep(next, dep.name, dep.version);
|
|
2208
|
+
}
|
|
2209
|
+
await writePackageJson(pkgPath, next);
|
|
2210
|
+
const installResult = await spawn2("pnpm", ["install"], { cwd, streaming: true });
|
|
2211
|
+
if (installResult.code !== 0) {
|
|
2212
|
+
return {
|
|
2213
|
+
kind: "failed",
|
|
2214
|
+
notes: `pnpm install failed (exit ${installResult.code})`
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
await commit2(`chore(reddoor): onboard with ${PACKAGE_NAME} ${packageVersion}`);
|
|
2218
|
+
return {
|
|
2219
|
+
kind: "ok",
|
|
2220
|
+
notes: `Added ${toAdd.length} dep(s): ${toAdd.map((d) => d.name).join(", ")}`
|
|
2221
|
+
};
|
|
2208
2222
|
}
|
|
2209
|
-
}
|
|
2210
|
-
if (toAdd.length === 0) {
|
|
2211
|
-
return {
|
|
2212
|
-
recipe: "onboard",
|
|
2213
|
-
site: label,
|
|
2214
|
-
status: "noop",
|
|
2215
|
-
commits: [],
|
|
2216
|
-
notes: `site already has ${PACKAGE_NAME} and audit deps (${audits.join("+")})`
|
|
2217
|
-
};
|
|
2218
|
-
}
|
|
2219
|
-
if (!await isWorkingTreeClean(site.path)) {
|
|
2220
|
-
throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
|
|
2221
|
-
}
|
|
2222
|
-
const branch = branchName("onboard");
|
|
2223
|
-
await createBranch(site.path, branch);
|
|
2224
|
-
let next = pkg;
|
|
2225
|
-
for (const dep of toAdd) {
|
|
2226
|
-
next = bumpDep(next, dep.name, dep.version);
|
|
2227
|
-
}
|
|
2228
|
-
await writePackageJson(pkgPath, next);
|
|
2229
|
-
const installResult = await spawn2("pnpm", ["install"], {
|
|
2230
|
-
cwd: site.path,
|
|
2231
|
-
streaming: true
|
|
2232
2223
|
});
|
|
2233
|
-
if (installResult.code !== 0) {
|
|
2234
|
-
return {
|
|
2235
|
-
recipe: "onboard",
|
|
2236
|
-
site: label,
|
|
2237
|
-
status: "failed",
|
|
2238
|
-
commits: [],
|
|
2239
|
-
notes: `pnpm install failed (exit ${installResult.code}). branch ${branch} left for inspection.`
|
|
2240
|
-
};
|
|
2241
|
-
}
|
|
2242
|
-
const sha = await commit(
|
|
2243
|
-
site.path,
|
|
2244
|
-
`chore(reddoor): onboard with ${PACKAGE_NAME} ${packageVersion}`
|
|
2245
|
-
);
|
|
2246
|
-
const shas = sha ? [sha] : [];
|
|
2247
|
-
return {
|
|
2248
|
-
recipe: "onboard",
|
|
2249
|
-
site: label,
|
|
2250
|
-
status: "applied",
|
|
2251
|
-
commits: shas,
|
|
2252
|
-
notes: `branch: ${branch}. Added ${toAdd.length} dep(s): ${toAdd.map((d) => d.name).join(", ")}`
|
|
2253
|
-
};
|
|
2254
2224
|
}
|
|
2255
2225
|
|
|
2256
2226
|
// src/cli/commands/onboard.ts
|
|
@@ -2302,36 +2272,24 @@ import { resolve as resolve8 } from "path";
|
|
|
2302
2272
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
2303
2273
|
import { join as join18 } from "path";
|
|
2304
2274
|
async function svelteCodemods(site) {
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
}
|
|
2324
|
-
const sha = await commit(
|
|
2325
|
-
site.path,
|
|
2326
|
-
`refactor(svelte5): apply codemods (${changes.length} files)`
|
|
2327
|
-
);
|
|
2328
|
-
return {
|
|
2329
|
-
recipe: "svelte-codemods",
|
|
2330
|
-
site: label,
|
|
2331
|
-
status: "applied",
|
|
2332
|
-
commits: sha ? [sha] : [],
|
|
2333
|
-
notes: `branch: ${branch}`
|
|
2334
|
-
};
|
|
2275
|
+
return withRecipe({
|
|
2276
|
+
name: "svelte-codemods",
|
|
2277
|
+
site,
|
|
2278
|
+
plan: async () => {
|
|
2279
|
+
const changes = await planGotchaCodemods(site.path);
|
|
2280
|
+
if (changes.length === 0) {
|
|
2281
|
+
return { kind: "noop", notes: "no codemod targets matched" };
|
|
2282
|
+
}
|
|
2283
|
+
return { kind: "apply", plan: changes };
|
|
2284
|
+
},
|
|
2285
|
+
apply: async (changes, { commit: commit2, cwd }) => {
|
|
2286
|
+
for (const c of changes) {
|
|
2287
|
+
await writeFile8(join18(cwd, c.rel), c.after, "utf-8");
|
|
2288
|
+
}
|
|
2289
|
+
await commit2(`refactor(svelte5): apply codemods (${changes.length} files)`);
|
|
2290
|
+
return { kind: "ok" };
|
|
2291
|
+
}
|
|
2292
|
+
});
|
|
2335
2293
|
}
|
|
2336
2294
|
|
|
2337
2295
|
// src/cli/commands/svelte-codemods.ts
|