{"version":3,"file":"migrate-route-mounts.cjs","names":["MOUNT_ALIASES","KNOWN_MOUNTS","UPSTREAM_REF","Effect","listFiles","readText","fileExists","writeText","removePath"],"sources":["../../../../../src/services/scaffold/upgrade-audit/steps/migrate-route-mounts.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { KNOWN_MOUNTS, MOUNT_ALIASES } from \"../domain\";\nimport { fileExists, listFiles, readText, removePath, writeText } from \"../fs\";\nimport { UPSTREAM_REF } from \"../upstream-ref\";\nimport type { StepResult } from \"./author-config\";\n\nconst ROUTES_GLOB = \"ui/src/routes/**/*.{ts,tsx}\";\nconst ROUTE_FILE_RE = /createFileRoute(?:<[^>]*>)?\\(\\s*[\"'`]([^\"'`]+)[\"'`]/;\n\n/**\n * Unknown mount id → registered mount id. Child apps commonly root their UI\n * under `_layout` (v1 convention) which is not in the host mount registry.\n * Map it to `_public` by default; individual entries can be overridden.\n */\nconst MOUNT_REDIRECTS: Record<string, string> = {\n  layout: \"public\",\n};\n\nconst mountIdOf = (routePath: string): string | null => {\n  const segments = routePath.split(\"/\").filter(Boolean);\n  if (segments.length === 0) return null;\n  const first = segments[0];\n  if (!first.startsWith(\"_\")) return null;\n  return first.slice(1);\n};\n\nconst canonicalMount = (mountId: string): string => MOUNT_ALIASES[mountId] ?? mountId;\n\nconst isRegistered = (mountId: string): boolean =>\n  KNOWN_MOUNTS.includes(mountId as (typeof KNOWN_MOUNTS)[number]);\n\nconst mountLayoutFile = (mountId: string): string => `ui/src/routes/_${mountId}.tsx`;\n\nconst renderMountLayout = (mountId: string): string => {\n  const canonical = canonicalMount(mountId);\n  const needsGate = canonical === \"authenticated\";\n  return [\n    \"/**\",\n    \" * GENERATED mount layout — a minimal Outlet shell (plus the requireSession\",\n    \" * gate on authed mounts) so rehomed routes sit on a registered mount.\",\n    \" *\",\n    ` * Upstream canonical shapes live in everything-dev's`,\n    ` * upgrade-audit fixtures/upstream/routes/ at UPSTREAM_REF ${UPSTREAM_REF}`,\n    \" * — conform to those when adding chrome (headers, session context) to\",\n    \" * this mount. Chrome from the old layout was NOT carried over; port it\",\n    \" * manually.\",\n    \" */\",\n    \"\",\n    `import { createFileRoute, Outlet${needsGate ? \", redirect\" : \"\"} } from \"@tanstack/react-router\";`,\n    needsGate ? `import { requireSession } from \"@/lib/auth\";` : \"\",\n    \"\",\n    \"export const Route = createFileRoute(\",\n    `  \"/_${canonical}\",`,\n    needsGate ? \"  { beforeLoad: requireSession, component: Outlet },\" : \"  { component: Outlet },\",\n    \"});\",\n    \"\",\n  ]\n    .filter(Boolean)\n    .join(\"\\n\");\n};\n\nconst rehomeRoute = (\n  file: string,\n  source: string,\n  targetMount: string,\n): { next: string; newFile: string } | null => {\n  const match = source.match(ROUTE_FILE_RE);\n  if (!match) return null;\n  const oldPath = match[1];\n  const segments = oldPath.split(\"/\").filter(Boolean);\n  if (segments.length === 0) return null;\n  // segments[0] is the old mount (e.g. \"_layout\")\n  const oldMountDir = `_${segments[0].slice(1)}`;\n\n  // Build the new route path: drop the old mount segment. If the next segment\n  // is itself a registered mount (e.g. /_layout/_authenticated/dashboard),\n  // it becomes the new mount; otherwise prepend the target mount.\n  let newPathSegments: string[];\n  if (segments.length >= 2 && segments[1].startsWith(\"_\")) {\n    const segMountId = segments[1].slice(1);\n    const canonical = canonicalMount(segMountId);\n    if (KNOWN_MOUNTS.includes(canonical as (typeof KNOWN_MOUNTS)[number])) {\n      newPathSegments = [`_${canonical}`, ...segments.slice(2)];\n    } else {\n      newPathSegments = [`_${targetMount}`, ...segments.slice(1)];\n    }\n  } else {\n    newPathSegments = [`_${targetMount}`, ...segments.slice(1)];\n  }\n  const newPath =\n    \"/\" +\n    newPathSegments.join(\"/\") +\n    (oldPath.endsWith(\"/\") && newPathSegments.length >= 1 ? \"/\" : \"\");\n  const next = source.replace(match[0], match[0].replace(oldPath, newPath));\n\n  // New file location: drop the old mount directory. If the next directory is\n  // already the right mount, keep it; otherwise insert the target mount dir.\n  const parts = file.split(\"/\");\n  const mountIdx = parts.indexOf(oldMountDir);\n  if (mountIdx === -1) return null;\n\n  let insertTargetMount = true;\n  if (mountIdx + 1 < parts.length) {\n    const nextSeg = parts[mountIdx + 1];\n    if (nextSeg.startsWith(\"_\")) {\n      const nextMountId = nextSeg.slice(1);\n      const canonical = canonicalMount(nextMountId);\n      if (KNOWN_MOUNTS.includes(canonical as (typeof KNOWN_MOUNTS)[number])) {\n        insertTargetMount = false;\n      }\n    }\n  }\n\n  const newParts = insertTargetMount\n    ? [...parts.slice(0, mountIdx), `_${targetMount}`, ...parts.slice(mountIdx + 1)]\n    : [...parts.slice(0, mountIdx), ...parts.slice(mountIdx + 1)];\n  const newFile = newParts.join(\"/\");\n\n  return { next, newFile };\n};\n\nexport const migrateRouteMounts = (\n  projectDir: string,\n  dryRun: boolean,\n): Effect.Effect<StepResult, Error> =>\n  Effect.gen(function* () {\n    const writes: string[] = [];\n    const deletes: string[] = [];\n    const notes: string[] = [];\n\n    const files = (yield* listFiles(projectDir, ROUTES_GLOB)).filter(\n      (f) => !f.endsWith(\".test.ts\"),\n    );\n\n    // Collect files whose route path is under an unknown mount\n    const rehome: Array<{ file: string; source: string; targetMount: string }> = [];\n\n    for (const file of files) {\n      const source = yield* readText(projectDir, file);\n      const match = source.match(ROUTE_FILE_RE);\n      if (!match) continue;\n      const segments = match[1].split(\"/\").filter(Boolean);\n      if (segments.length === 0) continue;\n      const mountId = mountIdOf(match[1]);\n      if (!mountId || isRegistered(mountId)) continue;\n\n      // Target mount precedence: the mount's own canonical alias\n      // (e.g. _auth → authenticated), then a registered mount found in later\n      // path segments (e.g. /_layout/_authenticated/dashboard → _authenticated),\n      // then MOUNT_REDIRECTS, then \"public\".\n      let targetMount = \"public\";\n      const ownCanonical = canonicalMount(mountId);\n      if (KNOWN_MOUNTS.includes(ownCanonical as (typeof KNOWN_MOUNTS)[number])) {\n        targetMount = ownCanonical;\n      } else {\n        for (let i = 1; i < segments.length; i++) {\n          const seg = segments[i];\n          if (!seg.startsWith(\"_\")) continue;\n          const segMountId = seg.slice(1);\n          const canonical = canonicalMount(segMountId);\n          if (KNOWN_MOUNTS.includes(canonical as (typeof KNOWN_MOUNTS)[number])) {\n            targetMount = canonical;\n            break;\n          }\n        }\n        if (targetMount === \"public\" && MOUNT_REDIRECTS[mountId]) {\n          targetMount = MOUNT_REDIRECTS[mountId];\n        }\n      }\n      rehome.push({ file, source, targetMount });\n    }\n\n    if (rehome.length === 0) {\n      notes.push(\"no routes on unknown mounts — nothing to rehome\");\n      return { writes, deletes, notes };\n    }\n\n    // Create mount layout files for each target mount\n    const targetMounts = [...new Set(rehome.map((r) => r.targetMount))];\n    for (const targetMount of targetMounts) {\n      const path = mountLayoutFile(targetMount);\n      if (yield* fileExists(projectDir, path)) {\n        notes.push(`${path} exists — left untouched`);\n        continue;\n      }\n      if (!dryRun) yield* writeText(projectDir, path, renderMountLayout(targetMount));\n      writes.push(path);\n      notes.push(`created mount layout ${path}`);\n    }\n\n    // Rehome each route file\n    for (const { file, source, targetMount } of rehome) {\n      const result = rehomeRoute(file, source, targetMount);\n      if (!result) {\n        notes.push(`${file}: could not parse route path — left for manual rehome`);\n        continue;\n      }\n      const { next, newFile } = result;\n      if (next !== source && !dryRun) {\n        yield* writeText(projectDir, newFile, next);\n      }\n      writes.push(newFile);\n      if (!dryRun) {\n        yield* removePath(projectDir, file);\n      }\n      deletes.push(file);\n      notes.push(`rehomed ${file} → ${newFile}`);\n    }\n\n    // Remove old mount layout files and empty mount directories\n    for (const mountId of Object.keys(MOUNT_REDIRECTS)) {\n      const layoutPath = mountLayoutFile(mountId);\n      if (yield* fileExists(projectDir, layoutPath)) {\n        if (!dryRun) yield* removePath(projectDir, layoutPath);\n        deletes.push(layoutPath);\n        notes.push(`removed obsolete mount layout ${layoutPath}`);\n      }\n    }\n\n    notes.push(\n      \"mount layouts are minimal Outlet shells — migrate any chrome (Header/Footer, session) from the old layout into the new mount manually\",\n    );\n\n    return { writes, deletes, notes };\n  });\n"],"mappings":"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"}