@reddoorla/maintenance 0.6.6 → 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/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: siteLabel2(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: siteLabel2(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
- if (!parsed.metadata?.vulnerabilities) {
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 = siteLabel3(site);
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 = siteLabel4(site);
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 = siteLabel5(site);
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 iso = when.toISOString().replace(/[-:.]/g, "").replace(/Z$/, "Z");
824
- const trimmed = iso.replace(/(\d{8}T\d{6})\d+(Z)$/, "$1$2");
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 = siteLabel6(site);
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);
@@ -933,9 +920,6 @@ async function syncConfigs(site, opts = {}) {
933
920
  // src/recipes/bump-deps.ts
934
921
  import { stat } from "fs/promises";
935
922
  import { join as join6 } from "path";
936
- function siteLabel7(site) {
937
- return site.name ?? site.path;
938
- }
939
923
  async function exists(path) {
940
924
  try {
941
925
  await stat(path);
@@ -954,7 +938,7 @@ function upFlagsForGroup(group) {
954
938
  return [];
955
939
  }
956
940
  async function bumpDeps(site, opts = {}) {
957
- const label = siteLabel7(site);
941
+ const label = siteLabel(site);
958
942
  const group = opts.group ?? "minor";
959
943
  const spawn2 = opts.spawn ?? defaultSpawn;
960
944
  const hasPnpmLock = await exists(join6(site.path, "pnpm-lock.yaml"));
@@ -1196,6 +1180,34 @@ import { glob as glob2 } from "tinyglobby";
1196
1180
  // src/recipes/svelte-5/codemods/on-event-to-handler.ts
1197
1181
  var SCRIPT_BLOCK = /<script\b[^>]*>[\s\S]*?<\/script>/g;
1198
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
+ }
1199
1211
  function onEventToHandler(source) {
1200
1212
  const masked = [];
1201
1213
  const placeholder = (i) => ` SCRIPT_${i} `;
@@ -1203,8 +1215,9 @@ function onEventToHandler(source) {
1203
1215
  masked.push(match);
1204
1216
  return placeholder(masked.length - 1);
1205
1217
  });
1206
- const rewritten = intermediate.replace(SIMPLE_ON_EVENT, (_full, name) => `on${name}`);
1207
- let out = rewritten;
1218
+ let processed = intermediate.replace(SIMPLE_ON_EVENT, (_full, name) => `on${name}`);
1219
+ processed = flagEventModifiers(processed);
1220
+ let out = processed;
1208
1221
  masked.forEach((blk, i) => {
1209
1222
  out = out.replace(placeholder(i), blk);
1210
1223
  });
@@ -1254,6 +1267,22 @@ function exportLetToProps(source) {
1254
1267
  return source.replace(SCRIPT_BLOCK2, (full) => full.replace(inner, body));
1255
1268
  }
1256
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
+
1257
1286
  // src/recipes/svelte-5/codemods/dollar-restprops.ts
1258
1287
  function removeInterfaceBlock(source) {
1259
1288
  const re = /^\s*interface\s+\$\$Props\s*\{/m;
@@ -1277,20 +1306,6 @@ function removeInterfaceBlock(source) {
1277
1306
  out = out.slice(0, match.index) + out.slice(endIdx);
1278
1307
  }
1279
1308
  }
1280
- function findStringEnd(source, openIdx) {
1281
- const quote = source[openIdx];
1282
- let i = openIdx + 1;
1283
- while (i < source.length) {
1284
- const ch = source[i];
1285
- if (ch === "\\") {
1286
- i += 2;
1287
- continue;
1288
- }
1289
- if (ch === quote) return i;
1290
- i++;
1291
- }
1292
- return -1;
1293
- }
1294
1309
  function maskStringLiterals(source) {
1295
1310
  const strings = [];
1296
1311
  let out = "";
@@ -1367,7 +1382,8 @@ function stateEffectSyncToDerived(source) {
1367
1382
 
1368
1383
  // src/recipes/svelte-5/codemods/dollar-props-class.ts
1369
1384
  var PROPS_DESTRUCTURE = /let\s*\{([\s\S]*?)\}(\s*:\s*\{([\s\S]*?)\})?\s*=\s*\$props\(\)/;
1370
- var DOLLAR_PROPS_CLASS = /\$\$props\.class\b/g;
1385
+ var HAS_DOLLAR_PROPS_CLASS = /\$\$props\.class\b/;
1386
+ var DOLLAR_PROPS_CLASS_GLOBAL = /\$\$props\.class\b/g;
1371
1387
  var DOLLAR_PROPS_ANY = /\$\$props\b/;
1372
1388
  var SCRIPT_BLOCK4 = /<script\b[^>]*>[\s\S]*?<\/script>/g;
1373
1389
  var MIGRATION_TASK = /^<!--\s*@migration-task[\s\S]*?-->\s*\n?/gm;
@@ -1389,8 +1405,7 @@ function restoreScripts(masked, blocks) {
1389
1405
  }
1390
1406
  function dollarPropsClass(source) {
1391
1407
  const { masked } = maskScripts(source);
1392
- if (!DOLLAR_PROPS_CLASS.test(masked)) return source;
1393
- DOLLAR_PROPS_CLASS.lastIndex = 0;
1408
+ if (!HAS_DOLLAR_PROPS_CLASS.test(masked)) return source;
1394
1409
  if (!PROPS_DESTRUCTURE.test(source)) return source;
1395
1410
  let updated = source.replace(PROPS_DESTRUCTURE, (full, body, typeAnno, typeBody) => {
1396
1411
  if (/\bclass\s*:/.test(body)) return full;
@@ -1404,7 +1419,7 @@ function dollarPropsClass(source) {
1404
1419
  return `let { ${newBody} } = $props()`;
1405
1420
  });
1406
1421
  const reMasked = maskScripts(updated);
1407
- const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS, IDENT);
1422
+ const templateRewritten = reMasked.masked.replace(DOLLAR_PROPS_CLASS_GLOBAL, IDENT);
1408
1423
  updated = restoreScripts(templateRewritten, reMasked.blocks);
1409
1424
  const stripped = updated.replace(MIGRATION_TASK, "");
1410
1425
  if (!DOLLAR_PROPS_ANY.test(stripped)) {
@@ -1423,7 +1438,7 @@ function findMatchingClose(source, openIdx) {
1423
1438
  while (i < source.length) {
1424
1439
  const ch = source[i];
1425
1440
  if (ch === '"' || ch === "'" || ch === "`") {
1426
- const closeStr = findStringClose(source, i);
1441
+ const closeStr = findStringEnd(source, i);
1427
1442
  if (closeStr === -1) return -1;
1428
1443
  i = closeStr + 1;
1429
1444
  continue;
@@ -1437,20 +1452,6 @@ function findMatchingClose(source, openIdx) {
1437
1452
  }
1438
1453
  return -1;
1439
1454
  }
1440
- function findStringClose(source, openIdx) {
1441
- const quote = source[openIdx];
1442
- let i = openIdx + 1;
1443
- while (i < source.length) {
1444
- const ch = source[i];
1445
- if (ch === "\\") {
1446
- i += 2;
1447
- continue;
1448
- }
1449
- if (ch === quote) return i;
1450
- i++;
1451
- }
1452
- return -1;
1453
- }
1454
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.";
1455
1456
  function transformBlocks(body) {
1456
1457
  const out = [];
@@ -1562,9 +1563,6 @@ async function writeMigrationSummary(input) {
1562
1563
  }
1563
1564
 
1564
1565
  // src/recipes/svelte-5/index.ts
1565
- function siteLabel8(site) {
1566
- return site.name ?? site.path;
1567
- }
1568
1566
  async function alreadyOnSvelte5(cwd) {
1569
1567
  try {
1570
1568
  const pkg = await readPackageJson(join12(cwd, "package.json"));
@@ -1575,7 +1573,7 @@ async function alreadyOnSvelte5(cwd) {
1575
1573
  }
1576
1574
  }
1577
1575
  async function upgradeSvelte4to5(site, opts = {}) {
1578
- const label = siteLabel8(site);
1576
+ const label = siteLabel(site);
1579
1577
  const spawn2 = opts.spawn ?? defaultSpawn;
1580
1578
  if (await alreadyOnSvelte5(site.path)) {
1581
1579
  return {
@@ -1646,11 +1644,8 @@ async function upgradeSvelte4to5(site, opts = {}) {
1646
1644
  // src/recipes/svelte-codemods.ts
1647
1645
  import { writeFile as writeFile8 } from "fs/promises";
1648
1646
  import { join as join13 } from "path";
1649
- function siteLabel9(site) {
1650
- return site.name ?? site.path;
1651
- }
1652
1647
  async function svelteCodemods(site) {
1653
- const label = siteLabel9(site);
1648
+ const label = siteLabel(site);
1654
1649
  const changes = await planGotchaCodemods(site.path);
1655
1650
  if (changes.length === 0) {
1656
1651
  return {
@@ -1714,11 +1709,8 @@ async function exists2(path) {
1714
1709
  return false;
1715
1710
  }
1716
1711
  }
1717
- function siteLabel10(site) {
1718
- return site.name ?? site.path;
1719
- }
1720
1712
  async function convertToPnpm(site, opts = {}) {
1721
- const label = siteLabel10(site);
1713
+ const label = siteLabel(site);
1722
1714
  const spawn2 = opts.spawn ?? defaultSpawn;
1723
1715
  const pnpmVersion = opts.pnpmVersion ?? DEFAULT_PNPM_VERSION;
1724
1716
  const pnpmLockPath = join14(site.path, "pnpm-lock.yaml");
@@ -1844,14 +1836,11 @@ async function exists3(path) {
1844
1836
  return false;
1845
1837
  }
1846
1838
  }
1847
- function siteLabel11(site) {
1848
- return site.name ?? site.path;
1849
- }
1850
1839
  function isDeclared(pkg, name) {
1851
1840
  return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
1852
1841
  }
1853
1842
  async function onboard(site, opts = {}) {
1854
- const label = siteLabel11(site);
1843
+ const label = siteLabel(site);
1855
1844
  const spawn2 = opts.spawn ?? defaultSpawn;
1856
1845
  const audits = opts.audits ?? ["lighthouse", "a11y"];
1857
1846
  const packageVersion = opts.packageVersion ?? selfCaretRange(import.meta.url);