@reddoorla/maintenance 0.55.0 → 0.57.0

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.
Files changed (150) hide show
  1. package/README.md +3 -1
  2. package/dist/airtable-2BJX4ATJ.js +9 -0
  3. package/dist/airtable-2BJX4ATJ.js.map +1 -0
  4. package/dist/announce-JEVCDDKL.js +171 -0
  5. package/dist/announce-JEVCDDKL.js.map +1 -0
  6. package/dist/bump-deps-O3SOLQYY.js +57 -0
  7. package/dist/bump-deps-O3SOLQYY.js.map +1 -0
  8. package/dist/chunk-2X322ZES.js +99 -0
  9. package/dist/chunk-2X322ZES.js.map +1 -0
  10. package/dist/chunk-4NAOR44V.js +153 -0
  11. package/dist/chunk-4NAOR44V.js.map +1 -0
  12. package/dist/chunk-4PY4I5OR.js +1167 -0
  13. package/dist/chunk-4PY4I5OR.js.map +1 -0
  14. package/dist/chunk-5L2QXUTU.js +279 -0
  15. package/dist/chunk-5L2QXUTU.js.map +1 -0
  16. package/dist/chunk-5WL3ET24.js +203 -0
  17. package/dist/chunk-5WL3ET24.js.map +1 -0
  18. package/dist/chunk-6LIWDIXU.js +38 -0
  19. package/dist/chunk-6LIWDIXU.js.map +1 -0
  20. package/dist/chunk-6VAL7XJT.js +52 -0
  21. package/dist/chunk-6VAL7XJT.js.map +1 -0
  22. package/dist/chunk-BWLUNY7L.js +585 -0
  23. package/dist/chunk-BWLUNY7L.js.map +1 -0
  24. package/dist/chunk-CASCWQQX.js +161 -0
  25. package/dist/chunk-CASCWQQX.js.map +1 -0
  26. package/dist/chunk-CLVTVDOU.js +274 -0
  27. package/dist/chunk-CLVTVDOU.js.map +1 -0
  28. package/dist/chunk-D3DQFC32.js +79 -0
  29. package/dist/chunk-D3DQFC32.js.map +1 -0
  30. package/dist/chunk-EYODPUHV.js +37 -0
  31. package/dist/chunk-EYODPUHV.js.map +1 -0
  32. package/dist/chunk-FDIFTIXC.js +70 -0
  33. package/dist/chunk-FDIFTIXC.js.map +1 -0
  34. package/dist/chunk-FUD4I2ZW.js +35 -0
  35. package/dist/chunk-FUD4I2ZW.js.map +1 -0
  36. package/dist/chunk-HG5DTFNF.js +62 -0
  37. package/dist/chunk-HG5DTFNF.js.map +1 -0
  38. package/dist/chunk-HN6I2XWI.js +65 -0
  39. package/dist/chunk-HN6I2XWI.js.map +1 -0
  40. package/dist/chunk-HVOOCK6L.js +84 -0
  41. package/dist/chunk-HVOOCK6L.js.map +1 -0
  42. package/dist/chunk-IMIR7SC2.js +51 -0
  43. package/dist/chunk-IMIR7SC2.js.map +1 -0
  44. package/dist/chunk-J3OPWFZE.js +64 -0
  45. package/dist/chunk-J3OPWFZE.js.map +1 -0
  46. package/dist/chunk-J4ECCFRT.js +221 -0
  47. package/dist/chunk-J4ECCFRT.js.map +1 -0
  48. package/dist/chunk-JNXCDUYB.js +211 -0
  49. package/dist/chunk-JNXCDUYB.js.map +1 -0
  50. package/dist/chunk-JQG222CI.js +49 -0
  51. package/dist/chunk-JQG222CI.js.map +1 -0
  52. package/dist/chunk-LKGVSM2O.js +43 -0
  53. package/dist/chunk-LKGVSM2O.js.map +1 -0
  54. package/dist/chunk-LYCKN7ZR.js +41 -0
  55. package/dist/chunk-LYCKN7ZR.js.map +1 -0
  56. package/dist/chunk-MKSZBODM.js +162 -0
  57. package/dist/chunk-MKSZBODM.js.map +1 -0
  58. package/dist/chunk-O65I7G7G.js +246 -0
  59. package/dist/chunk-O65I7G7G.js.map +1 -0
  60. package/dist/chunk-OCS7SZUZ.js +243 -0
  61. package/dist/chunk-OCS7SZUZ.js.map +1 -0
  62. package/dist/chunk-OGGRFBL4.js +14 -0
  63. package/dist/chunk-OGGRFBL4.js.map +1 -0
  64. package/dist/chunk-OQPMZ6BM.js +368 -0
  65. package/dist/chunk-OQPMZ6BM.js.map +1 -0
  66. package/dist/chunk-OTGB6YQO.js +52 -0
  67. package/dist/chunk-OTGB6YQO.js.map +1 -0
  68. package/dist/chunk-PZQ3WED2.js +14 -0
  69. package/dist/chunk-PZQ3WED2.js.map +1 -0
  70. package/dist/chunk-QTHKOIFJ.js +13 -0
  71. package/dist/chunk-QTHKOIFJ.js.map +1 -0
  72. package/dist/chunk-U5SZ4FOP.js +26 -0
  73. package/dist/chunk-U5SZ4FOP.js.map +1 -0
  74. package/dist/chunk-W3G7XFBZ.js +444 -0
  75. package/dist/chunk-W3G7XFBZ.js.map +1 -0
  76. package/dist/chunk-WEKL2HYJ.js +31 -0
  77. package/dist/chunk-WEKL2HYJ.js.map +1 -0
  78. package/dist/chunk-WVRPOXKH.js +74 -0
  79. package/dist/chunk-WVRPOXKH.js.map +1 -0
  80. package/dist/chunk-XKOZUVK2.js +121 -0
  81. package/dist/chunk-XKOZUVK2.js.map +1 -0
  82. package/dist/chunk-XO2RUYVM.js +138 -0
  83. package/dist/chunk-XO2RUYVM.js.map +1 -0
  84. package/dist/chunk-XTK5VIQB.js +97 -0
  85. package/dist/chunk-XTK5VIQB.js.map +1 -0
  86. package/dist/chunk-XXTZBPUY.js +9 -0
  87. package/dist/chunk-XXTZBPUY.js.map +1 -0
  88. package/dist/chunk-ZYBOT5PM.js +153 -0
  89. package/dist/chunk-ZYBOT5PM.js.map +1 -0
  90. package/dist/cli/bin.js +75 -7785
  91. package/dist/cli/bin.js.map +1 -1
  92. package/dist/cli/commands/audit.js +37 -2336
  93. package/dist/cli/commands/audit.js.map +1 -1
  94. package/dist/client-EPVO3FEH.js +10 -0
  95. package/dist/client-EPVO3FEH.js.map +1 -0
  96. package/dist/client-JA6QLEDW.js +48 -0
  97. package/dist/client-JA6QLEDW.js.map +1 -0
  98. package/dist/configs/lighthouse.js +4 -31
  99. package/dist/configs/lighthouse.js.map +1 -1
  100. package/dist/configs/playwright-a11y.js +6 -33
  101. package/dist/configs/playwright-a11y.js.map +1 -1
  102. package/dist/convert-to-pnpm-UR64X3OX.js +51 -0
  103. package/dist/convert-to-pnpm-UR64X3OX.js.map +1 -0
  104. package/dist/db-AXKMRTVA.js +23 -0
  105. package/dist/db-AXKMRTVA.js.map +1 -0
  106. package/dist/digest-YTO73BEO.js +408 -0
  107. package/dist/digest-YTO73BEO.js.map +1 -0
  108. package/dist/forms/index.js +3 -8
  109. package/dist/forms/index.js.map +1 -1
  110. package/dist/github-signals-XLQXXH6Q.js +116 -0
  111. package/dist/github-signals-XLQXXH6Q.js.map +1 -0
  112. package/dist/index.js +102 -4757
  113. package/dist/index.js.map +1 -1
  114. package/dist/init-JITQXFVL.js +85 -0
  115. package/dist/init-JITQXFVL.js.map +1 -0
  116. package/dist/launch-XW6PEKUL.js +207 -0
  117. package/dist/launch-XW6PEKUL.js.map +1 -0
  118. package/dist/lighthouse-airtable-4FKYKFH6.js +12 -0
  119. package/dist/lighthouse-airtable-4FKYKFH6.js.map +1 -0
  120. package/dist/migrate-P2LS4WXI.js +7 -0
  121. package/dist/migrate-P2LS4WXI.js.map +1 -0
  122. package/dist/onboard-TCQOP2W4.js +69 -0
  123. package/dist/onboard-TCQOP2W4.js.map +1 -0
  124. package/dist/orchestrate-LHH3LQNP.js +23 -0
  125. package/dist/orchestrate-LHH3LQNP.js.map +1 -0
  126. package/dist/recipes/sync-configs.js +9 -505
  127. package/dist/recipes/sync-configs.js.map +1 -1
  128. package/dist/renovate-dispatch-YVBTBJJY.js +100 -0
  129. package/dist/renovate-dispatch-YVBTBJJY.js.map +1 -0
  130. package/dist/report-WK3SKWJI.js +240 -0
  131. package/dist/report-WK3SKWJI.js.map +1 -0
  132. package/dist/resend-G2BJMAQV.js +7 -0
  133. package/dist/resend-G2BJMAQV.js.map +1 -0
  134. package/dist/self-updating-BYUBUNO4.js +61 -0
  135. package/dist/self-updating-BYUBUNO4.js.map +1 -0
  136. package/dist/svelte-codemods-BYRJAU7I.js +51 -0
  137. package/dist/svelte-codemods-BYRJAU7I.js.map +1 -0
  138. package/dist/sync-configs-JPRLAW5J.js +109 -0
  139. package/dist/sync-configs-JPRLAW5J.js.map +1 -0
  140. package/dist/upgrade-ERA45EVR.js +64 -0
  141. package/dist/upgrade-ERA45EVR.js.map +1 -0
  142. package/dist/util/git.js +19 -76
  143. package/dist/util/git.js.map +1 -1
  144. package/dist/util/pkg.js +5 -45
  145. package/dist/util/pkg.js.map +1 -1
  146. package/dist/websites-X5DLC2PC.js +43 -0
  147. package/dist/websites-X5DLC2PC.js.map +1 -0
  148. package/dist/write-audits-to-airtable-CE4POBAF.js +13 -0
  149. package/dist/write-audits-to-airtable-CE4POBAF.js.map +1 -0
  150. package/package.json +13 -12
