{"version":3,"file":"getRouteNodes.cjs","names":[],"sources":["../../../../src/filesystem/virtual/getRouteNodes.ts"],"sourcesContent":["import path, { join, resolve } from 'node:path'\nimport {\n  cleanPath,\n  determineInitialRoutePath,\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 { 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          subtreeNode.variableName = routePathToVariable(\n            `${node.pathPrefix}/${removeExt(subtreeNode.filePath)}`,\n          )\n          subtreeNode.routePath = cleanPath(\n            `${parent?.routePath ?? ''}${node.pathPrefix}${subtreeNode.routePath}`,\n          )\n          // Keep originalRoutePath aligned with routePath for escape detection\n          if (subtreeNode.originalRoutePath) {\n            subtreeNode.originalRoutePath = cleanPath(\n              `${parent?.routePath ?? ''}${node.pathPrefix}${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 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          return {\n            filePath,\n            fullPath,\n            variableName,\n            routePath,\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          // Store the original path with brackets for escape detection\n          const originalRoutePath = `${parentRoutePath}${originalSegment}`\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              _fsRouteType: 'static',\n              _virtualParentRoutePath: virtualParentRoutePath,\n            }\n          } else {\n            routeNode = {\n              filePath: '',\n              fullPath: '',\n              variableName: routePathToVariable(routePath),\n              routePath,\n              originalRoutePath,\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 = `${parentRoutePath}${originalSegment}`\n\n          const routeNode: RouteNode = {\n            fullPath,\n            filePath,\n            variableName,\n            routePath,\n            originalRoutePath,\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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"}