{"version":3,"file":"getRouteNodes.cjs","names":[],"sources":["../../../../src/filesystem/virtual/getRouteNodes.ts"],"sourcesContent":["import path, { join, resolve } from 'node:path'\nimport {\n  cleanPath,\n  createLiteralRoutePathSegmentMetadata,\n  createRoutePathSegmentMetadata,\n  determineInitialRoutePath,\n  joinRoutePathSegmentMetadata,\n  removeExt,\n  removeLeadingSlash,\n  removeTrailingSlash,\n  replaceBackslash,\n  routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesPhysical } from '../physical/getRouteNodes'\nimport { rootPathId } from '../physical/rootPathId'\nimport { virtualRootRouteSchema } from './config'\nimport { loadConfigFile } from './loadConfigFile'\nimport type {\n  VirtualRootRoute,\n  VirtualRouteNode,\n} from '@tanstack/virtual-file-routes'\nimport type { GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\nimport type { TokenRegexBundle } from '../physical/getRouteNodes'\n\nfunction ensureLeadingUnderScore(id: string) {\n  if (id.startsWith('_')) {\n    return id\n  }\n  return `_${id}`\n}\n\nfunction flattenTree(node: RouteNode): Array<RouteNode> {\n  const result = [node]\n\n  if (node.children) {\n    for (const child of node.children) {\n      result.push(...flattenTree(child))\n    }\n  }\n  delete node.children\n\n  return result\n}\n\nexport async function getRouteNodes(\n  tsrConfig: Pick<\n    Config,\n    | 'routesDirectory'\n    | 'virtualRouteConfig'\n    | 'routeFileIgnorePrefix'\n    | 'disableLogging'\n    | 'indexToken'\n    | 'routeToken'\n  >,\n  root: string,\n  tokenRegexes: TokenRegexBundle,\n): Promise<GetRouteNodesResult> {\n  const fullDir = resolve(tsrConfig.routesDirectory)\n  if (tsrConfig.virtualRouteConfig === undefined) {\n    throw new Error(`virtualRouteConfig is undefined`)\n  }\n  let virtualRouteConfig: VirtualRootRoute\n  if (typeof tsrConfig.virtualRouteConfig === 'string') {\n    virtualRouteConfig = await getVirtualRouteConfigFromFileExport(\n      tsrConfig,\n      root,\n    )\n  } else {\n    virtualRouteConfig = tsrConfig.virtualRouteConfig\n  }\n  const { children, physicalDirectories } = await getRouteNodesRecursive(\n    tsrConfig,\n    root,\n    fullDir,\n    virtualRouteConfig.children,\n    tokenRegexes,\n  )\n  const allNodes = flattenTree({\n    children,\n    filePath: virtualRouteConfig.file,\n    fullPath: replaceBackslash(join(fullDir, virtualRouteConfig.file)),\n    variableName: 'root',\n    routePath: `/${rootPathId}`,\n    _fsRouteType: '__root',\n  })\n\n  const rootRouteNode = allNodes[0]\n  const routeNodes = allNodes.slice(1)\n\n  return { rootRouteNode, routeNodes, physicalDirectories }\n}\n\n/**\n * Get the virtual route config from a file export\n *\n * @example\n * ```ts\n * // routes.ts\n * import { rootRoute } from '@tanstack/virtual-file-routes'\n *\n * export const routes = rootRoute({ ... })\n * // or\n * export default rootRoute({ ... })\n * ```\n *\n */\nasync function getVirtualRouteConfigFromFileExport(\n  tsrConfig: Pick<Config, 'virtualRouteConfig'>,\n  root: string,\n): Promise<VirtualRootRoute> {\n  if (\n    tsrConfig.virtualRouteConfig === undefined ||\n    typeof tsrConfig.virtualRouteConfig !== 'string' ||\n    tsrConfig.virtualRouteConfig === ''\n  ) {\n    throw new Error(`virtualRouteConfig is undefined or empty`)\n  }\n  const exports = await loadConfigFile(join(root, tsrConfig.virtualRouteConfig))\n\n  if (!('routes' in exports) && !('default' in exports)) {\n    throw new Error(\n      `routes not found in ${tsrConfig.virtualRouteConfig}. The routes export must be named like 'export const routes = ...' or done using 'export default ...'`,\n    )\n  }\n\n  const virtualRouteConfig =\n    'routes' in exports ? exports.routes : exports.default\n\n  return virtualRootRouteSchema.parse(virtualRouteConfig)\n}\n\nexport async function getRouteNodesRecursive(\n  tsrConfig: Pick<\n    Config,\n    | 'routesDirectory'\n    | 'routeFileIgnorePrefix'\n    | 'disableLogging'\n    | 'indexToken'\n    | 'routeToken'\n  >,\n  root: string,\n  fullDir: string,\n  nodes: Array<VirtualRouteNode> | undefined,\n  tokenRegexes: TokenRegexBundle,\n  parent?: RouteNode,\n): Promise<{ children: Array<RouteNode>; physicalDirectories: Array<string> }> {\n  if (nodes === undefined) {\n    return { children: [], physicalDirectories: [] }\n  }\n  const allPhysicalDirectories: Array<string> = []\n  const children = await Promise.all(\n    nodes.map(async (node) => {\n      if (node.type === 'physical') {\n        const {\n          routePath: routePathPrefix,\n          originalRoutePath: originalRoutePathPrefix,\n        } = node.pathPrefix\n          ? determineInitialRoutePath(removeLeadingSlash(node.pathPrefix))\n          : { routePath: '', originalRoutePath: '' }\n        const { routeNodes, physicalDirectories } = await getRouteNodesPhysical(\n          {\n            ...tsrConfig,\n            routesDirectory: resolve(fullDir, node.directory),\n          },\n          root,\n          tokenRegexes,\n        )\n        allPhysicalDirectories.push(\n          resolve(fullDir, node.directory),\n          ...physicalDirectories,\n        )\n        routeNodes.forEach((subtreeNode) => {\n          const pathPrefix = cleanPath(\n            `${parent?.routePath ?? ''}${routePathPrefix}`,\n          )\n          const originalPathPrefix = cleanPath(\n            `${parent?.originalRoutePath ?? parent?.routePath ?? ''}${originalRoutePathPrefix}`,\n          )\n          const literalPathPrefixSegments =\n            createLiteralRoutePathSegmentMetadata(pathPrefix, parent, true)\n          const routePath = cleanPath(`${pathPrefix}${subtreeNode.routePath}`)\n          subtreeNode.variableName = routePathToVariable(\n            `${routePathPrefix}/${removeExt(subtreeNode.filePath)}`,\n          )\n          subtreeNode._routePathSegmentMetadata = joinRoutePathSegmentMetadata(\n            routePath,\n            pathPrefix,\n            literalPathPrefixSegments,\n            subtreeNode._routePathSegmentMetadata,\n          )\n          subtreeNode.routePath = routePath\n          // Keep originalRoutePath aligned with routePath for escape detection\n          if (subtreeNode.originalRoutePath) {\n            subtreeNode.originalRoutePath = cleanPath(\n              `${originalPathPrefix}${subtreeNode.originalRoutePath}`,\n            )\n          }\n          subtreeNode.filePath = `${node.directory}/${subtreeNode.filePath}`\n        })\n        return routeNodes\n      }\n\n      function getFile(file: string) {\n        const filePath = file\n        const variableName = routePathToVariable(removeExt(filePath))\n        const fullPath = replaceBackslash(join(fullDir, filePath))\n        return { filePath, variableName, fullPath }\n      }\n      const parentRoutePath = removeTrailingSlash(parent?.routePath ?? '/')\n      const parentOriginalRoutePath = removeTrailingSlash(\n        parent?.originalRoutePath ?? parent?.routePath ?? '/',\n      )\n      const virtualParentRoutePath = parent?.routePath ?? `/${rootPathId}`\n\n      switch (node.type) {\n        case 'index': {\n          const { filePath, variableName, fullPath } = getFile(node.file)\n          const routePath = `${parentRoutePath}/`\n          const originalRoutePath = `${parentOriginalRoutePath}/`\n          return {\n            filePath,\n            fullPath,\n            variableName,\n            routePath,\n            originalRoutePath,\n            _routePathSegmentMetadata: parent?._routePathSegmentMetadata\n              ? [...parent._routePathSegmentMetadata]\n              : undefined,\n            _fsRouteType: 'static',\n            _virtualParentRoutePath: virtualParentRoutePath,\n          } satisfies RouteNode\n        }\n\n        case 'route': {\n          const lastSegment = node.path\n          let routeNode: RouteNode\n\n          // Process the segment to handle escape sequences like [_]\n          const {\n            routePath: escapedSegment,\n            originalRoutePath: originalSegment,\n          } = determineInitialRoutePath(removeLeadingSlash(lastSegment))\n          const routePath = `${parentRoutePath}${escapedSegment}`\n          const originalRoutePath = `${parentOriginalRoutePath}${originalSegment}`\n          const routePathSegmentMetadata =\n            createLiteralRoutePathSegmentMetadata(routePath, parent, true)\n\n          if (node.file) {\n            const { filePath, variableName, fullPath } = getFile(node.file)\n            routeNode = {\n              filePath,\n              fullPath,\n              variableName,\n              routePath,\n              originalRoutePath,\n              _routePathSegmentMetadata: routePathSegmentMetadata,\n              _fsRouteType: 'static',\n              _virtualParentRoutePath: virtualParentRoutePath,\n            }\n          } else {\n            routeNode = {\n              filePath: '',\n              fullPath: '',\n              variableName: routePathToVariable(routePath),\n              routePath,\n              originalRoutePath,\n              _routePathSegmentMetadata: routePathSegmentMetadata,\n              isVirtual: true,\n              _fsRouteType: 'static',\n              _virtualParentRoutePath: virtualParentRoutePath,\n            }\n          }\n\n          if (node.children !== undefined) {\n            const { children, physicalDirectories } =\n              await getRouteNodesRecursive(\n                tsrConfig,\n                root,\n                fullDir,\n                node.children,\n                tokenRegexes,\n                routeNode,\n              )\n            routeNode.children = children\n            allPhysicalDirectories.push(...physicalDirectories)\n\n            // If the route has children, it should be a layout\n            routeNode._fsRouteType = 'layout'\n          }\n          return routeNode\n        }\n        case 'layout': {\n          const { filePath, variableName, fullPath } = getFile(node.file)\n\n          if (node.id !== undefined) {\n            node.id = ensureLeadingUnderScore(node.id)\n          } else {\n            const baseName = path.basename(filePath)\n            const fileNameWithoutExt = path.parse(baseName).name\n            node.id = ensureLeadingUnderScore(fileNameWithoutExt)\n          }\n          const lastSegment = node.id\n          // Process the segment to handle escape sequences like [_]\n          const {\n            routePath: escapedSegment,\n            originalRoutePath: originalSegment,\n          } = determineInitialRoutePath(removeLeadingSlash(lastSegment))\n          const routePath = `${parentRoutePath}${escapedSegment}`\n          // Store the original path with brackets for escape detection\n          const originalRoutePath = `${parentOriginalRoutePath}${originalSegment}`\n          const routePathSegmentMetadata = joinRoutePathSegmentMetadata(\n            routePath,\n            parentRoutePath,\n            parent?._routePathSegmentMetadata,\n            createRoutePathSegmentMetadata(escapedSegment, originalSegment),\n          )\n\n          const routeNode: RouteNode = {\n            fullPath,\n            filePath,\n            variableName,\n            routePath,\n            originalRoutePath,\n            _routePathSegmentMetadata: routePathSegmentMetadata,\n            _fsRouteType: 'pathless_layout',\n            _virtualParentRoutePath: virtualParentRoutePath,\n          }\n\n          if (node.children !== undefined) {\n            const { children, physicalDirectories } =\n              await getRouteNodesRecursive(\n                tsrConfig,\n                root,\n                fullDir,\n                node.children,\n                tokenRegexes,\n                routeNode,\n              )\n            routeNode.children = children\n            allPhysicalDirectories.push(...physicalDirectories)\n          }\n          return routeNode\n        }\n      }\n    }),\n  )\n  return {\n    children: children.flat(),\n    physicalDirectories: allPhysicalDirectories,\n  }\n}\n"],"mappings":"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"}