@@ -0,0 +1,49 @@
1
+ import {
2
+ isHttpUrl
3
+ } from "./chunk-U5SZ4FOP.js";
4
+ import {
5
+ ACTIVE_STATUSES,
6
+ listWebsites,
7
+ siteSlug
8
+ } from "./chunk-CLVTVDOU.js";
9
+
10
+ // src/inventory/airtable.ts
11
+ function fromAirtableBase(base, opts = {}) {
12
+ return async () => {
13
+ const workdir = opts.workdir ?? process.env.REDDOOR_FLEET_WORKDIR;
14
+ if (!workdir) {
15
+ throw new Error(
16
+ "fromAirtableBase requires `workdir` option or REDDOOR_FLEET_WORKDIR env (sites need a local path)"
17
+ );
18
+ }
19
+ const websites = await listWebsites(base);
20
+ return websites.filter((w) => w.status !== null && ACTIVE_STATUSES.has(w.status) && w.url.length > 0).flatMap((w) => {
21
+ const slug = siteSlug(w.name);
22
+ if (slug.length === 0) {
23
+ console.warn(
24
+ `[inventory] skipping "${w.name}" (row ${w.id}): Name has no slug-able characters (empty slug)`
25
+ );
26
+ return [];
27
+ }
28
+ const site = {
29
+ path: `${workdir}/${slug}`,
30
+ name: slug,
31
+ meta: { airtableRowId: w.id, displayName: w.name }
32
+ };
33
+ if (isHttpUrl(w.url)) {
34
+ site.deployedUrl = w.url;
35
+ } else {
36
+ console.warn(
37
+ `[inventory] skipping deployed audit for "${w.name}": url is not http(s): ${JSON.stringify(w.url)}`
38
+ );
39
+ }
40
+ if (w.gitRepo) site.gitRepo = w.gitRepo;
41
+ return [site];
42
+ });
43
+ };
44
+ }
45
+
46
+ export {
47
+ fromAirtableBase
48
+ };
49
+ //# sourceMappingURL=chunk-JQG222CI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/inventory/airtable.ts"],"sourcesContent":["import type { Site, InventoryProvider } from \"../types.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { listWebsites, siteSlug, ACTIVE_STATUSES } from \"../reports/airtable/websites.js\";\nimport { isHttpUrl } from \"../util/url.js\";\n\nexport type AirtableInventoryOptions = {\n /**\n * Local workdir to compute each site's path as `{workdir}/{slug}`.\n * Defaults to REDDOOR_FLEET_WORKDIR env var if not provided.\n * Airtable doesn't store local checkout paths, so this is required.\n */\n workdir?: string;\n};\n\n/**\n * Read sites from the Airtable Websites table as an InventoryProvider.\n * Each row becomes one Site; `path` is computed as `{workdir}/{slug}`.\n * Only `maintenance` / `launch period` sites that have a `url` are included\n * (the live sites we audit + report on). The production URL is exposed as\n * `Site.deployedUrl` so the lighthouse audit can run against it with no\n * checkout. `repoUrl` is intentionally NOT set from `url` — a clone source\n * must come from `gitRepo` (`owner/repo`), never the production URL.\n */\nexport function fromAirtableBase(\n base: AirtableBase,\n opts: AirtableInventoryOptions = {},\n): InventoryProvider {\n return async (): Promise<Site[]> => {\n const workdir = opts.workdir ?? process.env.REDDOOR_FLEET_WORKDIR;\n if (!workdir) {\n throw new Error(\n \"fromAirtableBase requires `workdir` option or REDDOOR_FLEET_WORKDIR env (sites need a local path)\",\n );\n }\n const websites = await listWebsites(base);\n return websites\n .filter((w) => w.status !== null && ACTIVE_STATUSES.has(w.status) && w.url.length > 0)\n .flatMap((w) => {\n const slug = siteSlug(w.name);\n // An empty slug (a Name with no slug-able characters) can't form a stable\n // path and — fatally — can't be matched back to its Websites row on\n // write-back: every empty-slug site would collapse under the \"\" key and\n // mis-write or fail. Skip it loudly rather than silently mis-map it.\n if (slug.length === 0) {\n console.warn(\n `[inventory] skipping \"${w.name}\" (row ${w.id}): Name has no slug-able characters (empty slug)`,\n );\n return [];\n }\n const site: Site = {\n path: `${workdir}/${slug}`,\n name: slug,\n meta: { airtableRowId: w.id, displayName: w.name },\n };\n // Scheme-allowlist the Airtable `url` before exposing it as the\n // deployed-audit target (it's handed straight to Chrome/lhci). A\n // `file://`/`gopher://`/internal-host value would be a local-file read\n // or SSRF — skip the deployed audit for that site rather than trust it.\n if (isHttpUrl(w.url)) {\n site.deployedUrl = w.url;\n } else {\n console.warn(\n `[inventory] skipping deployed audit for \"${w.name}\": url is not http(s): ${JSON.stringify(w.url)}`,\n );\n }\n if (w.gitRepo) site.gitRepo = w.gitRepo;\n return [site];\n });\n };\n}\n"],"mappings":";;;;;;;;;;AAuBO,SAAS,iBACd,MACA,OAAiC,CAAC,GACf;AACnB,SAAO,YAA6B;AAClC,UAAM,UAAU,KAAK,WAAW,QAAQ,IAAI;AAC5C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,WAAW,MAAM,aAAa,IAAI;AACxC,WAAO,SACJ,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,gBAAgB,IAAI,EAAE,MAAM,KAAK,EAAE,IAAI,SAAS,CAAC,EACpF,QAAQ,CAAC,MAAM;AACd,YAAM,OAAO,SAAS,EAAE,IAAI;AAK5B,UAAI,KAAK,WAAW,GAAG;AACrB,gBAAQ;AAAA,UACN,yBAAyB,EAAE,IAAI,UAAU,EAAE,EAAE;AAAA,QAC/C;AACA,eAAO,CAAC;AAAA,MACV;AACA,YAAM,OAAa;AAAA,QACjB,MAAM,GAAG,OAAO,IAAI,IAAI;AAAA,QACxB,MAAM;AAAA,QACN,MAAM,EAAE,eAAe,EAAE,IAAI,aAAa,EAAE,KAAK;AAAA,MACnD;AAKA,UAAI,UAAU,EAAE,GAAG,GAAG;AACpB,aAAK,cAAc,EAAE;AAAA,MACvB,OAAO;AACL,gBAAQ;AAAA,UACN,4CAA4C,EAAE,IAAI,0BAA0B,KAAK,UAAU,EAAE,GAAG,CAAC;AAAA,QACnG;AAAA,MACF;AACA,UAAI,EAAE,QAAS,MAAK,UAAU,EAAE;AAChC,aAAO,CAAC,IAAI;AAAA,IACd,CAAC;AAAA,EACL;AACF;","names":[]}
@@ -0,0 +1,43 @@
1
+ // src/configs/baseline-versions.ts
2
+ var baselineVersions = {
3
+ // SvelteKit core
4
+ svelte: "^5.55.10",
5
+ "@sveltejs/kit": "^2.61.1",
6
+ "@sveltejs/adapter-netlify": "^6.0.4",
7
+ "@sveltejs/adapter-auto": "^7.0.1",
8
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
9
+ "svelte-check": "^4.4.8",
10
+ // Build tooling
11
+ vite: "^8.0.14",
12
+ vitest: "^4.1.7",
13
+ typescript: "^6.0.3",
14
+ // Tailwind 4
15
+ tailwindcss: "^4.3.0",
16
+ "@tailwindcss/vite": "^4.3.0",
17
+ // Prismic
18
+ "@prismicio/client": "^7.21.8",
19
+ "@prismicio/svelte": "^2.2.1",
20
+ "@slicemachine/adapter-sveltekit": "^0.3.96",
21
+ "slice-machine-ui": "^2.21.3",
22
+ // Test tooling
23
+ "@playwright/test": "^1.60.0",
24
+ "@axe-core/playwright": "^4.11.3",
25
+ "@lhci/cli": "^0.15.1",
26
+ // Lint
27
+ eslint: "^10.4.0",
28
+ "eslint-plugin-svelte": "^3.18.0",
29
+ "eslint-config-prettier": "^10.1.8",
30
+ prettier: "^3.8.3",
31
+ "prettier-plugin-svelte": "^4.0.1",
32
+ "typescript-eslint": "^8.60.0",
33
+ "@eslint/js": "^10.0.1",
34
+ globals: "^17.6.0",
35
+ // Misc
36
+ "@lucide/svelte": "^1.17.0",
37
+ "@zerodevx/svelte-img": "^2.1.2"
38
+ };
39
+
40
+ export {
41
+ baselineVersions
42
+ };
43
+ //# sourceMappingURL=chunk-LKGVSM2O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/configs/baseline-versions.ts"],"sourcesContent":["// Curated map of the framework deps reddoor sites should stay close to.\n// Refreshed at each package release from reddoor-starter's package.json.\n// Versions are caret ranges to mirror what `pnpm add` would produce.\n\nexport const baselineVersions: Record<string, string> = {\n // SvelteKit core\n svelte: \"^5.55.10\",\n \"@sveltejs/kit\": \"^2.61.1\",\n \"@sveltejs/adapter-netlify\": \"^6.0.4\",\n \"@sveltejs/adapter-auto\": \"^7.0.1\",\n \"@sveltejs/vite-plugin-svelte\": \"^7.1.2\",\n \"svelte-check\": \"^4.4.8\",\n\n // Build tooling\n vite: \"^8.0.14\",\n vitest: \"^4.1.7\",\n typescript: \"^6.0.3\",\n\n // Tailwind 4\n tailwindcss: \"^4.3.0\",\n \"@tailwindcss/vite\": \"^4.3.0\",\n\n // Prismic\n \"@prismicio/client\": \"^7.21.8\",\n \"@prismicio/svelte\": \"^2.2.1\",\n \"@slicemachine/adapter-sveltekit\": \"^0.3.96\",\n \"slice-machine-ui\": \"^2.21.3\",\n\n // Test tooling\n \"@playwright/test\": \"^1.60.0\",\n \"@axe-core/playwright\": \"^4.11.3\",\n \"@lhci/cli\": \"^0.15.1\",\n\n // Lint\n eslint: \"^10.4.0\",\n \"eslint-plugin-svelte\": \"^3.18.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n prettier: \"^3.8.3\",\n \"prettier-plugin-svelte\": \"^4.0.1\",\n \"typescript-eslint\": \"^8.60.0\",\n \"@eslint/js\": \"^10.0.1\",\n globals: \"^17.6.0\",\n\n // Misc\n \"@lucide/svelte\": \"^1.17.0\",\n \"@zerodevx/svelte-img\": \"^2.1.2\",\n};\n\nexport default baselineVersions;\n"],"mappings":";AAIO,IAAM,mBAA2C;AAAA;AAAA,EAEtD,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,6BAA6B;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,gCAAgC;AAAA,EAChC,gBAAgB;AAAA;AAAA,EAGhB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA;AAAA,EAGZ,aAAa;AAAA,EACb,qBAAqB;AAAA;AAAA,EAGrB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,mCAAmC;AAAA,EACnC,oBAAoB;AAAA;AAAA,EAGpB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,aAAa;AAAA;AAAA,EAGb,QAAQ;AAAA,EACR,wBAAwB;AAAA,EACxB,0BAA0B;AAAA,EAC1B,UAAU;AAAA,EACV,0BAA0B;AAAA,EAC1B,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,SAAS;AAAA;AAAA,EAGT,kBAAkB;AAAA,EAClB,wBAAwB;AAC1B;","names":[]}
@@ -0,0 +1,41 @@
1
+ // src/configs/playwright-a11y.ts
2
+ import { defineConfig, devices } from "@playwright/test";
3
+ var a11yRoutes = [
4
+ { path: "/dev/a11y-fixtures", name: "a11y fixtures" },
5
+ { path: "/dev/animate-in", name: "animate-in demo" }
6
+ ];
7
+ var smokeRoutes = [{ path: "/", name: "home" }];
8
+ var playwrightA11yConfig = defineConfig({
9
+ testDir: "tests",
10
+ testMatch: /.*\.spec\.ts$/,
11
+ fullyParallel: true,
12
+ forbidOnly: !!process.env.CI,
13
+ retries: process.env.CI ? 2 : 0,
14
+ reporter: process.env.CI ? "github" : "list",
15
+ use: {
16
+ baseURL: "http://localhost:5173",
17
+ trace: "on-first-retry"
18
+ },
19
+ projects: [
20
+ {
21
+ name: "chromium",
22
+ use: { ...devices["Desktop Chrome"] }
23
+ }
24
+ ],
25
+ webServer: {
26
+ // Portable across pnpm and npm sites — pnpm respects `npm run` too.
27
+ command: "npm run vite:dev",
28
+ url: "http://localhost:5173/dev/a11y-fixtures",
29
+ reuseExistingServer: !process.env.CI,
30
+ timeout: 12e4
31
+ }
32
+ });
33
+ var playwright_a11y_default = playwrightA11yConfig;
34
+
35
+ export {
36
+ a11yRoutes,
37
+ smokeRoutes,
38
+ playwrightA11yConfig,
39
+ playwright_a11y_default
40
+ };
41
+ //# sourceMappingURL=chunk-LYCKN7ZR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/configs/playwright-a11y.ts"],"sourcesContent":["import { defineConfig, devices, type PlaywrightTestConfig } from \"@playwright/test\";\n\nexport type A11yRoute = { path: string; name: string };\n\nexport const a11yRoutes: A11yRoute[] = [\n { path: \"/dev/a11y-fixtures\", name: \"a11y fixtures\" },\n { path: \"/dev/animate-in\", name: \"animate-in demo\" },\n];\n\n// Routes smoke-loaded for client-side (hydration) errors only — NOT axe-scanned.\n// Catches the class of bug where build + SSR succeed but client hydration throws\n// and blanks the page (data-dynamiq 2026-06-09: a Svelte 4->5 `run()` referenced\n// a `$state` declared after it → TDZ ReferenceError on hydrate). `/` is the one\n// route every site has; real routes carry a11y debt we don't gate on here, so we\n// assert only that they don't crash on hydrate.\nexport const smokeRoutes: A11yRoute[] = [{ path: \"/\", name: \"home\" }];\n\nexport const playwrightA11yConfig: PlaywrightTestConfig = defineConfig({\n testDir: \"tests\",\n testMatch: /.*\\.spec\\.ts$/,\n fullyParallel: true,\n forbidOnly: !!process.env.CI,\n retries: process.env.CI ? 2 : 0,\n reporter: process.env.CI ? \"github\" : \"list\",\n use: {\n baseURL: \"http://localhost:5173\",\n trace: \"on-first-retry\",\n },\n projects: [\n {\n name: \"chromium\",\n use: { ...devices[\"Desktop Chrome\"] },\n },\n ],\n webServer: {\n // Portable across pnpm and npm sites — pnpm respects `npm run` too.\n command: \"npm run vite:dev\",\n url: \"http://localhost:5173/dev/a11y-fixtures\",\n reuseExistingServer: !process.env.CI,\n timeout: 120_000,\n },\n});\n\nexport default playwrightA11yConfig;\n"],"mappings":";AAAA,SAAS,cAAc,eAA0C;AAI1D,IAAM,aAA0B;AAAA,EACrC,EAAE,MAAM,sBAAsB,MAAM,gBAAgB;AAAA,EACpD,EAAE,MAAM,mBAAmB,MAAM,kBAAkB;AACrD;AAQO,IAAM,cAA2B,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC;AAE7D,IAAM,uBAA6C,aAAa;AAAA,EACrE,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe;AAAA,EACf,YAAY,CAAC,CAAC,QAAQ,IAAI;AAAA,EAC1B,SAAS,QAAQ,IAAI,KAAK,IAAI;AAAA,EAC9B,UAAU,QAAQ,IAAI,KAAK,WAAW;AAAA,EACtC,KAAK;AAAA,IACH,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,UAAU;AAAA,IACR;AAAA,MACE,MAAM;AAAA,MACN,KAAK,EAAE,GAAG,QAAQ,gBAAgB,EAAE;AAAA,IACtC;AAAA,EACF;AAAA,EACA,WAAW;AAAA;AAAA,IAET,SAAS;AAAA,IACT,KAAK;AAAA,IACL,qBAAqB,CAAC,QAAQ,IAAI;AAAA,IAClC,SAAS;AAAA,EACX;AACF,CAAC;AAED,IAAO,0BAAQ;","names":[]}
@@ -0,0 +1,162 @@
1
+ import {
2
+ makeGitHub
3
+ } from "./chunk-J4ECCFRT.js";
4
+ import {
5
+ templatesByName
6
+ } from "./chunk-CASCWQQX.js";
7
+ import {
8
+ siteLabel
9
+ } from "./chunk-XXTZBPUY.js";
10
+ import {
11
+ branchName,
12
+ checkoutBranch,
13
+ commit,
14
+ createBranch,
15
+ currentBranch,
16
+ getRemoteUrl,
17
+ isOwnerRepo,
18
+ isWorkingTreeClean,
19
+ parseOwnerRepo,
20
+ push
21
+ } from "./chunk-XTK5VIQB.js";
22
+
23
+ // src/recipes/self-updating/index.ts
24
+ import { mkdir, writeFile } from "fs/promises";
25
+ import { dirname, join } from "path";
26
+
27
+ // src/github/config.ts
28
+ function readGitHubConfig() {
29
+ const token = process.env.GITHUB_TOKEN?.trim();
30
+ if (!token) return null;
31
+ const renovateToken = process.env.RENOVATE_TOKEN?.trim() || token;
32
+ return { token, renovateToken };
33
+ }
34
+
35
+ // src/recipes/self-updating/index.ts
36
+ var SELF_UPDATING_CONFIGS = ["ci", "renovate-action", "renovate-config"];
37
+ var REQUIRED_CHECK = "ci / ci";
38
+ function resultOf(site, status, notes, commits = []) {
39
+ return { recipe: "self-updating", site: siteLabel(site), status, commits, notes };
40
+ }
41
+ async function resolveRepo(site) {
42
+ if (site.gitRepo) {
43
+ if (!isOwnerRepo(site.gitRepo)) {
44
+ throw new Error(
45
+ `refusing to act on malformed repo identity: expected "owner/repo", got ${JSON.stringify(site.gitRepo)}`
46
+ );
47
+ }
48
+ return site.gitRepo;
49
+ }
50
+ let fromOrigin;
51
+ try {
52
+ fromOrigin = parseOwnerRepo(await getRemoteUrl(site.path));
53
+ } catch {
54
+ return null;
55
+ }
56
+ if (fromOrigin === null) return null;
57
+ if (!isOwnerRepo(fromOrigin)) {
58
+ throw new Error(
59
+ `refusing to act on malformed repo identity from origin: ${JSON.stringify(fromOrigin)}`
60
+ );
61
+ }
62
+ return fromOrigin;
63
+ }
64
+ async function selfUpdating(site, deps = {}) {
65
+ const templates = templatesByName([...SELF_UPDATING_CONFIGS]);
66
+ const paths = templates.map((t) => t.path);
67
+ let repo;
68
+ try {
69
+ repo = await resolveRepo(site);
70
+ } catch (err) {
71
+ return resultOf(site, "failed", err instanceof Error ? err.message : String(err));
72
+ }
73
+ if (!repo) {
74
+ return resultOf(
75
+ site,
76
+ "failed",
77
+ "no Git repo (set Airtable 'Git repo' or add an origin remote)"
78
+ );
79
+ }
80
+ const cfg = readGitHubConfig();
81
+ const renovateToken = deps.renovateToken ?? cfg?.renovateToken;
82
+ if (!deps.github && !cfg) return resultOf(site, "failed", "GITHUB_TOKEN not set");
83
+ if (!renovateToken) return resultOf(site, "failed", "no RENOVATE_TOKEN available");
84
+ const github = deps.github ?? makeGitHub({ token: cfg.token });
85
+ const base = await github.defaultBranch(repo).catch(() => "main");
86
+ const actions = [];
87
+ const commits = [];
88
+ let original = null;
89
+ let maintBranch = null;
90
+ try {
91
+ const present = await github.filesOnBranch(repo, base, paths);
92
+ if (present.length < paths.length) {
93
+ const existingPR = await github.findOpenSelfUpdatingPR(repo);
94
+ if (existingPR) {
95
+ actions.push(`bootstrap PR already open: ${existingPR}`);
96
+ } else {
97
+ if (!await isWorkingTreeClean(site.path)) {
98
+ return resultOf(site, "failed", "working tree not clean \u2014 commit or stash first");
99
+ }
100
+ try {
101
+ original = await currentBranch(site.path);
102
+ } catch {
103
+ original = null;
104
+ }
105
+ maintBranch = branchName("self-updating");
106
+ await createBranch(site.path, maintBranch);
107
+ for (const t of templates) {
108
+ const dest = join(site.path, t.path);
109
+ await mkdir(dirname(dest), { recursive: true });
110
+ await writeFile(dest, t.contents, "utf-8");
111
+ }
112
+ const sha = await commit(
113
+ site.path,
114
+ "ci: enable self-updating (CI + Renovate auto-merge)"
115
+ );
116
+ if (sha) commits.push(sha);
117
+ await (deps.pushBranch ?? push)(site.path, maintBranch);
118
+ const pr = await github.openPullRequest(repo, {
119
+ head: maintBranch,
120
+ base,
121
+ title: "Enable self-updating (CI + Renovate)",
122
+ body: "Adds the unified CI gate, nightly Renovate, and auto-merge for patch/minor updates."
123
+ });
124
+ actions.push(`opened PR ${pr.url}`);
125
+ }
126
+ }
127
+ if (!await github.autoMergeEnabled(repo)) {
128
+ await github.enableRepoAutoMerge(repo);
129
+ actions.push("enabled auto-merge");
130
+ }
131
+ const existingContexts = await github.branchProtectionContexts(repo, base);
132
+ if (!existingContexts.includes(REQUIRED_CHECK)) {
133
+ const contexts = [.../* @__PURE__ */ new Set([...existingContexts, REQUIRED_CHECK])];
134
+ await github.protectBranch(repo, base, contexts);
135
+ actions.push(`required "${REQUIRED_CHECK}" check on ${base}`);
136
+ }
137
+ if (!await github.secretExists(repo, "RENOVATE_TOKEN")) {
138
+ await github.setRepoSecret(repo, "RENOVATE_TOKEN", renovateToken);
139
+ actions.push("set RENOVATE_TOKEN secret");
140
+ }
141
+ } catch (err) {
142
+ const done = actions.length ? ` (completed: ${actions.join("; ")})` : "";
143
+ const message = err instanceof Error ? err.message : String(err);
144
+ return resultOf(site, "failed", `${message}${done}`, commits);
145
+ } finally {
146
+ if (maintBranch !== null && original !== null && original !== maintBranch) {
147
+ try {
148
+ await checkoutBranch(site.path, original);
149
+ } catch (err) {
150
+ console.warn(
151
+ `warning: could not restore branch ${original} after self-updating: ${err instanceof Error ? err.message : String(err)}`
152
+ );
153
+ }
154
+ }
155
+ }
156
+ return actions.length ? resultOf(site, "applied", actions.join("; "), commits) : resultOf(site, "noop", "already self-updating", commits);
157
+ }
158
+
159
+ export {
160
+ selfUpdating
161
+ };
162
+ //# sourceMappingURL=chunk-MKSZBODM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recipes/self-updating/index.ts","../src/github/config.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { RecipeResult, Site } from \"../../types.js\";\nimport { templatesByName } from \"../sync-configs/templates.js\";\nimport {\n getRemoteUrl,\n parseOwnerRepo,\n isOwnerRepo,\n push as gitPush,\n branchName,\n checkoutBranch,\n createBranch,\n currentBranch,\n commit as gitCommit,\n isWorkingTreeClean,\n} from \"../../util/git.js\";\nimport { siteLabel } from \"../../util/site.js\";\nimport { readGitHubConfig } from \"../../github/config.js\";\nimport { makeGitHub, type GitHub } from \"../../github/gh.js\";\n\nconst SELF_UPDATING_CONFIGS = [\"ci\", \"renovate-action\", \"renovate-config\"] as const;\n\n// Reusable-workflow jobs report their check as \"<caller-job> / <reusable-job>\".\n// The thin `ci` caller (job `ci`) calls reddoorla/.github's reusable workflow (job `ci`),\n// so the required context is \"ci / ci\", NOT \"ci\". (Provisional — verified empirically on the\n// starter during M7.1 rollout; correct here if the live checks API reports a different string.)\nconst REQUIRED_CHECK = \"ci / ci\";\n\nexport type SelfUpdatingDeps = {\n github?: GitHub;\n pushBranch?: (cwd: string, branch: string) => Promise<void>;\n renovateToken?: string;\n};\n\nfunction resultOf(\n site: Site,\n status: RecipeResult[\"status\"],\n notes: string,\n commits: string[] = [],\n): RecipeResult {\n return { recipe: \"self-updating\", site: siteLabel(site), status, commits, notes };\n}\n\n/**\n * Resolve the `owner/repo` this recipe will mutate. An explicit `site.gitRepo`\n * (from Airtable) wins; otherwise derive it from the checkout's `origin`.\n *\n * Returns `null` when there is no repo to act on (no `gitRepo`, no origin) — a\n * benign \"nothing wired\" state. THROWS when a repo value IS present but doesn't\n * match the strict `owner/repo` shape: this recipe writes the broad GitHub\n * token as a repo secret (plus branch protection / auto-merge) at this\n * identity, so an attacker/typo-controlled value must be rejected here, before\n * the first `gh` call, rather than passed through to `gh`.\n */\nasync function resolveRepo(site: Site): Promise<string | null> {\n if (site.gitRepo) {\n if (!isOwnerRepo(site.gitRepo)) {\n throw new Error(\n `refusing to act on malformed repo identity: expected \"owner/repo\", got ${JSON.stringify(site.gitRepo)}`,\n );\n }\n return site.gitRepo;\n }\n let fromOrigin: string | null;\n try {\n fromOrigin = parseOwnerRepo(await getRemoteUrl(site.path));\n } catch {\n return null;\n }\n if (fromOrigin === null) return null;\n if (!isOwnerRepo(fromOrigin)) {\n throw new Error(\n `refusing to act on malformed repo identity from origin: ${JSON.stringify(fromOrigin)}`,\n );\n }\n return fromOrigin;\n}\n\nexport async function selfUpdating(site: Site, deps: SelfUpdatingDeps = {}): Promise<RecipeResult> {\n const templates = templatesByName([...SELF_UPDATING_CONFIGS]);\n const paths = templates.map((t) => t.path);\n\n let repo: string | null;\n try {\n repo = await resolveRepo(site);\n } catch (err) {\n // A malformed repo identity must abort before any `gh` write — surface it\n // as a recipe failure rather than letting the token reach an unintended repo.\n return resultOf(site, \"failed\", err instanceof Error ? err.message : String(err));\n }\n if (!repo) {\n return resultOf(\n site,\n \"failed\",\n \"no Git repo (set Airtable 'Git repo' or add an origin remote)\",\n );\n }\n\n const cfg = readGitHubConfig();\n const renovateToken = deps.renovateToken ?? cfg?.renovateToken;\n if (!deps.github && !cfg) return resultOf(site, \"failed\", \"GITHUB_TOKEN not set\");\n if (!renovateToken) return resultOf(site, \"failed\", \"no RENOVATE_TOKEN available\");\n const github = deps.github ?? makeGitHub({ token: cfg!.token });\n\n const base = await github.defaultBranch(repo).catch(() => \"main\");\n const actions: string[] = [];\n const commits: string[] = [];\n // Hoisted so the `finally` can restore the operator's branch even when a\n // failure (push/PR error) aborts AFTER we created + checked out the maint\n // branch. `maintBranch` stays null until createBranch succeeds, so the\n // restore is a no-op on every path that never left the operator's branch.\n let original: string | null = null;\n let maintBranch: string | null = null;\n\n try {\n // A. CI files on the default branch.\n const present = await github.filesOnBranch(repo, base, paths);\n if (present.length < paths.length) {\n const existingPR = await github.findOpenSelfUpdatingPR(repo);\n if (existingPR) {\n actions.push(`bootstrap PR already open: ${existingPR}`);\n } else {\n if (!(await isWorkingTreeClean(site.path))) {\n return resultOf(site, \"failed\", \"working tree not clean — commit or stash first\");\n }\n // Capture the operator's branch BEFORE creating the maint branch so the\n // `finally` can return them to it (#2). Best-effort capture: if it fails\n // we just skip the restore rather than guess.\n try {\n original = await currentBranch(site.path);\n } catch {\n original = null;\n }\n maintBranch = branchName(\"self-updating\");\n await createBranch(site.path, maintBranch);\n for (const t of templates) {\n const dest = join(site.path, t.path);\n await mkdir(dirname(dest), { recursive: true });\n await writeFile(dest, t.contents, \"utf-8\");\n }\n const sha = await gitCommit(\n site.path,\n \"ci: enable self-updating (CI + Renovate auto-merge)\",\n );\n if (sha) commits.push(sha);\n await (deps.pushBranch ?? gitPush)(site.path, maintBranch);\n const pr = await github.openPullRequest(repo, {\n head: maintBranch,\n base,\n title: \"Enable self-updating (CI + Renovate)\",\n body: \"Adds the unified CI gate, nightly Renovate, and auto-merge for patch/minor updates.\",\n });\n actions.push(`opened PR ${pr.url}`);\n // (Branch restore happens in the `finally` below — on success AND on a\n // failure that aborts after createBranch.)\n }\n }\n\n // B. Repo settings — check-then-ensure, each independent (self-healing).\n if (!(await github.autoMergeEnabled(repo))) {\n await github.enableRepoAutoMerge(repo);\n actions.push(\"enabled auto-merge\");\n }\n const existingContexts = await github.branchProtectionContexts(repo, base);\n if (!existingContexts.includes(REQUIRED_CHECK)) {\n // protectBranch issues a full PUT that REPLACES required-status-check contexts.\n // Send the UNION of the branch's existing required contexts + our REQUIRED_CHECK\n // so we ADD the CI gate without silently dropping any other checks the repo\n // already requires. (When the branch has no protection yet, existingContexts is\n // [] and this is just [REQUIRED_CHECK] — the original behavior.) Dedupe defends\n // against REQUIRED_CHECK already appearing among the existing contexts.\n const contexts = [...new Set([...existingContexts, REQUIRED_CHECK])];\n await github.protectBranch(repo, base, contexts);\n actions.push(`required \"${REQUIRED_CHECK}\" check on ${base}`);\n }\n if (!(await github.secretExists(repo, \"RENOVATE_TOKEN\"))) {\n await github.setRepoSecret(repo, \"RENOVATE_TOKEN\", renovateToken);\n actions.push(\"set RENOVATE_TOKEN secret\");\n }\n } catch (err) {\n const done = actions.length ? ` (completed: ${actions.join(\"; \")})` : \"\";\n const message = err instanceof Error ? err.message : String(err);\n return resultOf(site, \"failed\", `${message}${done}`, commits);\n } finally {\n // Restore the operator's branch whenever we created + checked out a maint\n // branch — on SUCCESS and, critically, on FAILURE. Without this, a push/PR\n // error after createBranch strands the checkout on the maint branch with an\n // unpushed commit, and the retry then fails at createBranch (\"branch already\n // exists\"). Best-effort: a restore failure must not mask the real outcome.\n if (maintBranch !== null && original !== null && original !== maintBranch) {\n try {\n await checkoutBranch(site.path, original);\n } catch (err) {\n console.warn(\n `warning: could not restore branch ${original} after self-updating: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n }\n\n return actions.length\n ? resultOf(site, \"applied\", actions.join(\"; \"), commits)\n : resultOf(site, \"noop\", \"already self-updating\", commits);\n}\n","export type GitHubConfig = {\n /** Broad PAT used by the tool's own `gh` calls (PRs, branch protection, secrets). */\n token: string;\n /** Narrow PAT stored per-repo as the RENOVATE_TOKEN secret. Falls back to `token`. */\n renovateToken: string;\n};\n\n/**\n * Read GitHub config from the environment (credentials.env is loaded into process.env by the CLI).\n * Returns null when GITHUB_TOKEN is unset — the signal that git/GitHub features aren't configured.\n * RENOVATE_TOKEN falls back to GITHUB_TOKEN when unset (a narrower token is recommended but optional).\n */\nexport function readGitHubConfig(): GitHubConfig | null {\n const token = process.env.GITHUB_TOKEN?.trim();\n if (!token) return null;\n const renovateToken = process.env.RENOVATE_TOKEN?.trim() || token;\n return { token, renovateToken };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,246 @@
1
+ import {
2
+ applyGotchaCodemods
3
+ } from "./chunk-OQPMZ6BM.js";
4
+ import {
5
+ withRecipe
6
+ } from "./chunk-XKOZUVK2.js";
7
+ import {
8
+ bumpDep,
9
+ readPackageJson,
10
+ writePackageJson
11
+ } from "./chunk-OTGB6YQO.js";
12
+ import {
13
+ defaultSpawn
14
+ } from "./chunk-HVOOCK6L.js";
15
+
16
+ // src/recipes/svelte-5/index.ts
17
+ import { join as join5 } from "path";
18
+
19
+ // src/recipes/svelte-5/step-bump-versions.ts
20
+ import { join } from "path";
21
+ var SVELTE_5_VERSIONS = {
22
+ svelte: "^5.55.5",
23
+ "@sveltejs/kit": "^2.59.0",
24
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
25
+ "@sveltejs/adapter-netlify": "^6.0.4",
26
+ "@sveltejs/adapter-auto": "^7.0.0",
27
+ vite: "^8.0.10",
28
+ "svelte-check": "^4.4.7",
29
+ typescript: "^6.0.3",
30
+ "typescript-svelte-plugin": "^0.3.52"
31
+ };
32
+ async function bumpToSvelte5Versions(cwd) {
33
+ const pkgPath = join(cwd, "package.json");
34
+ const pkg = await readPackageJson(pkgPath);
35
+ let next = pkg;
36
+ for (const [name, version] of Object.entries(SVELTE_5_VERSIONS)) {
37
+ next = bumpDep(next, name, version, { mode: "bump-only" });
38
+ }
39
+ if (next === pkg) return false;
40
+ await writePackageJson(pkgPath, next);
41
+ return true;
42
+ }
43
+
44
+ // src/recipes/svelte-5/step-svelte-config.ts
45
+ import { readFile, writeFile } from "fs/promises";
46
+ import { join as join2 } from "path";
47
+ var VITE_PLUGIN_PKG = "@sveltejs/vite-plugin-svelte";
48
+ var IMPORT_FROM_VITE_PLUGIN = new RegExp(
49
+ String.raw`^import\s+\{\s*([^}]+?)\s*\}\s+from\s+["']` + VITE_PLUGIN_PKG.replace(/[/]/g, "\\/") + String.raw`["'];?[ \t]*\n`,
50
+ "m"
51
+ );
52
+ function dropVitePreprocessImport(source) {
53
+ return source.replace(IMPORT_FROM_VITE_PLUGIN, (full, names) => {
54
+ const remaining = names.split(",").map((n) => n.trim()).filter((n) => n.length > 0 && n !== "vitePreprocess");
55
+ if (remaining.length === 0) return "";
56
+ return `import { ${remaining.join(", ")} } from "${VITE_PLUGIN_PKG}";
57
+ `;
58
+ });
59
+ }
60
+ function findMatchingParen(source, openIdx) {
61
+ if (source[openIdx] !== "(") return -1;
62
+ let depth = 0;
63
+ for (let i = openIdx; i < source.length; i++) {
64
+ const ch = source[i];
65
+ if (ch === "(") depth++;
66
+ else if (ch === ")") {
67
+ depth--;
68
+ if (depth === 0) return i;
69
+ }
70
+ }
71
+ return -1;
72
+ }
73
+ function dropPreprocessKey(source) {
74
+ const startRe = /^(\s*)preprocess:\s*vitePreprocess\(/m;
75
+ const m = startRe.exec(source);
76
+ if (!m) return source;
77
+ const indent = m[1] ?? "";
78
+ const parenOpenAbs = m.index + m[0].length - 1;
79
+ const parenCloseAbs = findMatchingParen(source, parenOpenAbs);
80
+ if (parenCloseAbs < 0) return source;
81
+ let tailIdx = parenCloseAbs + 1;
82
+ while (tailIdx < source.length && /[ \t,]/.test(source[tailIdx] ?? "")) tailIdx++;
83
+ if (source[tailIdx] === "\n") tailIdx++;
84
+ return source.slice(0, m.index) + source.slice(tailIdx).replace(new RegExp(`^${indent}\\n`), "");
85
+ }
86
+ async function migrateSvelteConfig(cwd) {
87
+ const path = join2(cwd, "svelte.config.js");
88
+ let src;
89
+ try {
90
+ src = await readFile(path, "utf-8");
91
+ } catch {
92
+ return false;
93
+ }
94
+ let next = src;
95
+ next = dropPreprocessKey(next);
96
+ next = dropVitePreprocessImport(next);
97
+ if (next === src) return false;
98
+ await writeFile(path, next, "utf-8");
99
+ return true;
100
+ }
101
+
102
+ // src/recipes/svelte-5/step-svelte-migrate.ts
103
+ async function runSvelteMigrate(cwd, spawn = defaultSpawn) {
104
+ try {
105
+ const { code, stderr } = await spawn(
106
+ "npx",
107
+ ["--yes", "svelte-migrate", "svelte-5", "--no-install"],
108
+ { cwd, timeoutMs: 5 * 6e4 }
109
+ );
110
+ if (code !== 0) {
111
+ return { ran: false, stderr };
112
+ }
113
+ return { ran: true, stderr };
114
+ } catch (err) {
115
+ const e = err;
116
+ if (e.code === "ENOENT" || /ENOENT/.test(String(err))) {
117
+ return { ran: false, stderr: "npx unavailable" };
118
+ }
119
+ throw err;
120
+ }
121
+ }
122
+
123
+ // src/recipes/svelte-5/step-tailwind-upgrade.ts
124
+ import { join as join3 } from "path";
125
+ async function upgradeTailwind(cwd, spawn = defaultSpawn) {
126
+ const pkg = await readPackageJson(join3(cwd, "package.json"));
127
+ const tailwindVersion = pkg.devDependencies?.tailwindcss ?? pkg.dependencies?.tailwindcss;
128
+ if (!tailwindVersion) return { ran: false, reason: "tailwindcss not installed" };
129
+ if (/^\^?4\./.test(tailwindVersion)) return { ran: false, reason: "already on tailwind 4.x" };
130
+ try {
131
+ const { code, stderr } = await spawn("npx", ["--yes", "@tailwindcss/upgrade", "--force"], {
132
+ cwd,
133
+ timeoutMs: 5 * 6e4
134
+ });
135
+ if (code !== 0) return { ran: false, reason: stderr.slice(0, 200) };
136
+ return { ran: true };
137
+ } catch (err) {
138
+ const e = err;
139
+ if (e.code === "ENOENT" || /ENOENT/.test(String(err))) {
140
+ return { ran: false, reason: "npx unavailable" };
141
+ }
142
+ throw err;
143
+ }
144
+ }
145
+
146
+ // src/recipes/svelte-5/step-verify.ts
147
+ async function verifyMigration(cwd, spawn = defaultSpawn) {
148
+ let install;
149
+ try {
150
+ install = await spawn("pnpm", ["install"], { cwd, timeoutMs: 10 * 6e4 });
151
+ } catch {
152
+ install = { skipped: true };
153
+ }
154
+ let check;
155
+ try {
156
+ check = await spawn("pnpm", ["run", "check"], { cwd, timeoutMs: 5 * 6e4 });
157
+ } catch {
158
+ check = { skipped: true };
159
+ }
160
+ return { install, check };
161
+ }
162
+
163
+ // src/recipes/svelte-5/step-summary.ts
164
+ import { writeFile as writeFile2 } from "fs/promises";
165
+ import { join as join4 } from "path";
166
+ async function writeMigrationSummary(input) {
167
+ const lines = [
168
+ `# Svelte 4 \u2192 5 migration summary`,
169
+ ``,
170
+ `Generated by @reddoorla/maintenance.`,
171
+ ``,
172
+ `- svelte-migrate run: ${input.svelteMigrateRan ? "yes" : "no"}`,
173
+ `- @tailwindcss/upgrade run: ${input.tailwindUpgraded ? "yes" : "no"}`,
174
+ `- .svelte files touched by gotcha codemods: ${input.filesChangedByCodemods}`,
175
+ ``,
176
+ `Next steps:`,
177
+ `- Run \`pnpm run check\` and resolve any remaining warnings.`,
178
+ `- Spot-check rune migrations in components that use \`reactive\` statements.`,
179
+ `- Verify Playwright a11y tests still pass.`
180
+ ];
181
+ const content = lines.join("\n") + "\n";
182
+ const path = join4(input.cwd, "MIGRATION_SVELTE_5.md");
183
+ await writeFile2(path, content, "utf-8");
184
+ return path;
185
+ }
186
+
187
+ // src/recipes/svelte-5/index.ts
188
+ async function alreadyOnSvelte5(cwd) {
189
+ try {
190
+ const pkg = await readPackageJson(join5(cwd, "package.json"));
191
+ const v = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte;
192
+ return !!v && /^\^?5\./.test(v);
193
+ } catch {
194
+ return false;
195
+ }
196
+ }
197
+ async function upgradeSvelte4to5(site, opts = {}) {
198
+ const spawn = opts.spawn ?? defaultSpawn;
199
+ return withRecipe({
200
+ name: "svelte-4-to-5",
201
+ site,
202
+ plan: async () => {
203
+ if (await alreadyOnSvelte5(site.path)) {
204
+ return { kind: "noop", notes: "site already declares svelte ^5.x" };
205
+ }
206
+ return { kind: "apply", plan: true };
207
+ },
208
+ apply: async (_plan, { commit, cwd }) => {
209
+ const bumped = await bumpToSvelte5Versions(cwd);
210
+ if (bumped) {
211
+ await commit("chore(svelte5): bump svelte/kit/vite/vite-plugin-svelte");
212
+ }
213
+ const configChanged = await migrateSvelteConfig(cwd);
214
+ if (configChanged) {
215
+ await commit("refactor(svelte5): migrate svelte.config.js (drop vitePreprocess)");
216
+ }
217
+ const migrate = await runSvelteMigrate(cwd, spawn);
218
+ if (migrate.ran) {
219
+ await commit("refactor(svelte5): run official svelte-migrate codemod");
220
+ }
221
+ const tw = await upgradeTailwind(cwd, spawn);
222
+ if (tw.ran) {
223
+ await commit("chore(svelte5): tailwindcss 3 \u2192 4 upgrade");
224
+ }
225
+ const codemods = await applyGotchaCodemods(cwd);
226
+ if (codemods.filesChanged > 0) {
227
+ await commit(`refactor(svelte5): apply gotcha codemods (${codemods.filesChanged} files)`);
228
+ }
229
+ await verifyMigration(cwd, spawn);
230
+ await commit("chore(svelte5): pnpm install + check");
231
+ await writeMigrationSummary({
232
+ cwd,
233
+ filesChangedByCodemods: codemods.filesChanged,
234
+ svelteMigrateRan: migrate.ran,
235
+ tailwindUpgraded: tw.ran
236
+ });
237
+ await commit("docs(svelte5): add MIGRATION_SVELTE_5.md summary");
238
+ return { kind: "ok" };
239
+ }
240
+ });
241
+ }
242
+
243
+ export {
244
+ upgradeSvelte4to5
245
+ };
246
+ //# sourceMappingURL=chunk-O65I7G7G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recipes/svelte-5/index.ts","../src/recipes/svelte-5/step-bump-versions.ts","../src/recipes/svelte-5/step-svelte-config.ts","../src/recipes/svelte-5/step-svelte-migrate.ts","../src/recipes/svelte-5/step-tailwind-upgrade.ts","../src/recipes/svelte-5/step-verify.ts","../src/recipes/svelte-5/step-summary.ts"],"sourcesContent":["import { join } from \"node:path\";\nimport type { RecipeResult, Site } from \"../../types.js\";\nimport { readPackageJson } from \"../../util/pkg.js\";\nimport { defaultSpawn, type SpawnFn } from \"../../audits/util/spawn.js\";\nimport { bumpToSvelte5Versions } from \"./step-bump-versions.js\";\nimport { migrateSvelteConfig } from \"./step-svelte-config.js\";\nimport { runSvelteMigrate } from \"./step-svelte-migrate.js\";\nimport { upgradeTailwind } from \"./step-tailwind-upgrade.js\";\nimport { applyGotchaCodemods } from \"./step-gotchas.js\";\nimport { verifyMigration } from \"./step-verify.js\";\nimport { writeMigrationSummary } from \"./step-summary.js\";\nimport { withRecipe } from \"../_with-recipe.js\";\n\nexport type UpgradeSvelte4to5Options = {\n spawn?: SpawnFn;\n};\n\nasync function alreadyOnSvelte5(cwd: string): Promise<boolean> {\n try {\n const pkg = await readPackageJson(join(cwd, \"package.json\"));\n const v = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte;\n return !!v && /^\\^?5\\./.test(v);\n } catch {\n return false;\n }\n}\n\nexport async function upgradeSvelte4to5(\n site: Site,\n opts: UpgradeSvelte4to5Options = {},\n): Promise<RecipeResult> {\n const spawn = opts.spawn ?? defaultSpawn;\n\n return withRecipe<true>({\n name: \"svelte-4-to-5\",\n site,\n plan: async () => {\n if (await alreadyOnSvelte5(site.path)) {\n return { kind: \"noop\", notes: \"site already declares svelte ^5.x\" };\n }\n return { kind: \"apply\", plan: true };\n },\n apply: async (_plan, { commit, cwd }) => {\n const bumped = await bumpToSvelte5Versions(cwd);\n if (bumped) {\n await commit(\"chore(svelte5): bump svelte/kit/vite/vite-plugin-svelte\");\n }\n\n const configChanged = await migrateSvelteConfig(cwd);\n if (configChanged) {\n await commit(\"refactor(svelte5): migrate svelte.config.js (drop vitePreprocess)\");\n }\n\n const migrate = await runSvelteMigrate(cwd, spawn);\n if (migrate.ran) {\n await commit(\"refactor(svelte5): run official svelte-migrate codemod\");\n }\n\n const tw = await upgradeTailwind(cwd, spawn);\n if (tw.ran) {\n await commit(\"chore(svelte5): tailwindcss 3 → 4 upgrade\");\n }\n\n const codemods = await applyGotchaCodemods(cwd);\n if (codemods.filesChanged > 0) {\n await commit(`refactor(svelte5): apply gotcha codemods (${codemods.filesChanged} files)`);\n }\n\n await verifyMigration(cwd, spawn);\n await commit(\"chore(svelte5): pnpm install + check\");\n\n await writeMigrationSummary({\n cwd,\n filesChangedByCodemods: codemods.filesChanged,\n svelteMigrateRan: migrate.ran,\n tailwindUpgraded: tw.ran,\n });\n await commit(\"docs(svelte5): add MIGRATION_SVELTE_5.md summary\");\n\n return { kind: \"ok\" };\n },\n });\n}\n","import { join } from \"node:path\";\nimport { readPackageJson, writePackageJson, bumpDep } from \"../../util/pkg.js\";\n\nconst SVELTE_5_VERSIONS: Record<string, string> = {\n svelte: \"^5.55.5\",\n \"@sveltejs/kit\": \"^2.59.0\",\n \"@sveltejs/vite-plugin-svelte\": \"^7.0.0\",\n \"@sveltejs/adapter-netlify\": \"^6.0.4\",\n \"@sveltejs/adapter-auto\": \"^7.0.0\",\n vite: \"^8.0.10\",\n \"svelte-check\": \"^4.4.7\",\n typescript: \"^6.0.3\",\n \"typescript-svelte-plugin\": \"^0.3.52\",\n};\n\nexport async function bumpToSvelte5Versions(cwd: string): Promise<boolean> {\n const pkgPath = join(cwd, \"package.json\");\n const pkg = await readPackageJson(pkgPath);\n let next = pkg;\n // bump-only: a svelte-4 site that doesn't declare e.g. adapter-netlify\n // should not get it added during the upgrade.\n for (const [name, version] of Object.entries(SVELTE_5_VERSIONS)) {\n next = bumpDep(next, name, version, { mode: \"bump-only\" });\n }\n if (next === pkg) return false;\n await writePackageJson(pkgPath, next);\n return true;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nconst VITE_PLUGIN_PKG = \"@sveltejs/vite-plugin-svelte\";\n\n/** Match an import statement that pulls one or more named bindings from\n * `@sveltejs/vite-plugin-svelte`. Group 1 is the comma-separated name list. */\nconst IMPORT_FROM_VITE_PLUGIN = new RegExp(\n String.raw`^import\\s+\\{\\s*([^}]+?)\\s*\\}\\s+from\\s+[\"']` +\n VITE_PLUGIN_PKG.replace(/[/]/g, \"\\\\/\") +\n String.raw`[\"'];?[ \\t]*\\n`,\n \"m\",\n);\n\n/** Rewrite the import to drop only `vitePreprocess`, preserving any other\n * named bindings. If `vitePreprocess` was the sole import, the whole line\n * is removed. */\nfunction dropVitePreprocessImport(source: string): string {\n return source.replace(IMPORT_FROM_VITE_PLUGIN, (full, names: string) => {\n const remaining = names\n .split(\",\")\n .map((n) => n.trim())\n .filter((n) => n.length > 0 && n !== \"vitePreprocess\");\n if (remaining.length === 0) return \"\"; // drop entire line including its trailing newline\n return `import { ${remaining.join(\", \")} } from \"${VITE_PLUGIN_PKG}\";\\n`;\n });\n}\n\n/** Find the end of a balanced-paren call starting at `openIdx`, which must\n * point at the `(` character. Returns the index of the matching `)`, or -1\n * if unbalanced. */\nfunction findMatchingParen(source: string, openIdx: number): number {\n if (source[openIdx] !== \"(\") return -1;\n let depth = 0;\n for (let i = openIdx; i < source.length; i++) {\n const ch = source[i];\n if (ch === \"(\") depth++;\n else if (ch === \")\") {\n depth--;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\n/** Remove a `preprocess: vitePreprocess(<anything>),?` key from a config\n * object. Handles the call with empty parens or with an options object. */\nfunction dropPreprocessKey(source: string): string {\n // Anchor on the start of the preprocess key on its own line so we don't\n // also strip whitespace / commas from neighboring keys.\n const startRe = /^(\\s*)preprocess:\\s*vitePreprocess\\(/m;\n const m = startRe.exec(source);\n if (!m) return source;\n\n const indent = m[1] ?? \"\";\n const parenOpenAbs = m.index + m[0].length - 1; // points at `(`\n const parenCloseAbs = findMatchingParen(source, parenOpenAbs);\n if (parenCloseAbs < 0) return source;\n\n // Consume an optional trailing comma and whitespace through end-of-line.\n let tailIdx = parenCloseAbs + 1;\n while (tailIdx < source.length && /[ \\t,]/.test(source[tailIdx] ?? \"\")) tailIdx++;\n if (source[tailIdx] === \"\\n\") tailIdx++;\n\n return source.slice(0, m.index) + source.slice(tailIdx).replace(new RegExp(`^${indent}\\\\n`), \"\");\n}\n\nexport async function migrateSvelteConfig(cwd: string): Promise<boolean> {\n const path = join(cwd, \"svelte.config.js\");\n let src: string;\n try {\n src = await readFile(path, \"utf-8\");\n } catch {\n return false;\n }\n\n let next = src;\n next = dropPreprocessKey(next);\n next = dropVitePreprocessImport(next);\n\n if (next === src) return false;\n await writeFile(path, next, \"utf-8\");\n return true;\n}\n","import { defaultSpawn, type SpawnFn } from \"../../audits/util/spawn.js\";\n\nexport async function runSvelteMigrate(\n cwd: string,\n spawn: SpawnFn = defaultSpawn,\n): Promise<{ ran: boolean; stderr: string }> {\n try {\n const { code, stderr } = await spawn(\n \"npx\",\n [\"--yes\", \"svelte-migrate\", \"svelte-5\", \"--no-install\"],\n { cwd, timeoutMs: 5 * 60_000 },\n );\n if (code !== 0) {\n return { ran: false, stderr };\n }\n return { ran: true, stderr };\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) {\n return { ran: false, stderr: \"npx unavailable\" };\n }\n throw err;\n }\n}\n","import { readPackageJson } from \"../../util/pkg.js\";\nimport { join } from \"node:path\";\nimport { defaultSpawn, type SpawnFn } from \"../../audits/util/spawn.js\";\n\nexport async function upgradeTailwind(\n cwd: string,\n spawn: SpawnFn = defaultSpawn,\n): Promise<{ ran: boolean; reason?: string }> {\n const pkg = await readPackageJson(join(cwd, \"package.json\"));\n const tailwindVersion = pkg.devDependencies?.tailwindcss ?? pkg.dependencies?.tailwindcss;\n if (!tailwindVersion) return { ran: false, reason: \"tailwindcss not installed\" };\n if (/^\\^?4\\./.test(tailwindVersion)) return { ran: false, reason: \"already on tailwind 4.x\" };\n\n try {\n const { code, stderr } = await spawn(\"npx\", [\"--yes\", \"@tailwindcss/upgrade\", \"--force\"], {\n cwd,\n timeoutMs: 5 * 60_000,\n });\n if (code !== 0) return { ran: false, reason: stderr.slice(0, 200) };\n return { ran: true };\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\" || /ENOENT/.test(String(err))) {\n return { ran: false, reason: \"npx unavailable\" };\n }\n throw err;\n }\n}\n","import { defaultSpawn, type SpawnFn, type SpawnResult } from \"../../audits/util/spawn.js\";\n\nexport type VerifyResult = {\n install: SpawnResult | { skipped: true };\n check: SpawnResult | { skipped: true };\n};\n\nexport async function verifyMigration(\n cwd: string,\n spawn: SpawnFn = defaultSpawn,\n): Promise<VerifyResult> {\n let install: VerifyResult[\"install\"];\n try {\n install = await spawn(\"pnpm\", [\"install\"], { cwd, timeoutMs: 10 * 60_000 });\n } catch {\n install = { skipped: true };\n }\n\n let check: VerifyResult[\"check\"];\n try {\n check = await spawn(\"pnpm\", [\"run\", \"check\"], { cwd, timeoutMs: 5 * 60_000 });\n } catch {\n check = { skipped: true };\n }\n\n return { install, check };\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport type SummaryInput = {\n cwd: string;\n filesChangedByCodemods: number;\n svelteMigrateRan: boolean;\n tailwindUpgraded: boolean;\n};\n\nexport async function writeMigrationSummary(input: SummaryInput): Promise<string> {\n const lines = [\n `# Svelte 4 → 5 migration summary`,\n ``,\n `Generated by @reddoorla/maintenance.`,\n ``,\n `- svelte-migrate run: ${input.svelteMigrateRan ? \"yes\" : \"no\"}`,\n `- @tailwindcss/upgrade run: ${input.tailwindUpgraded ? \"yes\" : \"no\"}`,\n `- .svelte files touched by gotcha codemods: ${input.filesChangedByCodemods}`,\n ``,\n `Next steps:`,\n `- Run \\`pnpm run check\\` and resolve any remaining warnings.`,\n `- Spot-check rune migrations in components that use \\`reactive\\` statements.`,\n `- Verify Playwright a11y tests still pass.`,\n ];\n const content = lines.join(\"\\n\") + \"\\n\";\n const path = join(input.cwd, \"MIGRATION_SVELTE_5.md\");\n await writeFile(path, content, \"utf-8\");\n return path;\n}\n"],"mappings":"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","names":["join","join","join","join","writeFile","join","join"]}