{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import type { RouteSegment, ParsedRoute } from \"./types\";\nimport path from \"path\";\nimport {\n  assertBrowserStableRoutePath,\n  assertTerminalCatchAll,\n  assertUniqueRouteParameters,\n} from \"./routing/specificity\";\nimport { searchParamsToObject } from \"./search-params\";\nimport { decodeRouteSegment } from \"./utils/decode\";\n\nexport function parseRoutePath(filePath: string): ParsedRoute {\n  const segments: RouteSegment[] = [];\n  const normalizedPath = filePath.replace(/\\\\/g, \"/\");\n  const pathParts = normalizedPath.split(\"/\").filter(Boolean);\n\n  const fileName = pathParts.pop() || \"\";\n  const fileType = getRouteType(fileName);\n  const routePath = `/${pathParts.join(\"/\")}`;\n  assertBrowserStableRoutePath(routePath);\n  assertTerminalCatchAll(routePath);\n  assertUniqueRouteParameters(routePath);\n\n  for (const part of pathParts) {\n    // Route groups like `(marketing)` organize files without adding URL\n    // segments. Mirrors isRouteGroup in router.ts for programmatic routes.\n    if (part.startsWith(\"(\") && part.endsWith(\")\")) continue;\n    if (part.startsWith(\"[\") && part.endsWith(\"]\")) {\n      let segment = part.slice(1, -1);\n      const isDynamic = true;\n      let isOptional = false;\n      let isCatchAll = false;\n\n      if (segment.startsWith(\"[\") && segment.endsWith(\"]\")) {\n        isOptional = true;\n        segment = segment.slice(1, -1);\n        if (segment.startsWith(\"...\")) {\n          segment = segment.slice(3);\n          isCatchAll = true;\n        }\n      } else if (segment.startsWith(\"...\")) {\n        segment = segment.slice(3);\n        isCatchAll = true;\n      }\n\n      segments.push({ segment, isDynamic, isOptional, isCatchAll });\n    } else {\n      segments.push({\n        segment: part,\n        isDynamic: false,\n        isOptional: false,\n        isCatchAll: false,\n      });\n    }\n  }\n\n  return {\n    segments,\n    filePath: normalizedPath,\n    type: fileType,\n  };\n}\n\nfunction getRouteType(fileName: string): ParsedRoute[\"type\"] {\n  const baseName = fileName.replace(/\\.(tsx?|jsx?|vue|svelte|mdx?|markdown)$/, \"\");\n\n  switch (baseName) {\n    case \"page\":\n      return \"page\";\n    case \"layout\":\n      return \"layout\";\n    case \"loading\":\n      return \"loading\";\n    case \"error\":\n      return \"error\";\n    case \"not-found\":\n      return \"not-found\";\n    default:\n      return \"page\";\n  }\n}\n\nexport function segmentsToPattern(segments: RouteSegment[]): string {\n  if (segments.length === 0) return \"/\";\n\n  return (\n    \"/\" +\n    segments\n      .map((segment) => {\n        if (!segment.isDynamic) return segment.segment;\n\n        if (segment.isCatchAll) {\n          return segment.isOptional ? `*${segment.segment}?` : `*${segment.segment}`;\n        }\n\n        return `:${segment.segment}`;\n      })\n      .join(\"/\")\n  );\n}\n\nexport function matchRoute(\n  url: string,\n  segments: RouteSegment[],\n): { params: Record<string, string>; matches: boolean } {\n  const urlParts = url.split(\"/\").filter(Boolean).map(decodeRouteSegment);\n  const params: Record<string, string> = {};\n  if (segments.length === 0) {\n    return { params, matches: urlParts.length === 0 };\n  }\n\n  let urlIndex = 0;\n  let segmentIndex = 0;\n\n  while (segmentIndex < segments.length && urlIndex <= urlParts.length) {\n    const segment = segments[segmentIndex];\n\n    if (!segment.isDynamic) {\n      if (urlParts[urlIndex] !== segment.segment) {\n        return { params: {}, matches: false };\n      }\n      urlIndex++;\n      segmentIndex++;\n    } else if (segment.isCatchAll) {\n      const remainingParts = urlParts.slice(urlIndex);\n\n      if (remainingParts.length === 0 && !segment.isOptional) {\n        return { params: {}, matches: false };\n      }\n\n      params[segment.segment] = remainingParts.join(\"/\");\n      urlIndex = urlParts.length;\n      segmentIndex++;\n    } else {\n      if (urlIndex >= urlParts.length) {\n        return { params: {}, matches: false };\n      }\n\n      params[segment.segment] = urlParts[urlIndex];\n      urlIndex++;\n      segmentIndex++;\n    }\n  }\n\n  const matches = segmentIndex === segments.length && urlIndex === urlParts.length;\n\n  return { params, matches };\n}\n\n/** Match a route segment chain as an owner of the pathname or one of its descendants. */\nexport function matchRoutePrefix(url: string, segments: RouteSegment[]): boolean {\n  const urlParts = url.split(\"/\").filter(Boolean).map(decodeRouteSegment);\n  let urlIndex = 0;\n\n  for (const segment of segments) {\n    if (segment.isCatchAll) {\n      return segment.isOptional || urlIndex < urlParts.length;\n    }\n\n    const urlPart = urlParts[urlIndex];\n    if (urlPart === undefined) return false;\n    if (!segment.isDynamic && segment.segment !== urlPart) return false;\n    urlIndex++;\n  }\n\n  return true;\n}\n\nexport function resolveAppPath(root: string, ...paths: string[]): string {\n  return path.resolve(root, ...paths);\n}\n\n/**\n * Convert an absolute module path inside the project root to a root-relative\n * URL path with forward slashes (e.g. `/src/app/page.tsx`), for values the\n * client passes to dynamic `import()`. On Windows the naive root-prefix slice\n * yields `\\src\\app\\page.tsx`, which is not a valid module specifier. Returns\n * undefined for paths outside the root so callers keep their own fallbacks.\n */\nexport function toRootRelativeUrlPath(\n  absolutePath: string,\n  projectRoot: string,\n): string | undefined {\n  if (absolutePath === projectRoot) return \"\";\n  if (absolutePath.startsWith(`${projectRoot}/`) || absolutePath.startsWith(`${projectRoot}\\\\`)) {\n    return absolutePath.slice(projectRoot.length).replace(/\\\\/g, \"/\");\n  }\n  return undefined;\n}\n\n/**\n * Normalize a filesystem path to forward slashes. Node's fs accepts these on\n * every platform, and module ids handed to bundlers (e.g. Nitro handler and\n * task entries) must not contain backslashes.\n */\nexport function toPosixPath(filePath: string): string {\n  return filePath.replace(/\\\\/g, \"/\");\n}\n\nexport function toViteModuleId(filePath: string, root: string): string {\n  if (!path.isAbsolute(filePath)) return filePath;\n\n  const relativePath = path.relative(root, filePath);\n  if (relativePath && !relativePath.startsWith(\"..\") && !path.isAbsolute(relativePath)) {\n    return `/${relativePath.split(path.sep).join(\"/\")}`;\n  }\n\n  const normalizedPath = filePath.replace(/\\\\/g, \"/\");\n  return normalizedPath.startsWith(\"/\") ? `/@fs${normalizedPath}` : `/@fs/${normalizedPath}`;\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n  try {\n    const fs = await import(\"fs/promises\");\n    await fs.access(filePath);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nexport async function globFiles(pattern: string, cwd: string): Promise<string[]> {\n  const glob = await import(\"fast-glob\");\n  return glob.default(pattern, { cwd, absolute: false });\n}\n\nexport function parseSearchParams(\n  searchParams: URLSearchParams,\n): Record<string, string | string[]> {\n  return searchParamsToObject(searchParams) as Record<string, string | string[]>;\n}\n\nexport const logger = {\n  info: (message: string) => console.log(`[info] ${message}`),\n  success: (message: string) => console.log(`[success] ${message}`),\n  warn: (message: string) => console.warn(`⚠️  ${message}`),\n  error: (message: string) => console.error(`❌ ${message}`),\n  ready: (message: string) => console.log(`${message}`),\n  event: (message: string) => console.log(`  ${message}`),\n};\n"],"mappings":"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","names":[]}