{"version":3,"file":"path.cjs","names":[],"sources":["../../src/path.ts"],"sourcesContent":["import { last } from './utils'\nimport {\n  SEGMENT_TYPE_OPTIONAL_PARAM,\n  SEGMENT_TYPE_PARAM,\n  SEGMENT_TYPE_WILDCARD,\n} from './new-process-route-tree'\nimport type { SieveCache } from './sieve-cache'\nimport type { DynamicPathSegment } from './new-process-route-tree'\nimport type { AnyRoute } from './route'\n\n/** Join path segments, cleaning duplicate slashes between parts. */\nexport function joinPaths(paths: Array<string | undefined>) {\n  return cleanPath(\n    paths\n      .filter((val) => {\n        return val !== undefined\n      })\n      .join('/'),\n  )\n}\n\n/** Remove repeated slashes from a path string. */\nexport function cleanPath(path: string) {\n  // remove double slashes\n  return path.replace(/\\/{2,}/g, '/')\n}\n\n/** Trim leading slashes (except preserving root '/'). */\nexport function trimPathLeft(path: string) {\n  return path === '/' ? path : path.replace(/^\\/+/, '')\n}\n\n/** Trim trailing slashes (except preserving root '/'). */\nexport function trimPathRight(path: string) {\n  const len = path.length\n  return len > 1 && path[len - 1] === '/' ? path.replace(/\\/+$/, '') : path\n}\n\n/** Trim both leading and trailing slashes. */\nexport function trimPath(path: string) {\n  return trimPathRight(trimPathLeft(path))\n}\n\n/** Remove a trailing slash from value when appropriate for comparisons. */\nexport function removeTrailingSlash(value: string, basepath: string): string {\n  if (value?.endsWith('/') && value !== '/' && value !== `${basepath}/`) {\n    return value.slice(0, -1)\n  }\n  return value\n}\n\n// intended to only compare path name\n// see the usage in the isActive under useLinkProps\n// /sample/path1 = /sample/path1/\n// /sample/path1/some <> /sample/path1\n/**\n * Compare two pathnames for exact equality after normalizing trailing slashes\n * relative to the provided `basepath`.\n */\nexport function exactPathTest(\n  pathName1: string,\n  pathName2: string,\n  basepath: string,\n): boolean {\n  return (\n    removeTrailingSlash(pathName1, basepath) ===\n    removeTrailingSlash(pathName2, basepath)\n  )\n}\n\n// When resolving relative paths, we treat all paths as if they are trailing slash\n// documents. All trailing slashes are removed after the path is resolved.\n// Here are a few examples:\n//\n// /a/b/c + ./d = /a/b/c/d\n// /a/b/c + ../d = /a/b/d\n// /a/b/c + ./d/ = /a/b/c/d\n// /a/b/c + ../d/ = /a/b/d\n// /a/b/c + ./ = /a/b/c\n//\n// Absolute paths that start with `/` short circuit the resolution process to the root\n// path.\n//\n// Here are some examples:\n//\n// /a/b/c + /d = /d\n// /a/b/c + /d/ = /d\n// /a/b/c + / = /\n//\n// Non-.-prefixed paths are still treated as relative paths, resolved like `./`\n//\n// Here are some examples:\n//\n// /a/b/c + d = /a/b/c/d\n// /a/b/c + d/ = /a/b/c/d\n// /a/b/c + d/e = /a/b/c/d/e\ninterface ResolvePathOptions {\n  base: string\n  to: string\n  trailingSlash?: 'always' | 'never' | 'preserve'\n  cache?: SieveCache<string, string>\n}\n\n/**\n * Resolve a destination path against a base, honoring trailing-slash policy\n * and supporting relative segments (`.`/`..`) and absolute `to` values.\n */\nexport function resolvePath({\n  base,\n  to,\n  trailingSlash = 'never',\n  cache,\n}: ResolvePathOptions) {\n  if (to.includes('//')) {\n    to = cleanPath(to)\n  }\n\n  if (to.startsWith('/')) {\n    if (to.length === 1 || trailingSlash === 'preserve') {\n      return to\n    }\n    if (trailingSlash === 'always') {\n      return to.endsWith('/') ? to : `${to}/`\n    }\n    return to.endsWith('/') ? to.slice(0, -1) : to\n  }\n\n  const isBase = to === '.'\n  let key\n  if (cache) {\n    // `trailingSlash` is static per router, so it doesn't need to be part of the cache key\n    key = isBase ? base : base + '\\0' + to\n    const cached = cache.get(key)\n    if (cached) return cached\n  }\n\n  let baseSegments: Array<string>\n  if (isBase) {\n    baseSegments = base.split('/')\n  } else {\n    if (base.includes('//')) {\n      base = cleanPath(base)\n    }\n    baseSegments = base.split('/')\n    while (baseSegments.length > 1 && last(baseSegments) === '') {\n      baseSegments.pop()\n    }\n\n    const toSegments = to.split('/')\n    for (let index = 0, length = toSegments.length; index < length; index++) {\n      const value = toSegments[index]!\n      if (value === '') {\n        if (!index) {\n          // Leading slash\n          baseSegments = [value]\n        } else if (index === length - 1) {\n          // Trailing Slash\n          baseSegments.push(value)\n        } else {\n          // ignore inter-slashes\n        }\n      } else if (value === '..') {\n        if (baseSegments.length > 1) {\n          baseSegments.pop()\n        } else {\n          baseSegments = ['']\n        }\n      } else if (value === '.') {\n        // ignore\n      } else {\n        baseSegments.push(value)\n      }\n    }\n  }\n\n  if (baseSegments.length > 1) {\n    if (last(baseSegments) === '') {\n      if (trailingSlash === 'never') {\n        baseSegments.pop()\n      }\n    } else if (trailingSlash === 'always') {\n      baseSegments.push('')\n    }\n  }\n\n  const joined = baseSegments.join('/')\n  const result = (isBase ? cleanPath(joined) : joined) || '/'\n  if (key && cache) cache.set(key, result)\n  return result\n}\n\n/**\n * Create a pre-compiled decode config from allowed characters.\n * Created once for the router's fixed encoding configuration.\n */\nexport function compileDecodeCharMap(\n  pathParamsAllowedCharacters: ReadonlyArray<string>,\n) {\n  const charMap = new Map(\n    pathParamsAllowedCharacters.map((char) => [encodeURIComponent(char), char]),\n  )\n  // Encoded keys contain no '|', and only these four regexp metacharacters.\n  const regex = new RegExp(\n    [...charMap.keys()].join('|').replace(/[.*()]/g, '\\\\$&'),\n    'g',\n  )\n  return (encoded: string) =>\n    encoded.replace(regex, (match) => charMap.get(match) ?? match)\n}\n\nexport type InterpolationSegment = string | DynamicPathSegment\n\nexport type RouteInterpolation = Array<InterpolationSegment> & {\n  names?: Array<string>\n}\n\nexport function getRouteSegments(route: AnyRoute) {\n  return route._interpolation\n}\n\n/** A splat is missing when it has no value; `0` and `false` are stringified like any other param. */\nfunction isMissingSplat(value: unknown): boolean {\n  return value == null || value === ''\n}\n\n/** Devtools checks navigation availability separately from the hot formatter. */\nexport function hasMissingPathParams(\n  segments: RouteInterpolation,\n  params: Record<string, unknown>,\n): boolean {\n  return segments.some((part) => {\n    if (typeof part === 'string') {\n      return false\n    }\n    const [kind, key] = part\n    return kind === SEGMENT_TYPE_WILDCARD\n      ? isMissingSplat(params[key])\n      : kind === SEGMENT_TYPE_PARAM && !(key in params)\n  })\n}\n\nfunction encodeParam(\n  key: string,\n  value: unknown,\n  decoder: ((encoded: string) => string) | undefined,\n): string {\n  if (typeof value !== 'string') {\n    return '' + (value ?? undefined)\n  }\n\n  const splat = key === '_splat'\n  // Early return if the splat contains only URL-safe characters.\n  if (splat && (!value || /^[a-zA-Z0-9\\-._~!/]*$/.test(value))) {\n    return value\n  }\n  let encoded = encodeURIComponent(value)\n  if (splat) {\n    // Splats preserve '/', but still encode spaces, '+', '?' and '#'.\n    // Restore separators before allowed characters can decode a literal '%2F'.\n    encoded = encoded.replaceAll('%2F', '/')\n  }\n  return decoder ? decoder(encoded) : encoded\n}\n\n/** Substitute current values into parsed segments, optionally collecting raw params. */\nexport function interpolatePath(\n  path: string,\n  segments: RouteInterpolation,\n  params: Record<string, unknown>,\n  decoder?: (encoded: string) => string,\n  usedParams?: Record<string, unknown>,\n): string {\n  // One parsed template serves both trailing-slash variants.\n  const trailingSlash = path.endsWith('/') ? '/' : ''\n  let joined = ''\n  for (const part of segments) {\n    if (typeof part === 'string') {\n      joined += part\n      continue\n    }\n    const [kind, key, prefix, rawSuffix] = part\n    const splat = kind === SEGMENT_TYPE_WILDCARD\n    const suffix =\n      splat && rawSuffix !== undefined ? rawSuffix + trailingSlash : rawSuffix\n    let paramValue = params[key]\n    // An omitted optional contributes neither a segment nor used-param metadata.\n    if (kind === SEGMENT_TYPE_OPTIONAL_PARAM && paramValue == null) {\n      continue\n    }\n    if (usedParams) {\n      // Match identity needs current raw values, never data retained from another call.\n      usedParams[key] = paramValue\n      // TODO: Deprecate *\n      if (splat) {\n        usedParams['*'] = paramValue\n      }\n    }\n    if (splat && isMissingSplat(paramValue)) {\n      // A missing wildcard keeps its affixes, but omits a bare segment.\n      if (prefix === '/' && !suffix) {\n        continue\n      }\n      paramValue = ''\n    }\n\n    joined += prefix + encodeParam(key, paramValue, decoder) + (suffix || '')\n  }\n\n  return joined + trailingSlash || '/'\n}\n"],"mappings":"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"}