@tanstack/router-generator 1.166.9 → 1.166.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/cjs/config.cjs +111 -147
  3. package/dist/cjs/config.cjs.map +1 -1
  4. package/dist/cjs/filesystem/physical/getRouteNodes.cjs +224 -303
  5. package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
  6. package/dist/cjs/filesystem/physical/rootPathId.cjs +5 -4
  7. package/dist/cjs/filesystem/physical/rootPathId.cjs.map +1 -1
  8. package/dist/cjs/filesystem/virtual/config.cjs +32 -30
  9. package/dist/cjs/filesystem/virtual/config.cjs.map +1 -1
  10. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +164 -209
  11. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
  12. package/dist/cjs/filesystem/virtual/loadConfigFile.cjs +9 -8
  13. package/dist/cjs/filesystem/virtual/loadConfigFile.cjs.map +1 -1
  14. package/dist/cjs/generator.cjs +766 -1106
  15. package/dist/cjs/generator.cjs.map +1 -1
  16. package/dist/cjs/index.cjs +32 -34
  17. package/dist/cjs/logger.cjs +28 -34
  18. package/dist/cjs/logger.cjs.map +1 -1
  19. package/dist/cjs/template.cjs +144 -151
  20. package/dist/cjs/template.cjs.map +1 -1
  21. package/dist/cjs/transform/transform.cjs +287 -426
  22. package/dist/cjs/transform/transform.cjs.map +1 -1
  23. package/dist/cjs/transform/utils.cjs +31 -33
  24. package/dist/cjs/transform/utils.cjs.map +1 -1
  25. package/dist/cjs/utils.cjs +534 -544
  26. package/dist/cjs/utils.cjs.map +1 -1
  27. package/dist/cjs/validate-route-params.cjs +66 -51
  28. package/dist/cjs/validate-route-params.cjs.map +1 -1
  29. package/dist/esm/config.js +106 -147
  30. package/dist/esm/config.js.map +1 -1
  31. package/dist/esm/filesystem/physical/getRouteNodes.js +220 -286
  32. package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
  33. package/dist/esm/filesystem/physical/rootPathId.js +6 -5
  34. package/dist/esm/filesystem/physical/rootPathId.js.map +1 -1
  35. package/dist/esm/filesystem/virtual/config.js +31 -30
  36. package/dist/esm/filesystem/virtual/config.js.map +1 -1
  37. package/dist/esm/filesystem/virtual/getRouteNodes.js +161 -208
  38. package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
  39. package/dist/esm/filesystem/virtual/loadConfigFile.js +7 -7
  40. package/dist/esm/filesystem/virtual/loadConfigFile.js.map +1 -1
  41. package/dist/esm/generator.js +756 -1083
  42. package/dist/esm/generator.js.map +1 -1
  43. package/dist/esm/index.js +4 -31
  44. package/dist/esm/logger.js +29 -35
  45. package/dist/esm/logger.js.map +1 -1
  46. package/dist/esm/template.js +144 -152
  47. package/dist/esm/template.js.map +1 -1
  48. package/dist/esm/transform/transform.js +285 -425
  49. package/dist/esm/transform/transform.js.map +1 -1
  50. package/dist/esm/transform/utils.js +31 -33
  51. package/dist/esm/transform/utils.js.map +1 -1
  52. package/dist/esm/utils.js +529 -564
  53. package/dist/esm/utils.js.map +1 -1
  54. package/dist/esm/validate-route-params.js +67 -52
  55. package/dist/esm/validate-route-params.js.map +1 -1
  56. package/package.json +5 -5
  57. package/dist/cjs/index.cjs.map +0 -1
  58. package/dist/esm/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/prefer-for-of */\nimport * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config, TokenMatcher } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\n/**\n * Prefix map for O(1) parent route lookups.\n * Maps each route path prefix to the route node that owns that prefix.\n * Enables finding longest matching parent without linear search.\n */\nexport class RoutePrefixMap {\n private prefixToRoute: Map<string, RouteNode> = new Map()\n private layoutRoutes: Array<RouteNode> = []\n\n constructor(routes: Array<RouteNode>) {\n for (const route of routes) {\n if (!route.routePath || route.routePath === `/${rootPathId}`) continue\n\n // Skip route pieces (lazy, loader, component, etc.) - they are merged with main routes\n // and should not be valid parent candidates\n if (\n route._fsRouteType === 'lazy' ||\n route._fsRouteType === 'loader' ||\n route._fsRouteType === 'component' ||\n route._fsRouteType === 'pendingComponent' ||\n route._fsRouteType === 'errorComponent' ||\n route._fsRouteType === 'notFoundComponent'\n ) {\n continue\n }\n\n // Index by exact path for direct lookups\n this.prefixToRoute.set(route.routePath, route)\n\n if (\n route._fsRouteType === 'pathless_layout' ||\n route._fsRouteType === 'layout' ||\n route._fsRouteType === '__root'\n ) {\n this.layoutRoutes.push(route)\n }\n }\n\n // Sort by path length descending for longest-match-first\n this.layoutRoutes.sort(\n (a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),\n )\n }\n\n /**\n * Find the longest matching parent route for a given path.\n * O(k) where k is the number of path segments, not O(n) routes.\n */\n findParent(routePath: string): RouteNode | null {\n if (!routePath || routePath === '/') return null\n\n // Walk up the path segments\n let searchPath = routePath\n while (searchPath.length > 0) {\n const lastSlash = searchPath.lastIndexOf('/')\n if (lastSlash <= 0) break\n\n searchPath = searchPath.substring(0, lastSlash)\n const parent = this.prefixToRoute.get(searchPath)\n if (parent && parent.routePath !== routePath) {\n return parent\n }\n }\n return null\n }\n\n /**\n * Check if a route exists at the given path.\n */\n has(routePath: string): boolean {\n return this.prefixToRoute.has(routePath)\n }\n\n /**\n * Get a route by exact path.\n */\n get(routePath: string): RouteNode | undefined {\n return this.prefixToRoute.get(routePath)\n }\n}\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n const len = arr.length\n // Pre-compute all accessor values to avoid repeated function calls during sort\n const indexed: Array<{ item: T; index: number; keys: Array<any> }> =\n new Array(len)\n for (let i = 0; i < len; i++) {\n const item = arr[i]!\n const keys = new Array(accessors.length)\n for (let j = 0; j < accessors.length; j++) {\n keys[j] = accessors[j]!(item)\n }\n indexed[i] = { item, index: i, keys }\n }\n\n indexed.sort((a, b) => {\n for (let j = 0; j < accessors.length; j++) {\n const ao = a.keys[j]\n const bo = b.keys[j]\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return a.index - b.index\n })\n\n const result: Array<T> = new Array(len)\n for (let i = 0; i < len; i++) {\n result[i] = indexed[i]!.item\n }\n return result\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\n/**\n * Characters that cannot be escaped in square brackets.\n * These are characters that would cause issues in URLs or file systems.\n */\nconst DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n])\n\nexport function determineInitialRoutePath(routePath: string) {\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n const parts = routePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `Error: Disallowed character \"${character}\" found in square brackets in route path \"${routePath}\".\\nYou cannot use any of the following characters in square brackets: ${Array.from(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n originalRoutePath,\n }\n}\n\n/**\n * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).\n * E.g., \"[index]\" -> true, \"[_layout]\" -> true, \"foo[.]bar\" -> false, \"index\" -> false\n */\nfunction isFullyEscapedSegment(originalSegment: string): boolean {\n return (\n originalSegment.startsWith('[') &&\n originalSegment.endsWith(']') &&\n !originalSegment.slice(1, -1).includes('[') &&\n !originalSegment.slice(1, -1).includes(']')\n )\n}\n\n/**\n * Checks if the leading underscore in a segment is escaped.\n * Returns true if:\n * - Segment starts with [_] pattern: \"[_]layout\" -> \"_layout\"\n * - Segment is fully escaped and content starts with _: \"[_1nd3x]\" -> \"_1nd3x\"\n */\nexport function hasEscapedLeadingUnderscore(originalSegment: string): boolean {\n // Pattern: [_]something or [_something]\n return (\n originalSegment.startsWith('[_]') ||\n (originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))\n )\n}\n\n/**\n * Checks if the trailing underscore in a segment is escaped.\n * Returns true if:\n * - Segment ends with [_] pattern: \"blog[_]\" -> \"blog_\"\n * - Segment is fully escaped and content ends with _: \"[_r0ut3_]\" -> \"_r0ut3_\"\n */\nexport function hasEscapedTrailingUnderscore(originalSegment: string): boolean {\n // Pattern: something[_] or [something_]\n return (\n originalSegment.endsWith('[_]') ||\n (originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))\n )\n}\n\nconst backslashRegex = /\\\\/g\n\nexport function replaceBackslash(s: string) {\n return s.replace(backslashRegex, '/')\n}\n\nconst alphanumericRegex = /[a-zA-Z0-9_]/\nconst splatSlashRegex = /\\/\\$\\//g\nconst trailingSplatRegex = /\\$$/g\nconst bracketSplatRegex = /\\$\\{\\$\\}/g\nconst dollarSignRegex = /\\$/g\nconst splitPathRegex = /[/-]/g\nconst leadingDigitRegex = /^(\\d)/g\n\nconst toVariableSafeChar = (char: string): string => {\n if (alphanumericRegex.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n}\n\nexport function routePathToVariable(routePath: string): string {\n const cleaned = removeUnderscores(routePath)\n if (!cleaned) return ''\n\n const parts = cleaned\n .replace(splatSlashRegex, '/splat/')\n .replace(trailingSplatRegex, 'splat')\n .replace(bracketSplatRegex, 'splat')\n .replace(dollarSignRegex, '')\n .split(splitPathRegex)\n\n let result = ''\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!\n const segment = i > 0 ? capitalize(part) : part\n for (let j = 0; j < segment.length; j++) {\n result += toVariableSafeChar(segment[j]!)\n }\n }\n\n return result.replace(leadingDigitRegex, 'R$1')\n}\n\nconst underscoreStartEndRegex = /(^_|_$)/gi\nconst underscoreSlashRegex = /(\\/_|_\\/)/gi\n\nexport function removeUnderscores(s?: string) {\n return s\n ?.replace(underscoreStartEndRegex, '')\n .replace(underscoreSlashRegex, '/')\n}\n\n/**\n * Removes underscores from a path, but preserves underscores that were escaped\n * in the original path (indicated by [_] syntax).\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped underscores removed\n */\nexport function removeUnderscoresWithEscape(\n routePath?: string,\n originalPath?: string,\n): string {\n if (!routePath) return ''\n if (!originalPath) return removeUnderscores(routePath) ?? ''\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n const newSegments = routeSegments.map((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n\n // Check if leading underscore is escaped\n const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)\n // Check if trailing underscore is escaped\n const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)\n\n let result = segment\n\n // Remove leading underscore only if not escaped\n if (result.startsWith('_') && !leadingEscaped) {\n result = result.slice(1)\n }\n\n // Remove trailing underscore only if not escaped\n if (result.endsWith('_') && !trailingEscaped) {\n result = result.slice(0, -1)\n }\n\n return result\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Removes layout segments (segments starting with underscore) from a path,\n * but preserves segments where the underscore was escaped.\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped layout segments removed\n */\nexport function removeLayoutSegmentsWithEscape(\n routePath: string = '/',\n originalPath?: string,\n): string {\n if (!originalPath) return removeLayoutSegments(routePath)\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n // Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)\n const newSegments = routeSegments.filter((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n return !isSegmentPathless(segment, originalSegment)\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Checks if a segment should be treated as a pathless/layout segment.\n * A segment is pathless if it starts with underscore and the underscore is not escaped.\n *\n * @param segment - The segment from routePath (brackets removed)\n * @param originalSegment - The segment from originalRoutePath (may contain brackets)\n * @returns true if the segment is pathless (has non-escaped leading underscore)\n */\nexport function isSegmentPathless(\n segment: string,\n originalSegment: string,\n): boolean {\n if (!segment.startsWith('_')) return false\n return !hasEscapedLeadingUnderscore(originalSegment)\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction sanitizeTokenFlags(flags?: string): string | undefined {\n if (!flags) return flags\n\n // Prevent stateful behavior with RegExp.prototype.test/exec\n // g = global, y = sticky\n return flags.replace(/[gy]/g, '')\n}\n\nexport function createTokenRegex(\n token: TokenMatcher,\n opts: {\n type: 'segment' | 'filename'\n },\n): RegExp {\n // Defensive check: if token is undefined/null, throw a clear error\n // (runtime safety for config loading edge cases)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (token === undefined || token === null) {\n throw new Error(\n `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`,\n )\n }\n\n try {\n if (typeof token === 'string') {\n return opts.type === 'segment'\n ? new RegExp(`^${escapeRegExp(token)}$`)\n : new RegExp(`[./]${escapeRegExp(token)}[.]`)\n }\n\n if (token instanceof RegExp) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.source})$`, flags)\n : new RegExp(`[./](?:${token.source})[.]`, flags)\n }\n\n // Handle JSON regex object form: { regex: string, flags?: string }\n if (typeof token === 'object' && 'regex' in token) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.regex})$`, flags)\n : new RegExp(`[./](?:${token.regex})[.]`, flags)\n }\n\n throw new Error(\n `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`,\n )\n } catch (e) {\n if (e instanceof SyntaxError) {\n const pattern =\n typeof token === 'string'\n ? token\n : token instanceof RegExp\n ? token.source\n : token.regex\n throw new Error(\n `Invalid regex pattern in token config: \"${pattern}\". ${e.message}`,\n )\n }\n throw e\n }\n}\n\nexport function isBracketWrappedSegment(segment: string): boolean {\n return segment.startsWith('[') && segment.endsWith(']')\n}\n\nexport function unwrapBracketWrappedSegment(segment: string): string {\n return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, addExtensions: boolean | string = false) {\n if (typeof addExtensions === 'string') {\n const dotIndex = d.lastIndexOf('.')\n if (dotIndex === -1) return d\n return d.substring(0, dotIndex) + addExtensions\n }\n return addExtensions ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\n/**\n * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).\n */\nexport function hasParentRoute(\n prefixMap: RoutePrefixMap,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n return prefixMap.findParent(routePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.\n * Also asserts that the RouteNode is defined.\n *\n * @param routeNode - The RouteNode to check.\n * @returns A boolean indicating whether the RouteNode is defined.\n */\nexport function isRouteNodeValidForAugmentation(\n routeNode?: RouteNode,\n): routeNode is RouteNode {\n if (!routeNode || routeNode.isVirtual) {\n return false\n }\n return true\n}\n\n/**\n * Infers the path for use by TS\n */\nexport const inferPath = (routeNode: RouteNode): string => {\n if (routeNode.cleanedPath === '/') {\n return routeNode.cleanedPath ?? ''\n }\n return routeNode.cleanedPath?.replace(/\\/$/, '') ?? ''\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscoresWithEscape(\n removeLayoutSegmentsWithEscape(\n routeNode.routePath,\n routeNode.originalRoutePath,\n ),\n routeNode.originalRoutePath,\n ),\n )\n\n if (fullPath === '') {\n return '/'\n }\n\n // Preserve trailing slash for index routes (routePath ends with '/')\n // This ensures types match runtime behavior\n const isIndexRoute = routeNode.routePath?.endsWith('/')\n if (isIndexRoute) {\n return fullPath\n }\n\n return fullPath.replace(/\\/$/, '')\n}\n\nconst shouldPreferIndexRoute = (\n current: RouteNode,\n existing: RouteNode,\n): boolean => {\n return existing.cleanedPath === '/' && current.cleanedPath !== '/'\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of routeNodes) {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(fullPath, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {\n const to = inferTo(routeNode)\n\n if (to === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(to, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nexport const inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nexport const dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const emptyPathRoutes = _routes.filter((d) => d.routePath === '')\n if (emptyPathRoutes.length) {\n const errorMessage = `Invalid route path \"\" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.\nConflicting files: \\n ${emptyPathRoutes\n .map((d) => path.resolve(config.routesDirectory, d.filePath))\n .join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function lowerCaseFirstChar(value: string) {\n if (!value[0]) {\n return value\n }\n\n return value[0].toLowerCase() + value.slice(1)\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged = new Map<string, ImportDeclaration>()\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind ?? ''}`\n let existing = merged.get(key)\n if (!existing) {\n existing = { ...imp, specifiers: [] }\n merged.set(key, existing)\n }\n\n const existingSpecs = existing.specifiers\n for (const specifier of imp.specifiers) {\n let found = false\n for (let i = 0; i < existingSpecs.length; i++) {\n const e = existingSpecs[i]!\n if (e.imported === specifier.imported && e.local === specifier.local) {\n found = true\n break\n }\n }\n if (!found) {\n existingSpecs.push(specifier)\n }\n }\n }\n\n return [...merged.values()]\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n return `${node.parent.variableName}Route`\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nexport function getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"names":["rootPathId","path","fsp","prettier","children"],"mappings":"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"}
1
+ {"version":3,"file":"utils.cjs","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/prefer-for-of */\nimport * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config, TokenMatcher } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\n/**\n * Prefix map for O(1) parent route lookups.\n * Maps each route path prefix to the route node that owns that prefix.\n * Enables finding longest matching parent without linear search.\n */\nexport class RoutePrefixMap {\n private prefixToRoute: Map<string, RouteNode> = new Map()\n private layoutRoutes: Array<RouteNode> = []\n\n constructor(routes: Array<RouteNode>) {\n for (const route of routes) {\n if (!route.routePath || route.routePath === `/${rootPathId}`) continue\n\n // Skip route pieces (lazy, loader, component, etc.) - they are merged with main routes\n // and should not be valid parent candidates\n if (\n route._fsRouteType === 'lazy' ||\n route._fsRouteType === 'loader' ||\n route._fsRouteType === 'component' ||\n route._fsRouteType === 'pendingComponent' ||\n route._fsRouteType === 'errorComponent' ||\n route._fsRouteType === 'notFoundComponent'\n ) {\n continue\n }\n\n // Index by exact path for direct lookups\n this.prefixToRoute.set(route.routePath, route)\n\n if (\n route._fsRouteType === 'pathless_layout' ||\n route._fsRouteType === 'layout' ||\n route._fsRouteType === '__root'\n ) {\n this.layoutRoutes.push(route)\n }\n }\n\n // Sort by path length descending for longest-match-first\n this.layoutRoutes.sort(\n (a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),\n )\n }\n\n /**\n * Find the longest matching parent route for a given path.\n * O(k) where k is the number of path segments, not O(n) routes.\n */\n findParent(routePath: string): RouteNode | null {\n if (!routePath || routePath === '/') return null\n\n // Walk up the path segments\n let searchPath = routePath\n while (searchPath.length > 0) {\n const lastSlash = searchPath.lastIndexOf('/')\n if (lastSlash <= 0) break\n\n searchPath = searchPath.substring(0, lastSlash)\n const parent = this.prefixToRoute.get(searchPath)\n if (parent && parent.routePath !== routePath) {\n return parent\n }\n }\n return null\n }\n\n /**\n * Check if a route exists at the given path.\n */\n has(routePath: string): boolean {\n return this.prefixToRoute.has(routePath)\n }\n\n /**\n * Get a route by exact path.\n */\n get(routePath: string): RouteNode | undefined {\n return this.prefixToRoute.get(routePath)\n }\n}\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n const len = arr.length\n // Pre-compute all accessor values to avoid repeated function calls during sort\n const indexed: Array<{ item: T; index: number; keys: Array<any> }> =\n new Array(len)\n for (let i = 0; i < len; i++) {\n const item = arr[i]!\n const keys = new Array(accessors.length)\n for (let j = 0; j < accessors.length; j++) {\n keys[j] = accessors[j]!(item)\n }\n indexed[i] = { item, index: i, keys }\n }\n\n indexed.sort((a, b) => {\n for (let j = 0; j < accessors.length; j++) {\n const ao = a.keys[j]\n const bo = b.keys[j]\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return a.index - b.index\n })\n\n const result: Array<T> = new Array(len)\n for (let i = 0; i < len; i++) {\n result[i] = indexed[i]!.item\n }\n return result\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\n/**\n * Characters that cannot be escaped in square brackets.\n * These are characters that would cause issues in URLs or file systems.\n */\nconst DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n])\n\nexport function determineInitialRoutePath(routePath: string) {\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n const parts = routePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `Error: Disallowed character \"${character}\" found in square brackets in route path \"${routePath}\".\\nYou cannot use any of the following characters in square brackets: ${Array.from(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n originalRoutePath,\n }\n}\n\n/**\n * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).\n * E.g., \"[index]\" -> true, \"[_layout]\" -> true, \"foo[.]bar\" -> false, \"index\" -> false\n */\nfunction isFullyEscapedSegment(originalSegment: string): boolean {\n return (\n originalSegment.startsWith('[') &&\n originalSegment.endsWith(']') &&\n !originalSegment.slice(1, -1).includes('[') &&\n !originalSegment.slice(1, -1).includes(']')\n )\n}\n\n/**\n * Checks if the leading underscore in a segment is escaped.\n * Returns true if:\n * - Segment starts with [_] pattern: \"[_]layout\" -> \"_layout\"\n * - Segment is fully escaped and content starts with _: \"[_1nd3x]\" -> \"_1nd3x\"\n */\nexport function hasEscapedLeadingUnderscore(originalSegment: string): boolean {\n // Pattern: [_]something or [_something]\n return (\n originalSegment.startsWith('[_]') ||\n (originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))\n )\n}\n\n/**\n * Checks if the trailing underscore in a segment is escaped.\n * Returns true if:\n * - Segment ends with [_] pattern: \"blog[_]\" -> \"blog_\"\n * - Segment is fully escaped and content ends with _: \"[_r0ut3_]\" -> \"_r0ut3_\"\n */\nexport function hasEscapedTrailingUnderscore(originalSegment: string): boolean {\n // Pattern: something[_] or [something_]\n return (\n originalSegment.endsWith('[_]') ||\n (originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))\n )\n}\n\nconst backslashRegex = /\\\\/g\n\nexport function replaceBackslash(s: string) {\n return s.replace(backslashRegex, '/')\n}\n\nconst alphanumericRegex = /[a-zA-Z0-9_]/\nconst splatSlashRegex = /\\/\\$\\//g\nconst trailingSplatRegex = /\\$$/g\nconst bracketSplatRegex = /\\$\\{\\$\\}/g\nconst dollarSignRegex = /\\$/g\nconst splitPathRegex = /[/-]/g\nconst leadingDigitRegex = /^(\\d)/g\n\nconst toVariableSafeChar = (char: string): string => {\n if (alphanumericRegex.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n}\n\nexport function routePathToVariable(routePath: string): string {\n const cleaned = removeUnderscores(routePath)\n if (!cleaned) return ''\n\n const parts = cleaned\n .replace(splatSlashRegex, '/splat/')\n .replace(trailingSplatRegex, 'splat')\n .replace(bracketSplatRegex, 'splat')\n .replace(dollarSignRegex, '')\n .split(splitPathRegex)\n\n let result = ''\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!\n const segment = i > 0 ? capitalize(part) : part\n for (let j = 0; j < segment.length; j++) {\n result += toVariableSafeChar(segment[j]!)\n }\n }\n\n return result.replace(leadingDigitRegex, 'R$1')\n}\n\nconst underscoreStartEndRegex = /(^_|_$)/gi\nconst underscoreSlashRegex = /(\\/_|_\\/)/gi\n\nexport function removeUnderscores(s?: string) {\n return s\n ?.replace(underscoreStartEndRegex, '')\n .replace(underscoreSlashRegex, '/')\n}\n\n/**\n * Removes underscores from a path, but preserves underscores that were escaped\n * in the original path (indicated by [_] syntax).\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped underscores removed\n */\nexport function removeUnderscoresWithEscape(\n routePath?: string,\n originalPath?: string,\n): string {\n if (!routePath) return ''\n if (!originalPath) return removeUnderscores(routePath) ?? ''\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n const newSegments = routeSegments.map((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n\n // Check if leading underscore is escaped\n const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)\n // Check if trailing underscore is escaped\n const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)\n\n let result = segment\n\n // Remove leading underscore only if not escaped\n if (result.startsWith('_') && !leadingEscaped) {\n result = result.slice(1)\n }\n\n // Remove trailing underscore only if not escaped\n if (result.endsWith('_') && !trailingEscaped) {\n result = result.slice(0, -1)\n }\n\n return result\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Removes layout segments (segments starting with underscore) from a path,\n * but preserves segments where the underscore was escaped.\n *\n * @param routePath - The path with brackets removed\n * @param originalPath - The original path that may contain [_] escape sequences\n * @returns The path with non-escaped layout segments removed\n */\nexport function removeLayoutSegmentsWithEscape(\n routePath: string = '/',\n originalPath?: string,\n): string {\n if (!originalPath) return removeLayoutSegments(routePath)\n\n const routeSegments = routePath.split('/')\n const originalSegments = originalPath.split('/')\n\n // Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)\n const newSegments = routeSegments.filter((segment, i) => {\n const originalSegment = originalSegments[i] || ''\n return !isSegmentPathless(segment, originalSegment)\n })\n\n return newSegments.join('/')\n}\n\n/**\n * Checks if a segment should be treated as a pathless/layout segment.\n * A segment is pathless if it starts with underscore and the underscore is not escaped.\n *\n * @param segment - The segment from routePath (brackets removed)\n * @param originalSegment - The segment from originalRoutePath (may contain brackets)\n * @returns true if the segment is pathless (has non-escaped leading underscore)\n */\nexport function isSegmentPathless(\n segment: string,\n originalSegment: string,\n): boolean {\n if (!segment.startsWith('_')) return false\n return !hasEscapedLeadingUnderscore(originalSegment)\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction sanitizeTokenFlags(flags?: string): string | undefined {\n if (!flags) return flags\n\n // Prevent stateful behavior with RegExp.prototype.test/exec\n // g = global, y = sticky\n return flags.replace(/[gy]/g, '')\n}\n\nexport function createTokenRegex(\n token: TokenMatcher,\n opts: {\n type: 'segment' | 'filename'\n },\n): RegExp {\n // Defensive check: if token is undefined/null, throw a clear error\n // (runtime safety for config loading edge cases)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (token === undefined || token === null) {\n throw new Error(\n `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`,\n )\n }\n\n try {\n if (typeof token === 'string') {\n return opts.type === 'segment'\n ? new RegExp(`^${escapeRegExp(token)}$`)\n : new RegExp(`[./]${escapeRegExp(token)}[.]`)\n }\n\n if (token instanceof RegExp) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.source})$`, flags)\n : new RegExp(`[./](?:${token.source})[.]`, flags)\n }\n\n // Handle JSON regex object form: { regex: string, flags?: string }\n if (typeof token === 'object' && 'regex' in token) {\n const flags = sanitizeTokenFlags(token.flags)\n return opts.type === 'segment'\n ? new RegExp(`^(?:${token.regex})$`, flags)\n : new RegExp(`[./](?:${token.regex})[.]`, flags)\n }\n\n throw new Error(\n `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`,\n )\n } catch (e) {\n if (e instanceof SyntaxError) {\n const pattern =\n typeof token === 'string'\n ? token\n : token instanceof RegExp\n ? token.source\n : token.regex\n throw new Error(\n `Invalid regex pattern in token config: \"${pattern}\". ${e.message}`,\n )\n }\n throw e\n }\n}\n\nexport function isBracketWrappedSegment(segment: string): boolean {\n return segment.startsWith('[') && segment.endsWith(']')\n}\n\nexport function unwrapBracketWrappedSegment(segment: string): string {\n return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, addExtensions: boolean | string = false) {\n if (typeof addExtensions === 'string') {\n const dotIndex = d.lastIndexOf('.')\n if (dotIndex === -1) return d\n return d.substring(0, dotIndex) + addExtensions\n }\n return addExtensions ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\n/**\n * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).\n */\nexport function hasParentRoute(\n prefixMap: RoutePrefixMap,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n return prefixMap.findParent(routePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.\n * Also asserts that the RouteNode is defined.\n *\n * @param routeNode - The RouteNode to check.\n * @returns A boolean indicating whether the RouteNode is defined.\n */\nexport function isRouteNodeValidForAugmentation(\n routeNode?: RouteNode,\n): routeNode is RouteNode {\n if (!routeNode || routeNode.isVirtual) {\n return false\n }\n return true\n}\n\n/**\n * Infers the path for use by TS\n */\nexport const inferPath = (routeNode: RouteNode): string => {\n if (routeNode.cleanedPath === '/') {\n return routeNode.cleanedPath ?? ''\n }\n return routeNode.cleanedPath?.replace(/\\/$/, '') ?? ''\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscoresWithEscape(\n removeLayoutSegmentsWithEscape(\n routeNode.routePath,\n routeNode.originalRoutePath,\n ),\n routeNode.originalRoutePath,\n ),\n )\n\n if (fullPath === '') {\n return '/'\n }\n\n // Preserve trailing slash for index routes (routePath ends with '/')\n // This ensures types match runtime behavior\n const isIndexRoute = routeNode.routePath?.endsWith('/')\n if (isIndexRoute) {\n return fullPath\n }\n\n return fullPath.replace(/\\/$/, '')\n}\n\nconst shouldPreferIndexRoute = (\n current: RouteNode,\n existing: RouteNode,\n): boolean => {\n return existing.cleanedPath === '/' && current.cleanedPath !== '/'\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of routeNodes) {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(fullPath, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n const map = new Map<string, RouteNode>()\n\n for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {\n const to = inferTo(routeNode)\n\n if (to === '/' && map.has('/')) {\n const existing = map.get('/')!\n if (shouldPreferIndexRoute(routeNode, existing)) {\n continue\n }\n }\n\n map.set(to, routeNode)\n }\n\n return map\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nexport const inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nexport const dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const emptyPathRoutes = _routes.filter((d) => d.routePath === '')\n if (emptyPathRoutes.length) {\n const errorMessage = `Invalid route path \"\" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.\nConflicting files: \\n ${emptyPathRoutes\n .map((d) => path.resolve(config.routesDirectory, d.filePath))\n .join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function lowerCaseFirstChar(value: string) {\n if (!value[0]) {\n return value\n }\n\n return value[0].toLowerCase() + value.slice(1)\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged = new Map<string, ImportDeclaration>()\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind ?? ''}`\n let existing = merged.get(key)\n if (!existing) {\n existing = { ...imp, specifiers: [] }\n merged.set(key, existing)\n }\n\n const existingSpecs = existing.specifiers\n for (const specifier of imp.specifiers) {\n let found = false\n for (let i = 0; i < existingSpecs.length; i++) {\n const e = existingSpecs[i]!\n if (e.imported === specifier.imported && e.local === specifier.local) {\n found = true\n break\n }\n }\n if (!found) {\n existingSpecs.push(specifier)\n }\n }\n }\n\n return [...merged.values()]\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n return `${node.parent.variableName}Route`\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nexport function getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"mappings":"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"}
@@ -1,58 +1,73 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
1
+ //#region src/validate-route-params.ts
2
+ /**
3
+ * Regex for valid JavaScript identifier (param name)
4
+ * Must start with letter, underscore, or dollar sign
5
+ * Can contain letters, numbers, underscores, or dollar signs
6
+ */
7
+ var VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
8
+ /**
9
+ * Extracts param names from a route path segment.
10
+ *
11
+ * Handles these patterns:
12
+ * - $paramName -> extract "paramName"
13
+ * - {$paramName} -> extract "paramName"
14
+ * - prefix{$paramName}suffix -> extract "paramName"
15
+ * - {-$paramName} -> extract "paramName" (optional)
16
+ * - prefix{-$paramName}suffix -> extract "paramName" (optional)
17
+ * - $ or {$} -> wildcard, skip validation
18
+ */
4
19
  function extractParamsFromSegment(segment) {
5
- const params = [];
6
- if (!segment || !segment.includes("$")) {
7
- return params;
8
- }
9
- if (segment === "$" || segment === "{$}") {
10
- return params;
11
- }
12
- if (segment.startsWith("$") && !segment.includes("{")) {
13
- const paramName = segment.slice(1);
14
- if (paramName) {
15
- params.push({
16
- paramName,
17
- isValid: VALID_PARAM_NAME_REGEX.test(paramName)
18
- });
19
- }
20
- return params;
21
- }
22
- const bracePattern = /\{(-?\$)([^}]*)\}/g;
23
- let match;
24
- while ((match = bracePattern.exec(segment)) !== null) {
25
- const paramName = match[2];
26
- if (!paramName) {
27
- continue;
28
- }
29
- params.push({
30
- paramName,
31
- isValid: VALID_PARAM_NAME_REGEX.test(paramName)
32
- });
33
- }
34
- return params;
20
+ const params = [];
21
+ if (!segment || !segment.includes("$")) return params;
22
+ if (segment === "$" || segment === "{$}") return params;
23
+ if (segment.startsWith("$") && !segment.includes("{")) {
24
+ const paramName = segment.slice(1);
25
+ if (paramName) params.push({
26
+ paramName,
27
+ isValid: VALID_PARAM_NAME_REGEX.test(paramName)
28
+ });
29
+ return params;
30
+ }
31
+ const bracePattern = /\{(-?\$)([^}]*)\}/g;
32
+ let match;
33
+ while ((match = bracePattern.exec(segment)) !== null) {
34
+ const paramName = match[2];
35
+ if (!paramName) continue;
36
+ params.push({
37
+ paramName,
38
+ isValid: VALID_PARAM_NAME_REGEX.test(paramName)
39
+ });
40
+ }
41
+ return params;
35
42
  }
43
+ /**
44
+ * Extracts all params from a route path.
45
+ *
46
+ * @param path - The route path (e.g., "/users/$userId/posts/$postId")
47
+ * @returns Array of extracted params with validation info
48
+ */
36
49
  function extractParamsFromPath(path) {
37
- if (!path || !path.includes("$")) {
38
- return [];
39
- }
40
- const segments = path.split("/");
41
- const allParams = [];
42
- for (const segment of segments) {
43
- const params = extractParamsFromSegment(segment);
44
- allParams.push(...params);
45
- }
46
- return allParams;
50
+ if (!path || !path.includes("$")) return [];
51
+ const segments = path.split("/");
52
+ const allParams = [];
53
+ for (const segment of segments) {
54
+ const params = extractParamsFromSegment(segment);
55
+ allParams.push(...params);
56
+ }
57
+ return allParams;
47
58
  }
59
+ /**
60
+ * Validates route params and logs warnings for invalid param names.
61
+ *
62
+ * @param routePath - The route path to validate
63
+ * @param filePath - The file path for error messages
64
+ * @param logger - Logger instance for warnings
65
+ */
48
66
  function validateRouteParams(routePath, filePath, logger) {
49
- const params = extractParamsFromPath(routePath);
50
- const invalidParams = params.filter((p) => !p.isValid);
51
- for (const param of invalidParams) {
52
- logger.warn(
53
- `WARNING: Invalid param name "${param.paramName}" in route "${routePath}" (file: ${filePath}). Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).`
54
- );
55
- }
67
+ const invalidParams = extractParamsFromPath(routePath).filter((p) => !p.isValid);
68
+ for (const param of invalidParams) logger.warn(`WARNING: Invalid param name "${param.paramName}" in route "${routePath}" (file: ${filePath}). Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).`);
56
69
  }
70
+ //#endregion
57
71
  exports.validateRouteParams = validateRouteParams;
58
- //# sourceMappingURL=validate-route-params.cjs.map
72
+
73
+ //# sourceMappingURL=validate-route-params.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-route-params.cjs","sources":["../../src/validate-route-params.ts"],"sourcesContent":["import type { Logger } from './logger'\n\n/**\n * Regex for valid JavaScript identifier (param name)\n * Must start with letter, underscore, or dollar sign\n * Can contain letters, numbers, underscores, or dollar signs\n */\nconst VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/\n\ninterface ExtractedParam {\n /** The param name without $ prefix (e.g., \"userId\", \"optional\") */\n paramName: string\n /** Whether this param name is valid */\n isValid: boolean\n}\n\n/**\n * Extracts param names from a route path segment.\n *\n * Handles these patterns:\n * - $paramName -> extract \"paramName\"\n * - {$paramName} -> extract \"paramName\"\n * - prefix{$paramName}suffix -> extract \"paramName\"\n * - {-$paramName} -> extract \"paramName\" (optional)\n * - prefix{-$paramName}suffix -> extract \"paramName\" (optional)\n * - $ or {$} -> wildcard, skip validation\n */\nfunction extractParamsFromSegment(segment: string): Array<ExtractedParam> {\n const params: Array<ExtractedParam> = []\n\n // Skip empty segments\n if (!segment || !segment.includes('$')) {\n return params\n }\n\n // Check for wildcard ($ alone or {$})\n if (segment === '$' || segment === '{$}') {\n return params // Wildcard, no param name to validate\n }\n\n // Pattern 1: Simple $paramName (entire segment starts with $)\n if (segment.startsWith('$') && !segment.includes('{')) {\n const paramName = segment.slice(1)\n if (paramName) {\n params.push({\n paramName,\n isValid: VALID_PARAM_NAME_REGEX.test(paramName),\n })\n }\n return params\n }\n\n // Pattern 2: Braces pattern {$paramName} or {-$paramName} with optional prefix/suffix\n // Match patterns like: prefix{$param}suffix, {$param}, {-$param}\n const bracePattern = /\\{(-?\\$)([^}]*)\\}/g\n let match\n\n while ((match = bracePattern.exec(segment)) !== null) {\n const paramName = match[2] // The param name after $ or -$\n\n if (!paramName) {\n // This is a wildcard {$} or {-$}, skip\n continue\n }\n\n params.push({\n paramName,\n isValid: VALID_PARAM_NAME_REGEX.test(paramName),\n })\n }\n\n return params\n}\n\n/**\n * Extracts all params from a route path.\n *\n * @param path - The route path (e.g., \"/users/$userId/posts/$postId\")\n * @returns Array of extracted params with validation info\n */\nfunction extractParamsFromPath(path: string): Array<ExtractedParam> {\n if (!path || !path.includes('$')) {\n return []\n }\n\n const segments = path.split('/')\n const allParams: Array<ExtractedParam> = []\n\n for (const segment of segments) {\n const params = extractParamsFromSegment(segment)\n allParams.push(...params)\n }\n\n return allParams\n}\n\n/**\n * Validates route params and logs warnings for invalid param names.\n *\n * @param routePath - The route path to validate\n * @param filePath - The file path for error messages\n * @param logger - Logger instance for warnings\n */\nexport function validateRouteParams(\n routePath: string,\n filePath: string,\n logger: Logger,\n): void {\n const params = extractParamsFromPath(routePath)\n const invalidParams = params.filter((p) => !p.isValid)\n\n for (const param of invalidParams) {\n logger.warn(\n `WARNING: Invalid param name \"${param.paramName}\" in route \"${routePath}\" (file: ${filePath}). ` +\n `Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).`,\n )\n }\n}\n"],"names":[],"mappings":";;AAOA,MAAM,yBAAyB;AAoB/B,SAAS,yBAAyB,SAAwC;AACxE,QAAM,SAAgC,CAAA;AAGtC,MAAI,CAAC,WAAW,CAAC,QAAQ,SAAS,GAAG,GAAG;AACtC,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,OAAO,YAAY,OAAO;AACxC,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,SAAS,GAAG,GAAG;AACrD,UAAM,YAAY,QAAQ,MAAM,CAAC;AACjC,QAAI,WAAW;AACb,aAAO,KAAK;AAAA,QACV;AAAA,QACA,SAAS,uBAAuB,KAAK,SAAS;AAAA,MAAA,CAC/C;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAIA,QAAM,eAAe;AACrB,MAAI;AAEJ,UAAQ,QAAQ,aAAa,KAAK,OAAO,OAAO,MAAM;AACpD,UAAM,YAAY,MAAM,CAAC;AAEzB,QAAI,CAAC,WAAW;AAEd;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,SAAS,uBAAuB,KAAK,SAAS;AAAA,IAAA,CAC/C;AAAA,EACH;AAEA,SAAO;AACT;AAQA,SAAS,sBAAsB,MAAqC;AAClE,MAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,GAAG,GAAG;AAChC,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,QAAM,YAAmC,CAAA;AAEzC,aAAW,WAAW,UAAU;AAC9B,UAAM,SAAS,yBAAyB,OAAO;AAC/C,cAAU,KAAK,GAAG,MAAM;AAAA,EAC1B;AAEA,SAAO;AACT;AASO,SAAS,oBACd,WACA,UACA,QACM;AACN,QAAM,SAAS,sBAAsB,SAAS;AAC9C,QAAM,gBAAgB,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO;AAErD,aAAW,SAAS,eAAe;AACjC,WAAO;AAAA,MACL,gCAAgC,MAAM,SAAS,eAAe,SAAS,YAAY,QAAQ;AAAA,IAAA;AAAA,EAG/F;AACF;;"}
1
+ {"version":3,"file":"validate-route-params.cjs","names":[],"sources":["../../src/validate-route-params.ts"],"sourcesContent":["import type { Logger } from './logger'\n\n/**\n * Regex for valid JavaScript identifier (param name)\n * Must start with letter, underscore, or dollar sign\n * Can contain letters, numbers, underscores, or dollar signs\n */\nconst VALID_PARAM_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/\n\ninterface ExtractedParam {\n /** The param name without $ prefix (e.g., \"userId\", \"optional\") */\n paramName: string\n /** Whether this param name is valid */\n isValid: boolean\n}\n\n/**\n * Extracts param names from a route path segment.\n *\n * Handles these patterns:\n * - $paramName -> extract \"paramName\"\n * - {$paramName} -> extract \"paramName\"\n * - prefix{$paramName}suffix -> extract \"paramName\"\n * - {-$paramName} -> extract \"paramName\" (optional)\n * - prefix{-$paramName}suffix -> extract \"paramName\" (optional)\n * - $ or {$} -> wildcard, skip validation\n */\nfunction extractParamsFromSegment(segment: string): Array<ExtractedParam> {\n const params: Array<ExtractedParam> = []\n\n // Skip empty segments\n if (!segment || !segment.includes('$')) {\n return params\n }\n\n // Check for wildcard ($ alone or {$})\n if (segment === '$' || segment === '{$}') {\n return params // Wildcard, no param name to validate\n }\n\n // Pattern 1: Simple $paramName (entire segment starts with $)\n if (segment.startsWith('$') && !segment.includes('{')) {\n const paramName = segment.slice(1)\n if (paramName) {\n params.push({\n paramName,\n isValid: VALID_PARAM_NAME_REGEX.test(paramName),\n })\n }\n return params\n }\n\n // Pattern 2: Braces pattern {$paramName} or {-$paramName} with optional prefix/suffix\n // Match patterns like: prefix{$param}suffix, {$param}, {-$param}\n const bracePattern = /\\{(-?\\$)([^}]*)\\}/g\n let match\n\n while ((match = bracePattern.exec(segment)) !== null) {\n const paramName = match[2] // The param name after $ or -$\n\n if (!paramName) {\n // This is a wildcard {$} or {-$}, skip\n continue\n }\n\n params.push({\n paramName,\n isValid: VALID_PARAM_NAME_REGEX.test(paramName),\n })\n }\n\n return params\n}\n\n/**\n * Extracts all params from a route path.\n *\n * @param path - The route path (e.g., \"/users/$userId/posts/$postId\")\n * @returns Array of extracted params with validation info\n */\nfunction extractParamsFromPath(path: string): Array<ExtractedParam> {\n if (!path || !path.includes('$')) {\n return []\n }\n\n const segments = path.split('/')\n const allParams: Array<ExtractedParam> = []\n\n for (const segment of segments) {\n const params = extractParamsFromSegment(segment)\n allParams.push(...params)\n }\n\n return allParams\n}\n\n/**\n * Validates route params and logs warnings for invalid param names.\n *\n * @param routePath - The route path to validate\n * @param filePath - The file path for error messages\n * @param logger - Logger instance for warnings\n */\nexport function validateRouteParams(\n routePath: string,\n filePath: string,\n logger: Logger,\n): void {\n const params = extractParamsFromPath(routePath)\n const invalidParams = params.filter((p) => !p.isValid)\n\n for (const param of invalidParams) {\n logger.warn(\n `WARNING: Invalid param name \"${param.paramName}\" in route \"${routePath}\" (file: ${filePath}). ` +\n `Param names must be valid JavaScript identifiers (match /[a-zA-Z_$][a-zA-Z0-9_$]*/).`,\n )\n }\n}\n"],"mappings":";;;;;;AAOA,IAAM,yBAAyB;;;;;;;;;;;;AAoB/B,SAAS,yBAAyB,SAAwC;CACxE,MAAM,SAAgC,EAAE;AAGxC,KAAI,CAAC,WAAW,CAAC,QAAQ,SAAS,IAAI,CACpC,QAAO;AAIT,KAAI,YAAY,OAAO,YAAY,MACjC,QAAO;AAIT,KAAI,QAAQ,WAAW,IAAI,IAAI,CAAC,QAAQ,SAAS,IAAI,EAAE;EACrD,MAAM,YAAY,QAAQ,MAAM,EAAE;AAClC,MAAI,UACF,QAAO,KAAK;GACV;GACA,SAAS,uBAAuB,KAAK,UAAU;GAChD,CAAC;AAEJ,SAAO;;CAKT,MAAM,eAAe;CACrB,IAAI;AAEJ,SAAQ,QAAQ,aAAa,KAAK,QAAQ,MAAM,MAAM;EACpD,MAAM,YAAY,MAAM;AAExB,MAAI,CAAC,UAEH;AAGF,SAAO,KAAK;GACV;GACA,SAAS,uBAAuB,KAAK,UAAU;GAChD,CAAC;;AAGJ,QAAO;;;;;;;;AAST,SAAS,sBAAsB,MAAqC;AAClE,KAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,IAAI,CAC9B,QAAO,EAAE;CAGX,MAAM,WAAW,KAAK,MAAM,IAAI;CAChC,MAAM,YAAmC,EAAE;AAE3C,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,SAAS,yBAAyB,QAAQ;AAChD,YAAU,KAAK,GAAG,OAAO;;AAG3B,QAAO;;;;;;;;;AAUT,SAAgB,oBACd,WACA,UACA,QACM;CAEN,MAAM,gBADS,sBAAsB,UAAU,CAClB,QAAQ,MAAM,CAAC,EAAE,QAAQ;AAEtD,MAAK,MAAM,SAAS,cAClB,QAAO,KACL,gCAAgC,MAAM,UAAU,cAAc,UAAU,WAAW,SAAS,yFAE7F"}