@reddoorla/maintenance 0.6.5 → 0.6.7
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 +221 -208
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +12 -21
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +162 -127
- package/dist/index.js.map +1 -1
- package/dist/recipes/sync-configs.d.ts +1 -1
- package/dist/recipes/sync-configs.js +19 -5
- package/dist/recipes/sync-configs.js.map +1 -1
- package/dist/{sync-configs-DRmSAnfV.d.ts → sync-configs-pMPW4wTq.d.ts} +7 -1
- package/dist/util/git.js +2 -3
- package/dist/util/git.js.map +1 -1
- package/dist/util/pkg.js +11 -1
- package/dist/util/pkg.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,6 +31,11 @@ var defaultSpawn = (cmd, args, opts = {}) => new Promise((resolve, reject) => {
|
|
|
31
31
|
import { readFile } from "fs/promises";
|
|
32
32
|
import { join } from "path";
|
|
33
33
|
|
|
34
|
+
// src/util/site.ts
|
|
35
|
+
function siteLabel(site) {
|
|
36
|
+
return site.name ?? site.path;
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
// src/configs/baseline-versions.ts
|
|
35
40
|
var baselineVersions = {
|
|
36
41
|
// SvelteKit core
|
|
@@ -71,9 +76,6 @@ var baselineVersions = {
|
|
|
71
76
|
};
|
|
72
77
|
|
|
73
78
|
// src/audits/deps.ts
|
|
74
|
-
function siteLabel(site) {
|
|
75
|
-
return site.name ?? site.path;
|
|
76
|
-
}
|
|
77
79
|
function stripCaret(range) {
|
|
78
80
|
return range.replace(/^[\^~]/, "");
|
|
79
81
|
}
|
|
@@ -146,9 +148,6 @@ import { check as prettierCheck, resolveConfig as prettierResolveConfig } from "
|
|
|
146
148
|
import { glob } from "tinyglobby";
|
|
147
149
|
var TARGET_GLOBS = ["**/*.{ts,js,svelte}"];
|
|
148
150
|
var IGNORE = ["node_modules/**", "dist/**", ".svelte-kit/**", "build/**", ".netlify/**"];
|
|
149
|
-
function siteLabel2(site) {
|
|
150
|
-
return site.name ?? site.path;
|
|
151
|
-
}
|
|
152
151
|
async function listFiles(cwd) {
|
|
153
152
|
return glob(TARGET_GLOBS, { cwd, ignore: IGNORE, absolute: false });
|
|
154
153
|
}
|
|
@@ -158,7 +157,7 @@ async function lintAudit(ctx) {
|
|
|
158
157
|
if (!existsSync(configPath)) {
|
|
159
158
|
return {
|
|
160
159
|
audit: "lint",
|
|
161
|
-
site:
|
|
160
|
+
site: siteLabel(site),
|
|
162
161
|
status: "skip",
|
|
163
162
|
summary: "no eslint config at site root"
|
|
164
163
|
};
|
|
@@ -184,7 +183,7 @@ async function lintAudit(ctx) {
|
|
|
184
183
|
const summary = status === "pass" ? `lint clean across ${relFiles.length} files` : `${eslintErrors} eslint errors, ${eslintWarnings} warnings, ${prettierUnformatted.length} unformatted`;
|
|
185
184
|
return {
|
|
186
185
|
audit: "lint",
|
|
187
|
-
site:
|
|
186
|
+
site: siteLabel(site),
|
|
188
187
|
status,
|
|
189
188
|
summary,
|
|
190
189
|
details: {
|
|
@@ -197,9 +196,6 @@ async function lintAudit(ctx) {
|
|
|
197
196
|
}
|
|
198
197
|
|
|
199
198
|
// src/audits/security.ts
|
|
200
|
-
function siteLabel3(site) {
|
|
201
|
-
return site.name ?? site.path;
|
|
202
|
-
}
|
|
203
199
|
function classify(v) {
|
|
204
200
|
if (v.critical > 0 || v.high > 0) return "fail";
|
|
205
201
|
if (v.moderate > 0 || v.low > 0) return "warn";
|
|
@@ -285,7 +281,8 @@ async function runAuditTool(spawn2, cmd, args, cwd) {
|
|
|
285
281
|
if (errEnvelope && typeof errEnvelope === "object") {
|
|
286
282
|
return { kind: "error", reason: errEnvelope.code ?? "error envelope returned" };
|
|
287
283
|
}
|
|
288
|
-
|
|
284
|
+
const vulnsMeta = parsed.metadata?.vulnerabilities;
|
|
285
|
+
if (!vulnsMeta || Object.keys(vulnsMeta).length === 0) {
|
|
289
286
|
return { kind: "error", reason: "no metadata.vulnerabilities in output" };
|
|
290
287
|
}
|
|
291
288
|
return { kind: "ok", parsed };
|
|
@@ -293,7 +290,7 @@ async function runAuditTool(spawn2, cmd, args, cwd) {
|
|
|
293
290
|
async function securityAudit(ctx) {
|
|
294
291
|
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
295
292
|
const site = ctx.site;
|
|
296
|
-
const label =
|
|
293
|
+
const label = siteLabel(site);
|
|
297
294
|
let used = "pnpm audit";
|
|
298
295
|
let result = await runAuditTool(spawn2, "pnpm", ["audit", "--json", "--prod"], site.path);
|
|
299
296
|
if (result.kind !== "ok") {
|
|
@@ -374,9 +371,6 @@ var lighthouseConfig = {
|
|
|
374
371
|
};
|
|
375
372
|
|
|
376
373
|
// src/audits/lighthouse.ts
|
|
377
|
-
function siteLabel4(site) {
|
|
378
|
-
return site.name ?? site.path;
|
|
379
|
-
}
|
|
380
374
|
async function readJsonMaybe(path) {
|
|
381
375
|
try {
|
|
382
376
|
const raw = await readFile3(path, "utf-8");
|
|
@@ -414,7 +408,7 @@ function messageForAssertion(a) {
|
|
|
414
408
|
async function lighthouseAudit(ctx) {
|
|
415
409
|
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
416
410
|
const site = ctx.site;
|
|
417
|
-
const label =
|
|
411
|
+
const label = siteLabel(site);
|
|
418
412
|
const configDir = await mkdtemp(join3(tmpdir(), "reddoor-lhci-"));
|
|
419
413
|
const configPath = join3(configDir, "lighthouserc.json");
|
|
420
414
|
await writeFile(configPath, JSON.stringify(lighthouseConfig), "utf-8");
|
|
@@ -512,9 +506,6 @@ var playwrightA11yConfig = defineConfig({
|
|
|
512
506
|
|
|
513
507
|
// src/audits/a11y.ts
|
|
514
508
|
var RESULTS_REL = ".reddoor-a11y/results.json";
|
|
515
|
-
function siteLabel5(site) {
|
|
516
|
-
return site.name ?? site.path;
|
|
517
|
-
}
|
|
518
509
|
async function readJsonMaybe2(path) {
|
|
519
510
|
try {
|
|
520
511
|
const raw = await readFile4(path, "utf-8");
|
|
@@ -572,7 +563,7 @@ test("a11y across configured routes", async ({ page }) => {
|
|
|
572
563
|
async function a11yAudit(ctx) {
|
|
573
564
|
const spawn2 = ctx.spawn ?? defaultSpawn;
|
|
574
565
|
const site = ctx.site;
|
|
575
|
-
const label =
|
|
566
|
+
const label = siteLabel(site);
|
|
576
567
|
const specDir = await mkdtemp2(join4(tmpdir2(), "reddoor-a11y-spec-"));
|
|
577
568
|
const specPath = join4(specDir, "a11y.spec.ts");
|
|
578
569
|
await writeFile2(specPath, buildSpec(), "utf-8");
|
|
@@ -820,9 +811,8 @@ async function git(cwd, args) {
|
|
|
820
811
|
return exec("git", args, { cwd, env: process.env });
|
|
821
812
|
}
|
|
822
813
|
function branchName(recipe, when = /* @__PURE__ */ new Date()) {
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
return `maint/${recipe}-${trimmed}`;
|
|
814
|
+
const compact = when.toISOString().replace(/[-:.]/g, "");
|
|
815
|
+
return `maint/${recipe}-${compact}`;
|
|
826
816
|
}
|
|
827
817
|
async function isWorkingTreeClean(cwd) {
|
|
828
818
|
const { stdout } = await git(cwd, ["status", "--porcelain"]);
|
|
@@ -853,9 +843,6 @@ async function commit(cwd, message) {
|
|
|
853
843
|
|
|
854
844
|
// src/recipes/sync-configs.ts
|
|
855
845
|
var GITIGNORE_CONFIG = "gitignore";
|
|
856
|
-
function siteLabel6(site) {
|
|
857
|
-
return site.name ?? site.path;
|
|
858
|
-
}
|
|
859
846
|
async function readMaybe(path) {
|
|
860
847
|
try {
|
|
861
848
|
return await readFile5(path, "utf-8");
|
|
@@ -886,7 +873,7 @@ async function applyGitignore(cwd, plan) {
|
|
|
886
873
|
}
|
|
887
874
|
}
|
|
888
875
|
async function syncConfigs(site, opts = {}) {
|
|
889
|
-
const label =
|
|
876
|
+
const label = siteLabel(site);
|
|
890
877
|
const requested = opts.which ?? ALL_TEMPLATES.map((t) => t.config).concat(GITIGNORE_CONFIG);
|
|
891
878
|
const templateNames = requested.filter((c) => c !== GITIGNORE_CONFIG);
|
|
892
879
|
const templates = templatesByName(templateNames);
|
|
@@ -931,8 +918,15 @@ async function syncConfigs(site, opts = {}) {
|
|
|
931
918
|
}
|
|
932
919
|
|
|
933
920
|
// src/recipes/bump-deps.ts
|
|
934
|
-
|
|
935
|
-
|
|
921
|
+
import { stat } from "fs/promises";
|
|
922
|
+
import { join as join6 } from "path";
|
|
923
|
+
async function exists(path) {
|
|
924
|
+
try {
|
|
925
|
+
await stat(path);
|
|
926
|
+
return true;
|
|
927
|
+
} catch {
|
|
928
|
+
return false;
|
|
929
|
+
}
|
|
936
930
|
}
|
|
937
931
|
function outdatedFlagsForGroup(group) {
|
|
938
932
|
if (group === "major") return ["--latest"];
|
|
@@ -944,9 +938,27 @@ function upFlagsForGroup(group) {
|
|
|
944
938
|
return [];
|
|
945
939
|
}
|
|
946
940
|
async function bumpDeps(site, opts = {}) {
|
|
947
|
-
const label =
|
|
941
|
+
const label = siteLabel(site);
|
|
948
942
|
const group = opts.group ?? "minor";
|
|
949
943
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
944
|
+
const hasPnpmLock = await exists(join6(site.path, "pnpm-lock.yaml"));
|
|
945
|
+
if (!hasPnpmLock) {
|
|
946
|
+
const hasNpmLock = await exists(join6(site.path, "package-lock.json"));
|
|
947
|
+
const hasYarnLock = await exists(join6(site.path, "yarn.lock"));
|
|
948
|
+
if (hasNpmLock || hasYarnLock) {
|
|
949
|
+
const competing = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
950
|
+
return {
|
|
951
|
+
recipe: "bump-deps",
|
|
952
|
+
site: label,
|
|
953
|
+
status: "failed",
|
|
954
|
+
commits: [],
|
|
955
|
+
notes: `site has ${competing} but no pnpm-lock.yaml \u2014 run convert-to-pnpm first`
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (!await isWorkingTreeClean(site.path)) {
|
|
960
|
+
throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
|
|
961
|
+
}
|
|
950
962
|
await spawn2("pnpm", ["install"], { cwd: site.path, streaming: true });
|
|
951
963
|
const outdated = await spawn2("pnpm", ["outdated", "--json", ...outdatedFlagsForGroup(group)], {
|
|
952
964
|
cwd: site.path
|
|
@@ -967,9 +979,6 @@ async function bumpDeps(site, opts = {}) {
|
|
|
967
979
|
notes: `pnpm outdated reported nothing for group=${group}`
|
|
968
980
|
};
|
|
969
981
|
}
|
|
970
|
-
if (!await isWorkingTreeClean(site.path)) {
|
|
971
|
-
throw new Error(`refusing to run: working tree is not clean at ${site.path}`);
|
|
972
|
-
}
|
|
973
982
|
const branch = branchName("bump-deps");
|
|
974
983
|
await createBranch(site.path, branch);
|
|
975
984
|
await spawn2("pnpm", ["up", ...upFlagsForGroup(group)], {
|
|
@@ -988,7 +997,7 @@ async function bumpDeps(site, opts = {}) {
|
|
|
988
997
|
}
|
|
989
998
|
|
|
990
999
|
// src/recipes/svelte-5/index.ts
|
|
991
|
-
import { join as
|
|
1000
|
+
import { join as join12 } from "path";
|
|
992
1001
|
|
|
993
1002
|
// src/util/pkg.ts
|
|
994
1003
|
import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
@@ -996,8 +1005,18 @@ async function readPackageJson(path) {
|
|
|
996
1005
|
const raw = await readFile6(path, "utf-8");
|
|
997
1006
|
return JSON.parse(raw);
|
|
998
1007
|
}
|
|
1008
|
+
function detectIndentFromContent(raw) {
|
|
1009
|
+
const match = raw.match(/\n([ \t]+)"/);
|
|
1010
|
+
return match ? match[1] ?? " " : " ";
|
|
1011
|
+
}
|
|
999
1012
|
async function writePackageJson(path, pkg) {
|
|
1000
|
-
|
|
1013
|
+
let indent = " ";
|
|
1014
|
+
try {
|
|
1015
|
+
const existing = await readFile6(path, "utf-8");
|
|
1016
|
+
indent = detectIndentFromContent(existing);
|
|
1017
|
+
} catch {
|
|
1018
|
+
}
|
|
1019
|
+
const content = JSON.stringify(pkg, null, indent) + "\n";
|
|
1001
1020
|
await writeFile4(path, content, "utf-8");
|
|
1002
1021
|
}
|
|
1003
1022
|
function bumpDep(pkg, name, version, opts = {}) {
|
|
@@ -1027,7 +1046,7 @@ function bumpDep(pkg, name, version, opts = {}) {
|
|
|
1027
1046
|
}
|
|
1028
1047
|
|
|
1029
1048
|
// src/recipes/svelte-5/step-bump-versions.ts
|
|
1030
|
-
import { join as
|
|
1049
|
+
import { join as join7 } from "path";
|
|
1031
1050
|
var SVELTE_5_VERSIONS = {
|
|
1032
1051
|
svelte: "^5.55.5",
|
|
1033
1052
|
"@sveltejs/kit": "^2.59.0",
|
|
@@ -1040,7 +1059,7 @@ var SVELTE_5_VERSIONS = {
|
|
|
1040
1059
|
"typescript-svelte-plugin": "^0.3.52"
|
|
1041
1060
|
};
|
|
1042
1061
|
async function bumpToSvelte5Versions(cwd) {
|
|
1043
|
-
const pkgPath =
|
|
1062
|
+
const pkgPath = join7(cwd, "package.json");
|
|
1044
1063
|
const pkg = await readPackageJson(pkgPath);
|
|
1045
1064
|
let next = pkg;
|
|
1046
1065
|
for (const [name, version] of Object.entries(SVELTE_5_VERSIONS)) {
|
|
@@ -1053,7 +1072,7 @@ async function bumpToSvelte5Versions(cwd) {
|
|
|
1053
1072
|
|
|
1054
1073
|
// src/recipes/svelte-5/step-svelte-config.ts
|
|
1055
1074
|
import { readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
|
|
1056
|
-
import { join as
|
|
1075
|
+
import { join as join8 } from "path";
|
|
1057
1076
|
var VITE_PLUGIN_PKG = "@sveltejs/vite-plugin-svelte";
|
|
1058
1077
|
var IMPORT_FROM_VITE_PLUGIN = new RegExp(
|
|
1059
1078
|
String.raw`^import\s+\{\s*([^}]+?)\s*\}\s+from\s+["']` + VITE_PLUGIN_PKG.replace(/[/]/g, "\\/") + String.raw`["'];?[ \t]*\n`,
|
|
@@ -1094,7 +1113,7 @@ function dropPreprocessKey(source) {
|
|
|
1094
1113
|
return source.slice(0, m.index) + source.slice(tailIdx).replace(new RegExp(`^${indent}\\n`), "");
|
|
1095
1114
|
}
|
|
1096
1115
|
async function migrateSvelteConfig(cwd) {
|
|
1097
|
-
const path =
|
|
1116
|
+
const path = join8(cwd, "svelte.config.js");
|
|
1098
1117
|
let src;
|
|
1099
1118
|
try {
|
|
1100
1119
|
src = await readFile7(path, "utf-8");
|
|
@@ -1131,9 +1150,9 @@ async function runSvelteMigrate(cwd, spawn2 = defaultSpawn) {
|
|
|
1131
1150
|
}
|
|
1132
1151
|
|
|
1133
1152
|
// src/recipes/svelte-5/step-tailwind-upgrade.ts
|
|
1134
|
-
import { join as
|
|
1153
|
+
import { join as join9 } from "path";
|
|
1135
1154
|
async function upgradeTailwind(cwd, spawn2 = defaultSpawn) {
|
|
1136
|
-
const pkg = await readPackageJson(
|
|
1155
|
+
const pkg = await readPackageJson(join9(cwd, "package.json"));
|
|
1137
1156
|
const tailwindVersion = pkg.devDependencies?.tailwindcss ?? pkg.dependencies?.tailwindcss;
|
|
1138
1157
|
if (!tailwindVersion) return { ran: false, reason: "tailwindcss not installed" };
|
|
1139
1158
|
if (/^\^?4\./.test(tailwindVersion)) return { ran: false, reason: "already on tailwind 4.x" };
|
|
@@ -1155,12 +1174,40 @@ async function upgradeTailwind(cwd, spawn2 = defaultSpawn) {
|
|
|
1155
1174
|
|
|
1156
1175
|
// src/recipes/svelte-5/step-gotchas.ts
|
|
1157
1176
|
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
1158
|
-
import { join as
|
|
1177
|
+
import { join as join10 } from "path";
|
|
1159
1178
|
import { glob as glob2 } from "tinyglobby";
|
|
1160
1179
|
|
|
1161
1180
|
// src/recipes/svelte-5/codemods/on-event-to-handler.ts
|
|
1162
1181
|
var SCRIPT_BLOCK = /<script\b[^>]*>[\s\S]*?<\/script>/g;
|
|
1163
1182
|
var SIMPLE_ON_EVENT = /\bon:([a-z]+)(?=\s*=)/g;
|
|
1183
|
+
var MODIFIER_EVENT = /\bon:[a-z]+\|[a-zA-Z]+(?=\s*=)/g;
|
|
1184
|
+
function flagEventModifiers(source) {
|
|
1185
|
+
const insertions = [];
|
|
1186
|
+
let m;
|
|
1187
|
+
MODIFIER_EVENT.lastIndex = 0;
|
|
1188
|
+
while ((m = MODIFIER_EVENT.exec(source)) !== null) {
|
|
1189
|
+
const tagStart = source.lastIndexOf("<", m.index);
|
|
1190
|
+
if (tagStart === -1) continue;
|
|
1191
|
+
const prevLineEnd = tagStart - 1;
|
|
1192
|
+
if (prevLineEnd >= 0) {
|
|
1193
|
+
const prevLineStart = source.lastIndexOf("\n", prevLineEnd - 1) + 1;
|
|
1194
|
+
const prevLine = source.slice(prevLineStart, prevLineEnd + 1);
|
|
1195
|
+
if (/<!--\s*@migration-task/.test(prevLine)) continue;
|
|
1196
|
+
}
|
|
1197
|
+
const lineStart = source.lastIndexOf("\n", tagStart - 1) + 1;
|
|
1198
|
+
const indent = source.slice(lineStart, tagStart);
|
|
1199
|
+
const safeIndent = /^[ \t]*$/.test(indent) ? indent : "";
|
|
1200
|
+
insertions.push({ tagStart, indent: safeIndent, modifier: m[0] });
|
|
1201
|
+
}
|
|
1202
|
+
let out = source;
|
|
1203
|
+
for (let i = insertions.length - 1; i >= 0; i--) {
|
|
1204
|
+
const { tagStart, indent, modifier } = insertions[i];
|
|
1205
|
+
const comment = `<!-- @migration-task: Svelte 5 removed event modifier syntax (\`${modifier}\`). Rewrite inline, e.g. onclick={(e) => { e.preventDefault(); ... }}. -->
|
|
1206
|
+
${indent}`;
|
|
1207
|
+
out = out.slice(0, tagStart) + comment + out.slice(tagStart);
|
|
1208
|
+
}
|
|
1209
|
+
return out;
|
|
1210
|
+
}
|
|
1164
1211
|
function onEventToHandler(source) {
|
|
1165
1212
|
const masked = [];
|
|
1166
1213
|
const placeholder = (i) => ` SCRIPT_${i} `;
|
|
@@ -1168,8 +1215,9 @@ function onEventToHandler(source) {
|
|
|
1168
1215
|
masked.push(match);
|
|
1169
1216
|
return placeholder(masked.length - 1);
|
|
1170
1217
|
});
|
|
1171
|
-
|
|
1172
|
-
|
|
1218
|
+
let processed = intermediate.replace(SIMPLE_ON_EVENT, (_full, name) => `on${name}`);
|
|
1219
|
+
processed = flagEventModifiers(processed);
|
|
1220
|
+
let out = processed;
|
|
1173
1221
|
masked.forEach((blk, i) => {
|
|
1174
1222
|
out = out.replace(placeholder(i), blk);
|
|
1175
1223
|
});
|
|
@@ -1219,6 +1267,22 @@ function exportLetToProps(source) {
|
|
|
1219
1267
|
return source.replace(SCRIPT_BLOCK2, (full) => full.replace(inner, body));
|
|
1220
1268
|
}
|
|
1221
1269
|
|
|
1270
|
+
// src/util/svelte-source.ts
|
|
1271
|
+
function findStringEnd(source, openIdx) {
|
|
1272
|
+
const quote = source[openIdx];
|
|
1273
|
+
let i = openIdx + 1;
|
|
1274
|
+
while (i < source.length) {
|
|
1275
|
+
const ch = source[i];
|
|
1276
|
+
if (ch === "\\") {
|
|
1277
|
+
i += 2;
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (ch === quote) return i;
|
|
1281
|
+
i++;
|
|
1282
|
+
}
|
|
1283
|
+
return -1;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1222
1286
|
// src/recipes/svelte-5/codemods/dollar-restprops.ts
|
|
1223
1287
|
function removeInterfaceBlock(source) {
|
|
1224
1288
|
const re = /^\s*interface\s+\$\$Props\s*\{/m;
|
|
@@ -1242,20 +1306,6 @@ function removeInterfaceBlock(source) {
|
|
|
1242
1306
|
out = out.slice(0, match.index) + out.slice(endIdx);
|
|
1243
1307
|
}
|
|
1244
1308
|
}
|
|
1245
|
-
function findStringEnd(source, openIdx) {
|
|
1246
|
-
const quote = source[openIdx];
|
|
1247
|
-
let i = openIdx + 1;
|
|
1248
|
-
while (i < source.length) {
|
|
1249
|
-
const ch = source[i];
|
|
1250
|
-
if (ch === "\\") {
|
|
1251
|
-
i += 2;
|
|
1252
|
-
continue;
|
|
1253
|
-
}
|
|
1254
|
-
if (ch === quote) return i;
|
|
1255
|
-
i++;
|
|
1256
|
-
}
|
|
1257
|
-
return -1;
|
|
1258
|
-
}
|
|
1259
1309
|
function maskStringLiterals(source) {
|
|
1260
1310
|
const strings = [];
|
|
1261
1311
|
let out = "";
|
|
@@ -1332,7 +1382,8 @@ function stateEffectSyncToDerived(source) {
|
|
|
1332
1382
|
|
|
1333
1383
|
// src/recipes/svelte-5/codemods/dollar-props-class.ts
|
|
1334
1384
|
var PROPS_DESTRUCTURE = /let\s*\{([\s\S]*?)\}(\s*:\s*\{([\s\S]*?)\})?\s*=\s*\$props\(\)/;
|
|
1335
|
-
var
|
|
1385
|
+
var HAS_DOLLAR_PROPS_CLASS = /\$\$props\.class\b/;
|
|
1386
|
+
var DOLLAR_PROPS_CLASS_GLOBAL = /\$\$props\.class\b/g;
|
|
1336
1387
|
var DOLLAR_PROPS_ANY = /\$\$props\b/;
|
|
1337
1388
|
var SCRIPT_BLOCK4 = /<script\b[^>]*>[\s\S]*?<\/script>/g;
|
|
1338
1389
|
var MIGRATION_TASK = /^<!--\s*@migration-task[\s\S]*?-->\s*\n?/gm;
|
|
@@ -1354,8 +1405,7 @@ function restoreScripts(masked, blocks) {
|
|
|
1354
1405
|
}
|
|
1355
1406
|
function dollarPropsClass(source) {
|
|
1356
1407
|
const { masked } = maskScripts(source);
|
|
1357
|
-
if (!
|
|
1358
|
-
DOLLAR_PROPS_CLASS.lastIndex = 0;
|
|
1408
|
+
if (!HAS_DOLLAR_PROPS_CLASS.test(masked)) return source;
|
|
1359
1409
|
if (!PROPS_DESTRUCTURE.test(source)) return source;
|
|
1360
1410
|
let updated = source.replace(PROPS_DESTRUCTURE, (full, body, typeAnno, typeBody) => {
|
|
1361
1411
|
if (/\bclass\s*:/.test(body)) return full;
|
|
@@ -1369,7 +1419,7 @@ function dollarPropsClass(source) {
|
|
|
1369
1419
|
return `let { ${newBody} } = $props()`;
|
|
1370
1420
|
});
|
|
1371
1421
|
const reMasked = maskScripts(updated);
|
|
1372
|
-
const templateRewritten = reMasked.masked.replace(
|
|
1422
|
+
const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS_GLOBAL, IDENT);
|
|
1373
1423
|
updated = restoreScripts(templateRewritten, reMasked.blocks);
|
|
1374
1424
|
const stripped = updated.replace(MIGRATION_TASK, "");
|
|
1375
1425
|
if (!DOLLAR_PROPS_ANY.test(stripped)) {
|
|
@@ -1388,7 +1438,7 @@ function findMatchingClose(source, openIdx) {
|
|
|
1388
1438
|
while (i < source.length) {
|
|
1389
1439
|
const ch = source[i];
|
|
1390
1440
|
if (ch === '"' || ch === "'" || ch === "`") {
|
|
1391
|
-
const closeStr =
|
|
1441
|
+
const closeStr = findStringEnd(source, i);
|
|
1392
1442
|
if (closeStr === -1) return -1;
|
|
1393
1443
|
i = closeStr + 1;
|
|
1394
1444
|
continue;
|
|
@@ -1402,20 +1452,6 @@ function findMatchingClose(source, openIdx) {
|
|
|
1402
1452
|
}
|
|
1403
1453
|
return -1;
|
|
1404
1454
|
}
|
|
1405
|
-
function findStringClose(source, openIdx) {
|
|
1406
|
-
const quote = source[openIdx];
|
|
1407
|
-
let i = openIdx + 1;
|
|
1408
|
-
while (i < source.length) {
|
|
1409
|
-
const ch = source[i];
|
|
1410
|
-
if (ch === "\\") {
|
|
1411
|
-
i += 2;
|
|
1412
|
-
continue;
|
|
1413
|
-
}
|
|
1414
|
-
if (ch === quote) return i;
|
|
1415
|
-
i++;
|
|
1416
|
-
}
|
|
1417
|
-
return -1;
|
|
1418
|
-
}
|
|
1419
1455
|
var MIGRATION_MARKER = "// @migration-task: $effect won't trigger UI updates on plain `let` bindings \u2014 refine mutated locals to $state or split into per-variable $derived.";
|
|
1420
1456
|
function transformBlocks(body) {
|
|
1421
1457
|
const out = [];
|
|
@@ -1470,7 +1506,7 @@ async function planGotchaCodemods(cwd) {
|
|
|
1470
1506
|
const changes = [];
|
|
1471
1507
|
const relPaths = await glob2(SVELTE_GLOBS, { cwd, ignore: IGNORE2, absolute: false });
|
|
1472
1508
|
for (const rel of relPaths) {
|
|
1473
|
-
const path =
|
|
1509
|
+
const path = join10(cwd, rel);
|
|
1474
1510
|
const before = await readFile8(path, "utf-8");
|
|
1475
1511
|
const after = CODEMODS.reduce((s, fn) => fn(s), before);
|
|
1476
1512
|
if (after !== before) changes.push({ rel, after });
|
|
@@ -1480,7 +1516,7 @@ async function planGotchaCodemods(cwd) {
|
|
|
1480
1516
|
async function applyGotchaCodemods(cwd) {
|
|
1481
1517
|
const changes = await planGotchaCodemods(cwd);
|
|
1482
1518
|
for (const c of changes) {
|
|
1483
|
-
await writeFile6(
|
|
1519
|
+
await writeFile6(join10(cwd, c.rel), c.after, "utf-8");
|
|
1484
1520
|
}
|
|
1485
1521
|
return { filesChanged: changes.length };
|
|
1486
1522
|
}
|
|
@@ -1504,7 +1540,7 @@ async function verifyMigration(cwd, spawn2 = defaultSpawn) {
|
|
|
1504
1540
|
|
|
1505
1541
|
// src/recipes/svelte-5/step-summary.ts
|
|
1506
1542
|
import { writeFile as writeFile7 } from "fs/promises";
|
|
1507
|
-
import { join as
|
|
1543
|
+
import { join as join11 } from "path";
|
|
1508
1544
|
async function writeMigrationSummary(input) {
|
|
1509
1545
|
const lines = [
|
|
1510
1546
|
`# Svelte 4 \u2192 5 migration summary`,
|
|
@@ -1521,18 +1557,15 @@ async function writeMigrationSummary(input) {
|
|
|
1521
1557
|
`- Verify Playwright a11y tests still pass.`
|
|
1522
1558
|
];
|
|
1523
1559
|
const content = lines.join("\n") + "\n";
|
|
1524
|
-
const path =
|
|
1560
|
+
const path = join11(input.cwd, "MIGRATION_SVELTE_5.md");
|
|
1525
1561
|
await writeFile7(path, content, "utf-8");
|
|
1526
1562
|
return path;
|
|
1527
1563
|
}
|
|
1528
1564
|
|
|
1529
1565
|
// src/recipes/svelte-5/index.ts
|
|
1530
|
-
function siteLabel8(site) {
|
|
1531
|
-
return site.name ?? site.path;
|
|
1532
|
-
}
|
|
1533
1566
|
async function alreadyOnSvelte5(cwd) {
|
|
1534
1567
|
try {
|
|
1535
|
-
const pkg = await readPackageJson(
|
|
1568
|
+
const pkg = await readPackageJson(join12(cwd, "package.json"));
|
|
1536
1569
|
const v = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte;
|
|
1537
1570
|
return !!v && /^\^?5\./.test(v);
|
|
1538
1571
|
} catch {
|
|
@@ -1540,7 +1573,7 @@ async function alreadyOnSvelte5(cwd) {
|
|
|
1540
1573
|
}
|
|
1541
1574
|
}
|
|
1542
1575
|
async function upgradeSvelte4to5(site, opts = {}) {
|
|
1543
|
-
const label =
|
|
1576
|
+
const label = siteLabel(site);
|
|
1544
1577
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1545
1578
|
if (await alreadyOnSvelte5(site.path)) {
|
|
1546
1579
|
return {
|
|
@@ -1610,12 +1643,9 @@ async function upgradeSvelte4to5(site, opts = {}) {
|
|
|
1610
1643
|
|
|
1611
1644
|
// src/recipes/svelte-codemods.ts
|
|
1612
1645
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
1613
|
-
import { join as
|
|
1614
|
-
function siteLabel9(site) {
|
|
1615
|
-
return site.name ?? site.path;
|
|
1616
|
-
}
|
|
1646
|
+
import { join as join13 } from "path";
|
|
1617
1647
|
async function svelteCodemods(site) {
|
|
1618
|
-
const label =
|
|
1648
|
+
const label = siteLabel(site);
|
|
1619
1649
|
const changes = await planGotchaCodemods(site.path);
|
|
1620
1650
|
if (changes.length === 0) {
|
|
1621
1651
|
return {
|
|
@@ -1632,7 +1662,7 @@ async function svelteCodemods(site) {
|
|
|
1632
1662
|
const branch = branchName("svelte-codemods");
|
|
1633
1663
|
await createBranch(site.path, branch);
|
|
1634
1664
|
for (const c of changes) {
|
|
1635
|
-
await writeFile8(
|
|
1665
|
+
await writeFile8(join13(site.path, c.rel), c.after, "utf-8");
|
|
1636
1666
|
}
|
|
1637
1667
|
const sha = await commit(
|
|
1638
1668
|
site.path,
|
|
@@ -1648,8 +1678,8 @@ async function svelteCodemods(site) {
|
|
|
1648
1678
|
}
|
|
1649
1679
|
|
|
1650
1680
|
// src/recipes/convert-to-pnpm.ts
|
|
1651
|
-
import { rm as rm3, stat } from "fs/promises";
|
|
1652
|
-
import { join as
|
|
1681
|
+
import { rm as rm3, stat as stat2 } from "fs/promises";
|
|
1682
|
+
import { join as join14 } from "path";
|
|
1653
1683
|
|
|
1654
1684
|
// src/recipes/convert-to-pnpm/script-rewrites.ts
|
|
1655
1685
|
function rewriteScriptForPnpm(script) {
|
|
@@ -1671,25 +1701,22 @@ function rewriteScriptsForPnpm(scripts) {
|
|
|
1671
1701
|
|
|
1672
1702
|
// src/recipes/convert-to-pnpm.ts
|
|
1673
1703
|
var DEFAULT_PNPM_VERSION = "10.33.1";
|
|
1674
|
-
async function
|
|
1704
|
+
async function exists2(path) {
|
|
1675
1705
|
try {
|
|
1676
|
-
await
|
|
1706
|
+
await stat2(path);
|
|
1677
1707
|
return true;
|
|
1678
1708
|
} catch {
|
|
1679
1709
|
return false;
|
|
1680
1710
|
}
|
|
1681
1711
|
}
|
|
1682
|
-
function siteLabel10(site) {
|
|
1683
|
-
return site.name ?? site.path;
|
|
1684
|
-
}
|
|
1685
1712
|
async function convertToPnpm(site, opts = {}) {
|
|
1686
|
-
const label =
|
|
1713
|
+
const label = siteLabel(site);
|
|
1687
1714
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1688
1715
|
const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
|
|
1689
|
-
const pnpmLockPath =
|
|
1690
|
-
const npmLockPath =
|
|
1691
|
-
const yarnLockPath =
|
|
1692
|
-
if (await
|
|
1716
|
+
const pnpmLockPath = join14(site.path, "pnpm-lock.yaml");
|
|
1717
|
+
const npmLockPath = join14(site.path, "package-lock.json");
|
|
1718
|
+
const yarnLockPath = join14(site.path, "yarn.lock");
|
|
1719
|
+
if (await exists2(pnpmLockPath)) {
|
|
1693
1720
|
return {
|
|
1694
1721
|
recipe: "convert-to-pnpm",
|
|
1695
1722
|
site: label,
|
|
@@ -1698,8 +1725,8 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
1698
1725
|
notes: "site already has pnpm-lock.yaml"
|
|
1699
1726
|
};
|
|
1700
1727
|
}
|
|
1701
|
-
const hasNpmLock = await
|
|
1702
|
-
const hasYarnLock = await
|
|
1728
|
+
const hasNpmLock = await exists2(npmLockPath);
|
|
1729
|
+
const hasYarnLock = await exists2(yarnLockPath);
|
|
1703
1730
|
if (!hasNpmLock && !hasYarnLock) {
|
|
1704
1731
|
return {
|
|
1705
1732
|
recipe: "convert-to-pnpm",
|
|
@@ -1720,7 +1747,7 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
1720
1747
|
const sourceLock = hasNpmLock ? "package-lock.json" : "yarn.lock";
|
|
1721
1748
|
const lockSha = await commit(site.path, `chore(pnpm): remove ${sourceLock}`);
|
|
1722
1749
|
if (lockSha) shas.push(lockSha);
|
|
1723
|
-
const pkgPath =
|
|
1750
|
+
const pkgPath = join14(site.path, "package.json");
|
|
1724
1751
|
const pkg = await readPackageJson(pkgPath);
|
|
1725
1752
|
const next = { ...pkg, packageManager: `pnpm@${pnpmVersion}` };
|
|
1726
1753
|
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
@@ -1734,7 +1761,7 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
1734
1761
|
await writePackageJson(pkgPath, next);
|
|
1735
1762
|
const pkgSha = await commit(site.path, "chore(pnpm): pin packageManager + rewrite npm scripts");
|
|
1736
1763
|
if (pkgSha) shas.push(pkgSha);
|
|
1737
|
-
await rm3(
|
|
1764
|
+
await rm3(join14(site.path, "node_modules"), { recursive: true, force: true });
|
|
1738
1765
|
const installResult = await spawn2("pnpm", ["install"], {
|
|
1739
1766
|
cwd: site.path,
|
|
1740
1767
|
streaming: true
|
|
@@ -1760,17 +1787,17 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
1760
1787
|
}
|
|
1761
1788
|
|
|
1762
1789
|
// src/recipes/onboard.ts
|
|
1763
|
-
import { stat as
|
|
1764
|
-
import { join as
|
|
1790
|
+
import { stat as stat3 } from "fs/promises";
|
|
1791
|
+
import { join as join16 } from "path";
|
|
1765
1792
|
|
|
1766
1793
|
// src/util/self-version.ts
|
|
1767
1794
|
import { readFileSync } from "fs";
|
|
1768
1795
|
import { fileURLToPath } from "url";
|
|
1769
|
-
import { dirname, join as
|
|
1796
|
+
import { dirname, join as join15 } from "path";
|
|
1770
1797
|
function selfPackageVersion(callerImportMetaUrl) {
|
|
1771
1798
|
try {
|
|
1772
1799
|
const here = dirname(fileURLToPath(callerImportMetaUrl));
|
|
1773
|
-
const raw = readFileSync(
|
|
1800
|
+
const raw = readFileSync(join15(here, "..", "..", "package.json"), "utf-8");
|
|
1774
1801
|
const pkg = JSON.parse(raw);
|
|
1775
1802
|
return pkg.version ?? "0.0.0";
|
|
1776
1803
|
} catch {
|
|
@@ -1783,33 +1810,41 @@ function selfCaretRange(callerImportMetaUrl) {
|
|
|
1783
1810
|
|
|
1784
1811
|
// src/recipes/onboard.ts
|
|
1785
1812
|
var PACKAGE_NAME = "@reddoorla/maintenance";
|
|
1786
|
-
var
|
|
1787
|
-
lighthouse: [
|
|
1788
|
-
a11y: [
|
|
1789
|
-
{ name: "@playwright/test", version: "^1.59.1" },
|
|
1790
|
-
{ name: "@axe-core/playwright", version: "^4.11.3" }
|
|
1791
|
-
]
|
|
1813
|
+
var AUDIT_DEP_NAMES = {
|
|
1814
|
+
lighthouse: ["@lhci/cli"],
|
|
1815
|
+
a11y: ["@playwright/test", "@axe-core/playwright"]
|
|
1792
1816
|
};
|
|
1793
|
-
|
|
1817
|
+
var AUDIT_DEPS = Object.fromEntries(
|
|
1818
|
+
Object.entries(AUDIT_DEP_NAMES).map(([audit, names]) => [
|
|
1819
|
+
audit,
|
|
1820
|
+
names.map((name) => {
|
|
1821
|
+
const version = baselineVersions[name];
|
|
1822
|
+
if (!version) {
|
|
1823
|
+
throw new Error(
|
|
1824
|
+
`baseline-versions is missing audit dep "${name}" \u2014 add it to src/configs/baseline-versions.ts`
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
return { name, version };
|
|
1828
|
+
})
|
|
1829
|
+
])
|
|
1830
|
+
);
|
|
1831
|
+
async function exists3(path) {
|
|
1794
1832
|
try {
|
|
1795
|
-
await
|
|
1833
|
+
await stat3(path);
|
|
1796
1834
|
return true;
|
|
1797
1835
|
} catch {
|
|
1798
1836
|
return false;
|
|
1799
1837
|
}
|
|
1800
1838
|
}
|
|
1801
|
-
function siteLabel11(site) {
|
|
1802
|
-
return site.name ?? site.path;
|
|
1803
|
-
}
|
|
1804
1839
|
function isDeclared(pkg, name) {
|
|
1805
1840
|
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
1806
1841
|
}
|
|
1807
1842
|
async function onboard(site, opts = {}) {
|
|
1808
|
-
const label =
|
|
1843
|
+
const label = siteLabel(site);
|
|
1809
1844
|
const spawn2 = opts.spawn ?? defaultSpawn;
|
|
1810
1845
|
const audits = opts.audits ?? ["lighthouse", "a11y"];
|
|
1811
1846
|
const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);
|
|
1812
|
-
if (!await
|
|
1847
|
+
if (!await exists3(join16(site.path, "pnpm-lock.yaml"))) {
|
|
1813
1848
|
return {
|
|
1814
1849
|
recipe: "onboard",
|
|
1815
1850
|
site: label,
|
|
@@ -1818,7 +1853,7 @@ async function onboard(site, opts = {}) {
|
|
|
1818
1853
|
notes: "no pnpm-lock.yaml at site root \u2014 run convert-to-pnpm first"
|
|
1819
1854
|
};
|
|
1820
1855
|
}
|
|
1821
|
-
const pkgPath =
|
|
1856
|
+
const pkgPath = join16(site.path, "package.json");
|
|
1822
1857
|
const pkg = await readPackageJson(pkgPath);
|
|
1823
1858
|
const toAdd = [];
|
|
1824
1859
|
if (!isDeclared(pkg, PACKAGE_NAME)) {
|