@tanstack/router-generator 1.141.8 → 1.142.0

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.
@@ -8,17 +8,12 @@ import { ImportDeclaration, RouteNode } from './types.cjs';
8
8
  export declare class RoutePrefixMap {
9
9
  private prefixToRoute;
10
10
  private layoutRoutes;
11
- private nonNestedRoutes;
12
11
  constructor(routes: Array<RouteNode>);
13
12
  /**
14
13
  * Find the longest matching parent route for a given path.
15
14
  * O(k) where k is the number of path segments, not O(n) routes.
16
15
  */
17
16
  findParent(routePath: string): RouteNode | null;
18
- /**
19
- * Find parent for non-nested routes (needs layout route matching).
20
- */
21
- findParentForNonNested(routePath: string, originalRoutePath: string | undefined, nonNestedSegments: Array<string>): RouteNode | null;
22
17
  /**
23
18
  * Check if a route exists at the given path.
24
19
  */
@@ -33,14 +28,54 @@ export declare function cleanPath(path: string): string;
33
28
  export declare function trimPathLeft(path: string): string;
34
29
  export declare function removeLeadingSlash(path: string): string;
35
30
  export declare function removeTrailingSlash(s: string): string;
36
- export declare function determineInitialRoutePath(routePath: string, config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>): {
31
+ export declare function determineInitialRoutePath(routePath: string): {
37
32
  routePath: string;
38
- isExperimentalNonNestedRoute: boolean;
39
33
  originalRoutePath: string;
40
34
  };
35
+ /**
36
+ * Checks if the leading underscore in a segment is escaped.
37
+ * Returns true if:
38
+ * - Segment starts with [_] pattern: "[_]layout" -> "_layout"
39
+ * - Segment is fully escaped and content starts with _: "[_1nd3x]" -> "_1nd3x"
40
+ */
41
+ export declare function hasEscapedLeadingUnderscore(originalSegment: string): boolean;
42
+ /**
43
+ * Checks if the trailing underscore in a segment is escaped.
44
+ * Returns true if:
45
+ * - Segment ends with [_] pattern: "blog[_]" -> "blog_"
46
+ * - Segment is fully escaped and content ends with _: "[_r0ut3_]" -> "_r0ut3_"
47
+ */
48
+ export declare function hasEscapedTrailingUnderscore(originalSegment: string): boolean;
41
49
  export declare function replaceBackslash(s: string): string;
42
50
  export declare function routePathToVariable(routePath: string): string;
43
51
  export declare function removeUnderscores(s?: string): string | undefined;
52
+ /**
53
+ * Removes underscores from a path, but preserves underscores that were escaped
54
+ * in the original path (indicated by [_] syntax).
55
+ *
56
+ * @param routePath - The path with brackets removed
57
+ * @param originalPath - The original path that may contain [_] escape sequences
58
+ * @returns The path with non-escaped underscores removed
59
+ */
60
+ export declare function removeUnderscoresWithEscape(routePath?: string, originalPath?: string): string;
61
+ /**
62
+ * Removes layout segments (segments starting with underscore) from a path,
63
+ * but preserves segments where the underscore was escaped.
64
+ *
65
+ * @param routePath - The path with brackets removed
66
+ * @param originalPath - The original path that may contain [_] escape sequences
67
+ * @returns The path with non-escaped layout segments removed
68
+ */
69
+ export declare function removeLayoutSegmentsWithEscape(routePath?: string, originalPath?: string): string;
70
+ /**
71
+ * Checks if a segment should be treated as a pathless/layout segment.
72
+ * A segment is pathless if it starts with underscore and the underscore is not escaped.
73
+ *
74
+ * @param segment - The segment from routePath (brackets removed)
75
+ * @param originalSegment - The segment from originalRoutePath (may contain brackets)
76
+ * @returns true if the segment is pathless (has non-escaped leading underscore)
77
+ */
78
+ export declare function isSegmentPathless(segment: string, originalSegment: string): boolean;
44
79
  export declare function removeLeadingUnderscores(s: string, routeToken: string): string;
45
80
  export declare function removeTrailingUnderscores(s: string, routeToken: string): string;
46
81
  export declare function capitalize(s: string): string;
@@ -111,15 +146,10 @@ export declare function determineNodePath(node: RouteNode): string | undefined;
111
146
  * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'
112
147
  */
113
148
  export declare function removeLastSegmentFromPath(routePath?: string): string;
114
- /**
115
- * Extracts non-nested segments from a route path.
116
- * Used for determining parent routes in non-nested route scenarios.
117
- */
118
- export declare function getNonNestedSegments(routePath: string): Array<string>;
119
149
  /**
120
150
  * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).
121
151
  */
122
- export declare function hasParentRoute(prefixMap: RoutePrefixMap, node: RouteNode, routePathToCheck: string | undefined, originalRoutePathToCheck: string | undefined): RouteNode | null;
152
+ export declare function hasParentRoute(prefixMap: RoutePrefixMap, node: RouteNode, routePathToCheck: string | undefined): RouteNode | null;
123
153
  /**
124
154
  * Gets the final variable name for a route
125
155
  */
@@ -139,15 +169,15 @@ export declare const inferPath: (routeNode: RouteNode) => string;
139
169
  /**
140
170
  * Infers the full path for use by TS
141
171
  */
142
- export declare const inferFullPath: (routeNode: RouteNode, config?: Pick<Config, "experimental" | "routeToken">) => string;
172
+ export declare const inferFullPath: (routeNode: RouteNode) => string;
143
173
  /**
144
174
  * Creates a map from fullPath to routeNode
145
175
  */
146
- export declare const createRouteNodesByFullPath: (routeNodes: Array<RouteNode>, config?: Pick<Config, "experimental" | "routeToken">) => Map<string, RouteNode>;
176
+ export declare const createRouteNodesByFullPath: (routeNodes: Array<RouteNode>) => Map<string, RouteNode>;
147
177
  /**
148
178
  * Create a map from 'to' to a routeNode
149
179
  */
150
- export declare const createRouteNodesByTo: (routeNodes: Array<RouteNode>, config?: Pick<Config, "experimental" | "routeToken">) => Map<string, RouteNode>;
180
+ export declare const createRouteNodesByTo: (routeNodes: Array<RouteNode>) => Map<string, RouteNode>;
151
181
  /**
152
182
  * Create a map from 'id' to a routeNode
153
183
  */
@@ -155,7 +185,7 @@ export declare const createRouteNodesById: (routeNodes: Array<RouteNode>) => Map
155
185
  /**
156
186
  * Infers to path
157
187
  */
158
- export declare const inferTo: (routeNode: RouteNode, config?: Pick<Config, "experimental" | "routeToken">) => string;
188
+ export declare const inferTo: (routeNode: RouteNode) => string;
159
189
  /**
160
190
  * Dedupes branches and index routes
161
191
  */
@@ -170,14 +200,7 @@ export declare function buildFileRoutesByPathInterface(opts: {
170
200
  routeNodes: Array<RouteNode>;
171
201
  module: string;
172
202
  interfaceName: string;
173
- config?: Pick<Config, 'experimental' | 'routeToken'>;
203
+ config?: Pick<Config, 'routeToken'>;
174
204
  }): string;
175
205
  export declare function getImportPath(node: RouteNode, config: Config, generatedRouteTreePath: string): string;
176
206
  export declare function getImportForRouteNode(node: RouteNode, config: Config, generatedRouteTreePath: string, root: string): ImportDeclaration;
177
- /**
178
- * Used to validate if a route is a pathless layout route
179
- * @param normalizedRoutePath Normalized route path, i.e `/foo/_layout/route.tsx` and `/foo._layout.route.tsx` to `/foo/_layout/route`
180
- * @param config The `router-generator` configuration object
181
- * @returns Boolean indicating if the route is a pathless layout route
182
- */
183
- export declare function isValidNonNestedRoute(normalizedRoutePath: string, config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>): boolean;
@@ -15,7 +15,7 @@ export declare const baseConfigSchema: z.ZodObject<{
15
15
  routeToken: z.ZodDefault<z.ZodOptional<z.ZodString>>;
16
16
  pathParamsAllowedCharacters: z.ZodOptional<z.ZodArray<z.ZodEnum<[";", ":", "@", "&", "=", "+", "$", ","]>, "many">>;
17
17
  }, "strip", z.ZodTypeAny, {
18
- target: "react" | "solid" | "vue";
18
+ target: "vue" | "react" | "solid";
19
19
  routeFileIgnorePrefix: string;
20
20
  routesDirectory: string;
21
21
  quoteStyle: "single" | "double";
@@ -27,9 +27,9 @@ export declare const baseConfigSchema: z.ZodObject<{
27
27
  virtualRouteConfig?: string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
28
28
  routeFilePrefix?: string | undefined;
29
29
  routeFileIgnorePattern?: string | undefined;
30
- pathParamsAllowedCharacters?: (";" | ":" | "@" | "&" | "=" | "+" | "$" | ",")[] | undefined;
30
+ pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
31
31
  }, {
32
- target?: "react" | "solid" | "vue" | undefined;
32
+ target?: "vue" | "react" | "solid" | undefined;
33
33
  virtualRouteConfig?: string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
34
34
  routeFilePrefix?: string | undefined;
35
35
  routeFileIgnorePrefix?: string | undefined;
@@ -41,7 +41,7 @@ export declare const baseConfigSchema: z.ZodObject<{
41
41
  routeTreeFileHeader?: string[] | undefined;
42
42
  indexToken?: string | undefined;
43
43
  routeToken?: string | undefined;
44
- pathParamsAllowedCharacters?: (";" | ":" | "@" | "&" | "=" | "+" | "$" | ",")[] | undefined;
44
+ pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
45
45
  }>;
46
46
  export type BaseConfig = z.infer<typeof baseConfigSchema>;
47
47
  export declare const configSchema: z.ZodObject<{
@@ -78,19 +78,16 @@ export declare const configSchema: z.ZodObject<{
78
78
  }>>;
79
79
  experimental: z.ZodOptional<z.ZodObject<{
80
80
  enableCodeSplitting: z.ZodOptional<z.ZodBoolean>;
81
- nonNestedRoutes: z.ZodOptional<z.ZodBoolean>;
82
81
  }, "strip", z.ZodTypeAny, {
83
82
  enableCodeSplitting?: boolean | undefined;
84
- nonNestedRoutes?: boolean | undefined;
85
83
  }, {
86
84
  enableCodeSplitting?: boolean | undefined;
87
- nonNestedRoutes?: boolean | undefined;
88
85
  }>>;
89
86
  plugins: z.ZodOptional<z.ZodArray<z.ZodType<GeneratorPlugin, z.ZodTypeDef, GeneratorPlugin>, "many">>;
90
87
  tmpDir: z.ZodDefault<z.ZodOptional<z.ZodString>>;
91
88
  importRoutesUsingAbsolutePaths: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
92
89
  }, "strip", z.ZodTypeAny, {
93
- target: "react" | "solid" | "vue";
90
+ target: "vue" | "react" | "solid";
94
91
  routeFileIgnorePrefix: string;
95
92
  routesDirectory: string;
96
93
  quoteStyle: "single" | "double";
@@ -105,10 +102,11 @@ export declare const configSchema: z.ZodObject<{
105
102
  enableRouteTreeFormatting: boolean;
106
103
  tmpDir: string;
107
104
  importRoutesUsingAbsolutePaths: boolean;
105
+ plugins?: GeneratorPlugin[] | undefined;
108
106
  virtualRouteConfig?: string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
109
107
  routeFilePrefix?: string | undefined;
110
108
  routeFileIgnorePattern?: string | undefined;
111
- pathParamsAllowedCharacters?: (";" | ":" | "@" | "&" | "=" | "+" | "$" | ",")[] | undefined;
109
+ pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
112
110
  verboseFileRoutes?: boolean | undefined;
113
111
  routeTreeFileFooter?: string[] | ((...args: unknown[]) => string[]) | undefined;
114
112
  autoCodeSplitting?: boolean | undefined;
@@ -118,11 +116,10 @@ export declare const configSchema: z.ZodObject<{
118
116
  } | undefined;
119
117
  experimental?: {
120
118
  enableCodeSplitting?: boolean | undefined;
121
- nonNestedRoutes?: boolean | undefined;
122
119
  } | undefined;
123
- plugins?: GeneratorPlugin[] | undefined;
124
120
  }, {
125
- target?: "react" | "solid" | "vue" | undefined;
121
+ plugins?: GeneratorPlugin[] | undefined;
122
+ target?: "vue" | "react" | "solid" | undefined;
126
123
  virtualRouteConfig?: string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
127
124
  routeFilePrefix?: string | undefined;
128
125
  routeFileIgnorePrefix?: string | undefined;
@@ -134,7 +131,7 @@ export declare const configSchema: z.ZodObject<{
134
131
  routeTreeFileHeader?: string[] | undefined;
135
132
  indexToken?: string | undefined;
136
133
  routeToken?: string | undefined;
137
- pathParamsAllowedCharacters?: (";" | ":" | "@" | "&" | "=" | "+" | "$" | ",")[] | undefined;
134
+ pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
138
135
  generatedRouteTree?: string | undefined;
139
136
  disableTypes?: boolean | undefined;
140
137
  verboseFileRoutes?: boolean | undefined;
@@ -148,9 +145,7 @@ export declare const configSchema: z.ZodObject<{
148
145
  } | undefined;
149
146
  experimental?: {
150
147
  enableCodeSplitting?: boolean | undefined;
151
- nonNestedRoutes?: boolean | undefined;
152
148
  } | undefined;
153
- plugins?: GeneratorPlugin[] | undefined;
154
149
  tmpDir?: string | undefined;
155
150
  importRoutesUsingAbsolutePaths?: boolean | undefined;
156
151
  }>;
@@ -38,9 +38,7 @@ const configSchema = baseConfigSchema.extend({
38
38
  }).optional(),
39
39
  experimental: z.object({
40
40
  // TODO: This has been made stable and is now "autoCodeSplitting". Remove in next major version.
41
- enableCodeSplitting: z.boolean().optional(),
42
- // TODO: This resolves issues with non-nested paths in file-based routing. To be made default in next major version.
43
- nonNestedRoutes: z.boolean().optional()
41
+ enableCodeSplitting: z.boolean().optional()
44
42
  }).optional(),
45
43
  plugins: z.array(z.custom()).optional(),
46
44
  tmpDir: z.string().optional().default(""),
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sources":["../../src/config.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { z } from 'zod'\nimport { virtualRootRouteSchema } from './filesystem/virtual/config'\nimport type { GeneratorPlugin } from './plugin/types'\n\nexport const baseConfigSchema = z.object({\n target: z.enum(['react', 'solid', 'vue']).optional().default('react'),\n virtualRouteConfig: virtualRootRouteSchema.or(z.string()).optional(),\n routeFilePrefix: z.string().optional(),\n routeFileIgnorePrefix: z.string().optional().default('-'),\n routeFileIgnorePattern: z.string().optional(),\n routesDirectory: z.string().optional().default('./src/routes'),\n quoteStyle: z.enum(['single', 'double']).optional().default('single'),\n semicolons: z.boolean().optional().default(false),\n disableLogging: z.boolean().optional().default(false),\n routeTreeFileHeader: z\n .array(z.string())\n .optional()\n .default([\n '/* eslint-disable */',\n '// @ts-nocheck',\n '// noinspection JSUnusedGlobalSymbols',\n ]),\n indexToken: z.string().optional().default('index'),\n routeToken: z.string().optional().default('route'),\n pathParamsAllowedCharacters: z\n .array(z.enum([';', ':', '@', '&', '=', '+', '$', ',']))\n .optional(),\n})\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>\n\nexport const configSchema = baseConfigSchema.extend({\n generatedRouteTree: z.string().optional().default('./src/routeTree.gen.ts'),\n disableTypes: z.boolean().optional().default(false),\n verboseFileRoutes: z.boolean().optional(),\n addExtensions: z.boolean().optional().default(false),\n enableRouteTreeFormatting: z.boolean().optional().default(true),\n routeTreeFileFooter: z\n .union([\n z.array(z.string()).optional().default([]),\n z.function().returns(z.array(z.string())),\n ])\n .optional(),\n autoCodeSplitting: z.boolean().optional(),\n customScaffolding: z\n .object({\n routeTemplate: z.string().optional(),\n lazyRouteTemplate: z.string().optional(),\n })\n .optional(),\n experimental: z\n .object({\n // TODO: This has been made stable and is now \"autoCodeSplitting\". Remove in next major version.\n enableCodeSplitting: z.boolean().optional(),\n // TODO: This resolves issues with non-nested paths in file-based routing. To be made default in next major version.\n nonNestedRoutes: z.boolean().optional(),\n })\n .optional(),\n plugins: z.array(z.custom<GeneratorPlugin>()).optional(),\n tmpDir: z.string().optional().default(''),\n importRoutesUsingAbsolutePaths: z.boolean().optional().default(false),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\ntype ResolveParams = {\n configDirectory: string\n}\n\nexport function resolveConfigPath({ configDirectory }: ResolveParams) {\n return path.resolve(configDirectory, 'tsr.config.json')\n}\n\nexport function getConfig(\n inlineConfig: Partial<Config> = {},\n configDirectory?: string,\n): Config {\n if (configDirectory === undefined) {\n configDirectory = process.cwd()\n }\n const configFilePathJson = resolveConfigPath({ configDirectory })\n const exists = existsSync(configFilePathJson)\n\n let config: Config\n\n if (exists) {\n config = configSchema.parse({\n ...JSON.parse(readFileSync(configFilePathJson, 'utf-8')),\n ...inlineConfig,\n })\n } else {\n config = configSchema.parse(inlineConfig)\n }\n\n // If typescript is disabled, make sure the generated route tree is a .js file\n if (config.disableTypes) {\n config.generatedRouteTree = config.generatedRouteTree.replace(\n /\\.(ts|tsx)$/,\n '.js',\n )\n }\n\n // if a configDirectory is used, paths should be relative to that directory\n if (configDirectory) {\n // if absolute configDirectory is provided, use it as the root\n if (path.isAbsolute(configDirectory)) {\n config.routesDirectory = path.resolve(\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n configDirectory,\n config.generatedRouteTree,\n )\n } else {\n config.routesDirectory = path.resolve(\n process.cwd(),\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n process.cwd(),\n configDirectory,\n config.generatedRouteTree,\n )\n }\n }\n\n const resolveTmpDir = (dir: string | Array<string>) => {\n if (Array.isArray(dir)) {\n dir = path.join(...dir)\n }\n if (!path.isAbsolute(dir)) {\n dir = path.resolve(process.cwd(), dir)\n }\n return dir\n }\n\n if (config.tmpDir) {\n config.tmpDir = resolveTmpDir(config.tmpDir)\n } else if (process.env.TSR_TMP_DIR) {\n config.tmpDir = resolveTmpDir(process.env.TSR_TMP_DIR)\n } else {\n config.tmpDir = resolveTmpDir(['.tanstack', 'tmp'])\n }\n\n validateConfig(config)\n return config\n}\n\nfunction validateConfig(config: Config) {\n if (typeof config.experimental?.enableCodeSplitting !== 'undefined') {\n const message = `\n------\n⚠️ ⚠️ ⚠️\nERROR: The \"experimental.enableCodeSplitting\" flag has been made stable and is now \"autoCodeSplitting\". Please update your configuration file to use \"autoCodeSplitting\" instead of \"experimental.enableCodeSplitting\".\n------\n`\n console.error(message)\n throw new Error(message)\n }\n\n if (config.indexToken === config.routeToken) {\n throw new Error(\n `The \"indexToken\" and \"routeToken\" options must be different.`,\n )\n }\n\n if (\n config.routeFileIgnorePrefix &&\n config.routeFileIgnorePrefix.trim() === '_'\n ) {\n throw new Error(\n `The \"routeFileIgnorePrefix\" cannot be an underscore (\"_\"). This is a reserved character used to denote a pathless route. Please use a different prefix.`,\n )\n }\n\n return config\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sources":["../../src/config.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { z } from 'zod'\nimport { virtualRootRouteSchema } from './filesystem/virtual/config'\nimport type { GeneratorPlugin } from './plugin/types'\n\nexport const baseConfigSchema = z.object({\n target: z.enum(['react', 'solid', 'vue']).optional().default('react'),\n virtualRouteConfig: virtualRootRouteSchema.or(z.string()).optional(),\n routeFilePrefix: z.string().optional(),\n routeFileIgnorePrefix: z.string().optional().default('-'),\n routeFileIgnorePattern: z.string().optional(),\n routesDirectory: z.string().optional().default('./src/routes'),\n quoteStyle: z.enum(['single', 'double']).optional().default('single'),\n semicolons: z.boolean().optional().default(false),\n disableLogging: z.boolean().optional().default(false),\n routeTreeFileHeader: z\n .array(z.string())\n .optional()\n .default([\n '/* eslint-disable */',\n '// @ts-nocheck',\n '// noinspection JSUnusedGlobalSymbols',\n ]),\n indexToken: z.string().optional().default('index'),\n routeToken: z.string().optional().default('route'),\n pathParamsAllowedCharacters: z\n .array(z.enum([';', ':', '@', '&', '=', '+', '$', ',']))\n .optional(),\n})\n\nexport type BaseConfig = z.infer<typeof baseConfigSchema>\n\nexport const configSchema = baseConfigSchema.extend({\n generatedRouteTree: z.string().optional().default('./src/routeTree.gen.ts'),\n disableTypes: z.boolean().optional().default(false),\n verboseFileRoutes: z.boolean().optional(),\n addExtensions: z.boolean().optional().default(false),\n enableRouteTreeFormatting: z.boolean().optional().default(true),\n routeTreeFileFooter: z\n .union([\n z.array(z.string()).optional().default([]),\n z.function().returns(z.array(z.string())),\n ])\n .optional(),\n autoCodeSplitting: z.boolean().optional(),\n customScaffolding: z\n .object({\n routeTemplate: z.string().optional(),\n lazyRouteTemplate: z.string().optional(),\n })\n .optional(),\n experimental: z\n .object({\n // TODO: This has been made stable and is now \"autoCodeSplitting\". Remove in next major version.\n enableCodeSplitting: z.boolean().optional(),\n })\n .optional(),\n plugins: z.array(z.custom<GeneratorPlugin>()).optional(),\n tmpDir: z.string().optional().default(''),\n importRoutesUsingAbsolutePaths: z.boolean().optional().default(false),\n})\n\nexport type Config = z.infer<typeof configSchema>\n\ntype ResolveParams = {\n configDirectory: string\n}\n\nexport function resolveConfigPath({ configDirectory }: ResolveParams) {\n return path.resolve(configDirectory, 'tsr.config.json')\n}\n\nexport function getConfig(\n inlineConfig: Partial<Config> = {},\n configDirectory?: string,\n): Config {\n if (configDirectory === undefined) {\n configDirectory = process.cwd()\n }\n const configFilePathJson = resolveConfigPath({ configDirectory })\n const exists = existsSync(configFilePathJson)\n\n let config: Config\n\n if (exists) {\n config = configSchema.parse({\n ...JSON.parse(readFileSync(configFilePathJson, 'utf-8')),\n ...inlineConfig,\n })\n } else {\n config = configSchema.parse(inlineConfig)\n }\n\n // If typescript is disabled, make sure the generated route tree is a .js file\n if (config.disableTypes) {\n config.generatedRouteTree = config.generatedRouteTree.replace(\n /\\.(ts|tsx)$/,\n '.js',\n )\n }\n\n // if a configDirectory is used, paths should be relative to that directory\n if (configDirectory) {\n // if absolute configDirectory is provided, use it as the root\n if (path.isAbsolute(configDirectory)) {\n config.routesDirectory = path.resolve(\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n configDirectory,\n config.generatedRouteTree,\n )\n } else {\n config.routesDirectory = path.resolve(\n process.cwd(),\n configDirectory,\n config.routesDirectory,\n )\n config.generatedRouteTree = path.resolve(\n process.cwd(),\n configDirectory,\n config.generatedRouteTree,\n )\n }\n }\n\n const resolveTmpDir = (dir: string | Array<string>) => {\n if (Array.isArray(dir)) {\n dir = path.join(...dir)\n }\n if (!path.isAbsolute(dir)) {\n dir = path.resolve(process.cwd(), dir)\n }\n return dir\n }\n\n if (config.tmpDir) {\n config.tmpDir = resolveTmpDir(config.tmpDir)\n } else if (process.env.TSR_TMP_DIR) {\n config.tmpDir = resolveTmpDir(process.env.TSR_TMP_DIR)\n } else {\n config.tmpDir = resolveTmpDir(['.tanstack', 'tmp'])\n }\n\n validateConfig(config)\n return config\n}\n\nfunction validateConfig(config: Config) {\n if (typeof config.experimental?.enableCodeSplitting !== 'undefined') {\n const message = `\n------\n⚠️ ⚠️ ⚠️\nERROR: The \"experimental.enableCodeSplitting\" flag has been made stable and is now \"autoCodeSplitting\". Please update your configuration file to use \"autoCodeSplitting\" instead of \"experimental.enableCodeSplitting\".\n------\n`\n console.error(message)\n throw new Error(message)\n }\n\n if (config.indexToken === config.routeToken) {\n throw new Error(\n `The \"indexToken\" and \"routeToken\" options must be different.`,\n )\n }\n\n if (\n config.routeFileIgnorePrefix &&\n config.routeFileIgnorePrefix.trim() === '_'\n ) {\n throw new Error(\n `The \"routeFileIgnorePrefix\" cannot be an underscore (\"_\"). This is a reserved character used to denote a pathless route. Please use a different prefix.`,\n )\n }\n\n return config\n}\n"],"names":[],"mappings":"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"}
@@ -1,15 +1,16 @@
1
1
  import { FsRouteType, GetRouteNodesResult } from '../../types.js';
2
2
  import { Config } from '../../config.js';
3
3
  export declare function isVirtualConfigFile(fileName: string): boolean;
4
- export declare function getRouteNodes(config: Pick<Config, 'routesDirectory' | 'routeFilePrefix' | 'routeFileIgnorePrefix' | 'routeFileIgnorePattern' | 'disableLogging' | 'routeToken' | 'indexToken' | 'experimental'>, root: string): Promise<GetRouteNodesResult>;
4
+ export declare function getRouteNodes(config: Pick<Config, 'routesDirectory' | 'routeFilePrefix' | 'routeFileIgnorePrefix' | 'routeFileIgnorePattern' | 'disableLogging' | 'routeToken' | 'indexToken'>, root: string): Promise<GetRouteNodesResult>;
5
5
  /**
6
6
  * Determines the metadata for a given route path based on the provided configuration.
7
7
  *
8
- * @param routePath - The determined initial routePath.
8
+ * @param routePath - The determined initial routePath (with brackets removed).
9
+ * @param originalRoutePath - The original route path (may contain brackets for escaped content).
9
10
  * @param config - The user configuration object.
10
11
  * @returns An object containing the type of the route and the variable name derived from the route path.
11
12
  */
12
- export declare function getRouteMeta(routePath: string, config: Pick<Config, 'routeToken' | 'indexToken'>): {
13
+ export declare function getRouteMeta(routePath: string, originalRoutePath: string, config: Pick<Config, 'routeToken' | 'indexToken'>): {
13
14
  fsRouteType: Extract<FsRouteType, 'static' | 'layout' | 'api' | 'lazy' | 'loader' | 'component' | 'pendingComponent' | 'errorComponent' | 'notFoundComponent'>;
14
15
  variableName: string;
15
16
  };
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import * as fsp from "node:fs/promises";
3
- import { replaceBackslash, routePathToVariable, removeExt, determineInitialRoutePath } from "../../utils.js";
3
+ import { replaceBackslash, routePathToVariable, removeExt, determineInitialRoutePath, hasEscapedLeadingUnderscore } from "../../utils.js";
4
4
  import { getRouteNodes as getRouteNodes$1 } from "../virtual/getRouteNodes.js";
5
5
  import { loadConfigFile } from "../virtual/loadConfigFile.js";
6
6
  import { logging } from "../../logger.js";
@@ -84,9 +84,8 @@ async function getRouteNodes(config, root) {
84
84
  const filePathNoExt = removeExt(filePath);
85
85
  const {
86
86
  routePath: initialRoutePath,
87
- originalRoutePath: initialOriginalRoutePath,
88
- isExperimentalNonNestedRoute
89
- } = determineInitialRoutePath(filePathNoExt, config);
87
+ originalRoutePath: initialOriginalRoutePath
88
+ } = determineInitialRoutePath(filePathNoExt);
90
89
  let routePath = initialRoutePath;
91
90
  let originalRoutePath = initialOriginalRoutePath;
92
91
  if (routeFilePrefix) {
@@ -101,14 +100,19 @@ async function getRouteNodes(config, root) {
101
100
  logger.error(`ERROR: ${errorMessage}`);
102
101
  throw new Error(errorMessage);
103
102
  }
104
- const meta = getRouteMeta(routePath, config);
103
+ const meta = getRouteMeta(routePath, originalRoutePath, config);
105
104
  const variableName = meta.variableName;
106
105
  let routeType = meta.fsRouteType;
107
106
  if (routeType === "lazy") {
108
107
  routePath = routePath.replace(/\/lazy$/, "");
109
108
  originalRoutePath = originalRoutePath.replace(/\/lazy$/, "");
110
109
  }
111
- if (isValidPathlessLayoutRoute(routePath, routeType, config)) {
110
+ if (isValidPathlessLayoutRoute(
111
+ routePath,
112
+ originalRoutePath,
113
+ routeType,
114
+ config
115
+ )) {
112
116
  routeType = "pathless_layout";
113
117
  }
114
118
  const isVueFile = filePath.endsWith(".vue");
@@ -127,36 +131,50 @@ async function getRouteNodes(config, root) {
127
131
  }
128
132
  });
129
133
  }
130
- routePath = routePath.replace(
131
- new RegExp(
132
- `/(component|errorComponent|notFoundComponent|pendingComponent|loader|${config.routeToken}|lazy)$`
133
- ),
134
- ""
135
- );
136
- originalRoutePath = originalRoutePath.replace(
137
- new RegExp(
138
- `/(component|errorComponent|notFoundComponent|pendingComponent|loader|${config.routeToken}|lazy)$`
139
- ),
140
- ""
141
- );
142
- if (routePath === config.indexToken) {
143
- routePath = "/";
134
+ const originalSegments = originalRoutePath.split("/").filter(Boolean);
135
+ const lastOriginalSegmentForSuffix = originalSegments[originalSegments.length - 1] || "";
136
+ const specialSuffixes = [
137
+ "component",
138
+ "errorComponent",
139
+ "notFoundComponent",
140
+ "pendingComponent",
141
+ "loader",
142
+ config.routeToken,
143
+ "lazy"
144
+ ];
145
+ const suffixToStrip = specialSuffixes.find((suffix) => {
146
+ const endsWithSuffix = routePath.endsWith(`/${suffix}`);
147
+ const isEscaped = lastOriginalSegmentForSuffix === `[${suffix}]`;
148
+ return endsWithSuffix && !isEscaped;
149
+ });
150
+ if (suffixToStrip) {
151
+ routePath = routePath.replace(new RegExp(`/${suffixToStrip}$`), "");
152
+ originalRoutePath = originalRoutePath.replace(
153
+ new RegExp(`/${suffixToStrip}$`),
154
+ ""
155
+ );
144
156
  }
145
- if (originalRoutePath === config.indexToken) {
146
- originalRoutePath = "/";
157
+ const lastOriginalSegment = originalRoutePath.split("/").filter(Boolean).pop() || "";
158
+ const isIndexEscaped = lastOriginalSegment === `[${config.indexToken}]`;
159
+ if (!isIndexEscaped) {
160
+ if (routePath === config.indexToken) {
161
+ routePath = "/";
162
+ }
163
+ if (originalRoutePath === config.indexToken) {
164
+ originalRoutePath = "/";
165
+ }
166
+ routePath = routePath.replace(new RegExp(`/${config.indexToken}$`), "/") || "/";
167
+ originalRoutePath = originalRoutePath.replace(
168
+ new RegExp(`/${config.indexToken}$`),
169
+ "/"
170
+ ) || "/";
147
171
  }
148
- routePath = routePath.replace(new RegExp(`/${config.indexToken}$`), "/") || "/";
149
- originalRoutePath = originalRoutePath.replace(
150
- new RegExp(`/${config.indexToken}$`),
151
- "/"
152
- ) || "/";
153
172
  routeNodes.push({
154
173
  filePath,
155
174
  fullPath,
156
175
  routePath,
157
176
  variableName,
158
177
  _fsRouteType: routeType,
159
- _isExperimentalNonNestedRoute: isExperimentalNonNestedRoute,
160
178
  originalRoutePath
161
179
  });
162
180
  }
@@ -185,42 +203,56 @@ async function getRouteNodes(config, root) {
185
203
  physicalDirectories: allPhysicalDirectories
186
204
  };
187
205
  }
188
- function getRouteMeta(routePath, config) {
206
+ function getRouteMeta(routePath, originalRoutePath, config) {
189
207
  let fsRouteType = "static";
190
- if (routePath.endsWith(`/${config.routeToken}`)) {
208
+ const originalSegments = originalRoutePath.split("/").filter(Boolean);
209
+ const lastOriginalSegment = originalSegments[originalSegments.length - 1] || "";
210
+ const isSuffixEscaped = (suffix) => {
211
+ return lastOriginalSegment === `[${suffix}]`;
212
+ };
213
+ if (routePath.endsWith(`/${config.routeToken}`) && !isSuffixEscaped(config.routeToken)) {
191
214
  fsRouteType = "layout";
192
- } else if (routePath.endsWith("/lazy")) {
215
+ } else if (routePath.endsWith("/lazy") && !isSuffixEscaped("lazy")) {
193
216
  fsRouteType = "lazy";
194
- } else if (routePath.endsWith("/loader")) {
217
+ } else if (routePath.endsWith("/loader") && !isSuffixEscaped("loader")) {
195
218
  fsRouteType = "loader";
196
- } else if (routePath.endsWith("/component")) {
219
+ } else if (routePath.endsWith("/component") && !isSuffixEscaped("component")) {
197
220
  fsRouteType = "component";
198
- } else if (routePath.endsWith("/pendingComponent")) {
221
+ } else if (routePath.endsWith("/pendingComponent") && !isSuffixEscaped("pendingComponent")) {
199
222
  fsRouteType = "pendingComponent";
200
- } else if (routePath.endsWith("/errorComponent")) {
223
+ } else if (routePath.endsWith("/errorComponent") && !isSuffixEscaped("errorComponent")) {
201
224
  fsRouteType = "errorComponent";
202
- } else if (routePath.endsWith("/notFoundComponent")) {
225
+ } else if (routePath.endsWith("/notFoundComponent") && !isSuffixEscaped("notFoundComponent")) {
203
226
  fsRouteType = "notFoundComponent";
204
227
  }
205
228
  const variableName = routePathToVariable(routePath);
206
229
  return { fsRouteType, variableName };
207
230
  }
208
- function isValidPathlessLayoutRoute(normalizedRoutePath, routeType, config) {
231
+ function isValidPathlessLayoutRoute(normalizedRoutePath, originalRoutePath, routeType, config) {
209
232
  if (routeType === "lazy") {
210
233
  return false;
211
234
  }
212
235
  const segments = normalizedRoutePath.split("/").filter(Boolean);
236
+ const originalSegments = originalRoutePath.split("/").filter(Boolean);
213
237
  if (segments.length === 0) {
214
238
  return false;
215
239
  }
216
240
  const lastRouteSegment = segments[segments.length - 1];
241
+ const lastOriginalSegment = originalSegments[originalSegments.length - 1] || "";
217
242
  const secondToLastRouteSegment = segments[segments.length - 2];
243
+ const secondToLastOriginalSegment = originalSegments[originalSegments.length - 2];
218
244
  if (lastRouteSegment === rootPathId) {
219
245
  return false;
220
246
  }
221
- if (lastRouteSegment === config.routeToken && typeof secondToLastRouteSegment === "string") {
247
+ if (lastRouteSegment === config.routeToken && typeof secondToLastRouteSegment === "string" && typeof secondToLastOriginalSegment === "string") {
248
+ if (hasEscapedLeadingUnderscore(secondToLastOriginalSegment)) {
249
+ return false;
250
+ }
222
251
  return secondToLastRouteSegment.startsWith("_");
223
252
  }
253
+ if (hasEscapedLeadingUnderscore(lastOriginalSegment)) {
254
+ return false;
255
+ }
224
256
  return lastRouteSegment !== config.indexToken && lastRouteSegment !== config.routeToken && lastRouteSegment.startsWith("_");
225
257
  }
226
258
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"getRouteNodes.js","sources":["../../../../src/filesystem/physical/getRouteNodes.ts"],"sourcesContent":["import path from 'node:path'\nimport * as fsp from 'node:fs/promises'\nimport {\n determineInitialRoutePath,\n removeExt,\n replaceBackslash,\n routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesVirtual } from '../virtual/getRouteNodes'\nimport { loadConfigFile } from '../virtual/loadConfigFile'\nimport { logging } from '../../logger'\nimport { rootPathId } from './rootPathId'\nimport type {\n VirtualRootRoute,\n VirtualRouteSubtreeConfig,\n} from '@tanstack/virtual-file-routes'\nimport type { FsRouteType, GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\n\nconst disallowedRouteGroupConfiguration = /\\(([^)]+)\\).(ts|js|tsx|jsx|vue)/\n\nconst virtualConfigFileRegExp = /__virtual\\.[mc]?[jt]s$/\nexport function isVirtualConfigFile(fileName: string): boolean {\n return virtualConfigFileRegExp.test(fileName)\n}\n\nexport async function getRouteNodes(\n config: Pick<\n Config,\n | 'routesDirectory'\n | 'routeFilePrefix'\n | 'routeFileIgnorePrefix'\n | 'routeFileIgnorePattern'\n | 'disableLogging'\n | 'routeToken'\n | 'indexToken'\n | 'experimental'\n >,\n root: string,\n): Promise<GetRouteNodesResult> {\n const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =\n config\n\n const logger = logging({ disabled: config.disableLogging })\n const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')\n\n const routeNodes: Array<RouteNode> = []\n const allPhysicalDirectories: Array<string> = []\n\n async function recurse(dir: string) {\n const fullDir = path.resolve(config.routesDirectory, dir)\n let dirList = await fsp.readdir(fullDir, { withFileTypes: true })\n\n dirList = dirList.filter((d) => {\n if (\n d.name.startsWith('.') ||\n (routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))\n ) {\n return false\n }\n\n if (routeFilePrefix) {\n if (routeFileIgnorePattern) {\n return (\n d.name.startsWith(routeFilePrefix) &&\n !d.name.match(routeFileIgnoreRegExp)\n )\n }\n\n return d.name.startsWith(routeFilePrefix)\n }\n\n if (routeFileIgnorePattern) {\n return !d.name.match(routeFileIgnoreRegExp)\n }\n\n return true\n })\n\n const virtualConfigFile = dirList.find((dirent) => {\n return dirent.isFile() && isVirtualConfigFile(dirent.name)\n })\n\n if (virtualConfigFile !== undefined) {\n const virtualRouteConfigExport = await loadConfigFile(\n path.resolve(fullDir, virtualConfigFile.name),\n )\n let virtualRouteSubtreeConfig: VirtualRouteSubtreeConfig\n if (typeof virtualRouteConfigExport.default === 'function') {\n virtualRouteSubtreeConfig = await virtualRouteConfigExport.default()\n } else {\n virtualRouteSubtreeConfig = virtualRouteConfigExport.default\n }\n const dummyRoot: VirtualRootRoute = {\n type: 'root',\n file: '',\n children: virtualRouteSubtreeConfig,\n }\n const { routeNodes: virtualRouteNodes, physicalDirectories } =\n await getRouteNodesVirtual(\n {\n ...config,\n routesDirectory: fullDir,\n virtualRouteConfig: dummyRoot,\n },\n root,\n )\n allPhysicalDirectories.push(...physicalDirectories)\n virtualRouteNodes.forEach((node) => {\n const filePath = replaceBackslash(path.join(dir, node.filePath))\n const routePath = `/${dir}${node.routePath}`\n\n node.variableName = routePathToVariable(\n `${dir}/${removeExt(node.filePath)}`,\n )\n node.routePath = routePath\n node.filePath = filePath\n })\n\n routeNodes.push(...virtualRouteNodes)\n\n return\n }\n\n await Promise.all(\n dirList.map(async (dirent) => {\n const fullPath = replaceBackslash(path.join(fullDir, dirent.name))\n const relativePath = path.posix.join(dir, dirent.name)\n\n if (dirent.isDirectory()) {\n await recurse(relativePath)\n } else if (fullPath.match(/\\.(tsx|ts|jsx|js|vue)$/)) {\n const filePath = replaceBackslash(path.join(dir, dirent.name))\n const filePathNoExt = removeExt(filePath)\n const {\n routePath: initialRoutePath,\n originalRoutePath: initialOriginalRoutePath,\n isExperimentalNonNestedRoute,\n } = determineInitialRoutePath(filePathNoExt, config)\n\n let routePath = initialRoutePath\n let originalRoutePath = initialOriginalRoutePath\n\n if (routeFilePrefix) {\n routePath = routePath.replaceAll(routeFilePrefix, '')\n originalRoutePath = originalRoutePath.replaceAll(\n routeFilePrefix,\n '',\n )\n }\n\n if (disallowedRouteGroupConfiguration.test(dirent.name)) {\n const errorMessage = `A route configuration for a route group was found at \\`${filePath}\\`. This is not supported. Did you mean to use a layout/pathless route instead?`\n logger.error(`ERROR: ${errorMessage}`)\n throw new Error(errorMessage)\n }\n\n const meta = getRouteMeta(routePath, config)\n const variableName = meta.variableName\n let routeType: FsRouteType = meta.fsRouteType\n\n if (routeType === 'lazy') {\n routePath = routePath.replace(/\\/lazy$/, '')\n originalRoutePath = originalRoutePath.replace(/\\/lazy$/, '')\n }\n\n // this check needs to happen after the lazy route has been cleaned up\n // since the routePath is used to determine if a route is pathless\n if (isValidPathlessLayoutRoute(routePath, routeType, config)) {\n routeType = 'pathless_layout'\n }\n\n // Only show deprecation warning for .tsx/.ts files, not .vue files\n // Vue files using .component.vue is the Vue-native way\n const isVueFile = filePath.endsWith('.vue')\n if (!isVueFile) {\n ;(\n [\n ['component', 'component'],\n ['errorComponent', 'errorComponent'],\n ['notFoundComponent', 'notFoundComponent'],\n ['pendingComponent', 'pendingComponent'],\n ['loader', 'loader'],\n ] satisfies Array<[FsRouteType, string]>\n ).forEach(([matcher, type]) => {\n if (routeType === matcher) {\n logger.warn(\n `WARNING: The \\`.${type}.tsx\\` suffix used for the ${filePath} file is deprecated. Use the new \\`.lazy.tsx\\` suffix instead.`,\n )\n }\n })\n }\n\n routePath = routePath.replace(\n new RegExp(\n `/(component|errorComponent|notFoundComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,\n ),\n '',\n )\n\n originalRoutePath = originalRoutePath.replace(\n new RegExp(\n `/(component|errorComponent|notFoundComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,\n ),\n '',\n )\n\n if (routePath === config.indexToken) {\n routePath = '/'\n }\n\n if (originalRoutePath === config.indexToken) {\n originalRoutePath = '/'\n }\n\n routePath =\n routePath.replace(new RegExp(`/${config.indexToken}$`), '/') || '/'\n\n originalRoutePath =\n originalRoutePath.replace(\n new RegExp(`/${config.indexToken}$`),\n '/',\n ) || '/'\n\n routeNodes.push({\n filePath,\n fullPath,\n routePath,\n variableName,\n _fsRouteType: routeType,\n _isExperimentalNonNestedRoute: isExperimentalNonNestedRoute,\n originalRoutePath,\n })\n }\n }),\n )\n\n return routeNodes\n }\n\n await recurse('./')\n\n // Find the root route node - prefer the actual route file over component/loader files\n const rootRouteNode =\n routeNodes.find(\n (d) =>\n d.routePath === `/${rootPathId}` &&\n ![\n 'component',\n 'errorComponent',\n 'notFoundComponent',\n 'pendingComponent',\n 'loader',\n 'lazy',\n ].includes(d._fsRouteType),\n ) ?? routeNodes.find((d) => d.routePath === `/${rootPathId}`)\n if (rootRouteNode) {\n rootRouteNode._fsRouteType = '__root'\n rootRouteNode.variableName = 'root'\n }\n\n return {\n rootRouteNode,\n routeNodes,\n physicalDirectories: allPhysicalDirectories,\n }\n}\n\n/**\n * Determines the metadata for a given route path based on the provided configuration.\n *\n * @param routePath - The determined initial routePath.\n * @param config - The user configuration object.\n * @returns An object containing the type of the route and the variable name derived from the route path.\n */\nexport function getRouteMeta(\n routePath: string,\n config: Pick<Config, 'routeToken' | 'indexToken'>,\n): {\n // `__root` is can be more easily determined by filtering down to routePath === /${rootPathId}\n // `pathless` is needs to determined after `lazy` has been cleaned up from the routePath\n fsRouteType: Extract<\n FsRouteType,\n | 'static'\n | 'layout'\n | 'api'\n | 'lazy'\n | 'loader'\n | 'component'\n | 'pendingComponent'\n | 'errorComponent'\n | 'notFoundComponent'\n >\n variableName: string\n} {\n let fsRouteType: FsRouteType = 'static'\n\n if (routePath.endsWith(`/${config.routeToken}`)) {\n // layout routes, i.e `/foo/route.tsx` or `/foo/_layout/route.tsx`\n fsRouteType = 'layout'\n } else if (routePath.endsWith('/lazy')) {\n // lazy routes, i.e. `/foo.lazy.tsx`\n fsRouteType = 'lazy'\n } else if (routePath.endsWith('/loader')) {\n // loader routes, i.e. `/foo.loader.tsx`\n fsRouteType = 'loader'\n } else if (routePath.endsWith('/component')) {\n // component routes, i.e. `/foo.component.tsx`\n fsRouteType = 'component'\n } else if (routePath.endsWith('/pendingComponent')) {\n // pending component routes, i.e. `/foo.pendingComponent.tsx`\n fsRouteType = 'pendingComponent'\n } else if (routePath.endsWith('/errorComponent')) {\n // error component routes, i.e. `/foo.errorComponent.tsx`\n fsRouteType = 'errorComponent'\n } else if (routePath.endsWith('/notFoundComponent')) {\n // not found component routes, i.e. `/foo.notFoundComponent.tsx`\n fsRouteType = 'notFoundComponent'\n }\n\n const variableName = routePathToVariable(routePath)\n\n return { fsRouteType, variableName }\n}\n\n/**\n * Used to validate if a route is a pathless layout route\n * @param normalizedRoutePath Normalized route path, i.e `/foo/_layout/route.tsx` and `/foo._layout.route.tsx` to `/foo/_layout/route`\n * @param config The `router-generator` configuration object\n * @returns Boolean indicating if the route is a pathless layout route\n */\nfunction isValidPathlessLayoutRoute(\n normalizedRoutePath: string,\n routeType: FsRouteType,\n config: Pick<Config, 'routeToken' | 'indexToken'>,\n): boolean {\n if (routeType === 'lazy') {\n return false\n }\n\n const segments = normalizedRoutePath.split('/').filter(Boolean)\n\n if (segments.length === 0) {\n return false\n }\n\n const lastRouteSegment = segments[segments.length - 1]!\n const secondToLastRouteSegment = segments[segments.length - 2]\n\n // If segment === __root, then exit as false\n if (lastRouteSegment === rootPathId) {\n return false\n }\n\n // If segment === config.routeToken and secondToLastSegment is a string that starts with _, then exit as true\n // Since the route is actually a configuration route for a layout/pathless route\n // i.e. /foo/_layout/route.tsx === /foo/_layout.tsx\n if (\n lastRouteSegment === config.routeToken &&\n typeof secondToLastRouteSegment === 'string'\n ) {\n return secondToLastRouteSegment.startsWith('_')\n }\n\n // Segment starts with _\n return (\n lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment.startsWith('_')\n )\n}\n"],"names":["getRouteNodesVirtual"],"mappings":"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"}
1
+ {"version":3,"file":"getRouteNodes.js","sources":["../../../../src/filesystem/physical/getRouteNodes.ts"],"sourcesContent":["import path from 'node:path'\nimport * as fsp from 'node:fs/promises'\nimport {\n determineInitialRoutePath,\n hasEscapedLeadingUnderscore,\n removeExt,\n replaceBackslash,\n routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesVirtual } from '../virtual/getRouteNodes'\nimport { loadConfigFile } from '../virtual/loadConfigFile'\nimport { logging } from '../../logger'\nimport { rootPathId } from './rootPathId'\nimport type {\n VirtualRootRoute,\n VirtualRouteSubtreeConfig,\n} from '@tanstack/virtual-file-routes'\nimport type { FsRouteType, GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\n\nconst disallowedRouteGroupConfiguration = /\\(([^)]+)\\).(ts|js|tsx|jsx|vue)/\n\nconst virtualConfigFileRegExp = /__virtual\\.[mc]?[jt]s$/\nexport function isVirtualConfigFile(fileName: string): boolean {\n return virtualConfigFileRegExp.test(fileName)\n}\n\nexport async function getRouteNodes(\n config: Pick<\n Config,\n | 'routesDirectory'\n | 'routeFilePrefix'\n | 'routeFileIgnorePrefix'\n | 'routeFileIgnorePattern'\n | 'disableLogging'\n | 'routeToken'\n | 'indexToken'\n >,\n root: string,\n): Promise<GetRouteNodesResult> {\n const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =\n config\n\n const logger = logging({ disabled: config.disableLogging })\n const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')\n\n const routeNodes: Array<RouteNode> = []\n const allPhysicalDirectories: Array<string> = []\n\n async function recurse(dir: string) {\n const fullDir = path.resolve(config.routesDirectory, dir)\n let dirList = await fsp.readdir(fullDir, { withFileTypes: true })\n\n dirList = dirList.filter((d) => {\n if (\n d.name.startsWith('.') ||\n (routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))\n ) {\n return false\n }\n\n if (routeFilePrefix) {\n if (routeFileIgnorePattern) {\n return (\n d.name.startsWith(routeFilePrefix) &&\n !d.name.match(routeFileIgnoreRegExp)\n )\n }\n\n return d.name.startsWith(routeFilePrefix)\n }\n\n if (routeFileIgnorePattern) {\n return !d.name.match(routeFileIgnoreRegExp)\n }\n\n return true\n })\n\n const virtualConfigFile = dirList.find((dirent) => {\n return dirent.isFile() && isVirtualConfigFile(dirent.name)\n })\n\n if (virtualConfigFile !== undefined) {\n const virtualRouteConfigExport = await loadConfigFile(\n path.resolve(fullDir, virtualConfigFile.name),\n )\n let virtualRouteSubtreeConfig: VirtualRouteSubtreeConfig\n if (typeof virtualRouteConfigExport.default === 'function') {\n virtualRouteSubtreeConfig = await virtualRouteConfigExport.default()\n } else {\n virtualRouteSubtreeConfig = virtualRouteConfigExport.default\n }\n const dummyRoot: VirtualRootRoute = {\n type: 'root',\n file: '',\n children: virtualRouteSubtreeConfig,\n }\n const { routeNodes: virtualRouteNodes, physicalDirectories } =\n await getRouteNodesVirtual(\n {\n ...config,\n routesDirectory: fullDir,\n virtualRouteConfig: dummyRoot,\n },\n root,\n )\n allPhysicalDirectories.push(...physicalDirectories)\n virtualRouteNodes.forEach((node) => {\n const filePath = replaceBackslash(path.join(dir, node.filePath))\n const routePath = `/${dir}${node.routePath}`\n\n node.variableName = routePathToVariable(\n `${dir}/${removeExt(node.filePath)}`,\n )\n node.routePath = routePath\n node.filePath = filePath\n })\n\n routeNodes.push(...virtualRouteNodes)\n\n return\n }\n\n await Promise.all(\n dirList.map(async (dirent) => {\n const fullPath = replaceBackslash(path.join(fullDir, dirent.name))\n const relativePath = path.posix.join(dir, dirent.name)\n\n if (dirent.isDirectory()) {\n await recurse(relativePath)\n } else if (fullPath.match(/\\.(tsx|ts|jsx|js|vue)$/)) {\n const filePath = replaceBackslash(path.join(dir, dirent.name))\n const filePathNoExt = removeExt(filePath)\n const {\n routePath: initialRoutePath,\n originalRoutePath: initialOriginalRoutePath,\n } = determineInitialRoutePath(filePathNoExt)\n\n let routePath = initialRoutePath\n let originalRoutePath = initialOriginalRoutePath\n\n if (routeFilePrefix) {\n routePath = routePath.replaceAll(routeFilePrefix, '')\n originalRoutePath = originalRoutePath.replaceAll(\n routeFilePrefix,\n '',\n )\n }\n\n if (disallowedRouteGroupConfiguration.test(dirent.name)) {\n const errorMessage = `A route configuration for a route group was found at \\`${filePath}\\`. This is not supported. Did you mean to use a layout/pathless route instead?`\n logger.error(`ERROR: ${errorMessage}`)\n throw new Error(errorMessage)\n }\n\n const meta = getRouteMeta(routePath, originalRoutePath, config)\n const variableName = meta.variableName\n let routeType: FsRouteType = meta.fsRouteType\n\n if (routeType === 'lazy') {\n routePath = routePath.replace(/\\/lazy$/, '')\n originalRoutePath = originalRoutePath.replace(/\\/lazy$/, '')\n }\n\n // this check needs to happen after the lazy route has been cleaned up\n // since the routePath is used to determine if a route is pathless\n if (\n isValidPathlessLayoutRoute(\n routePath,\n originalRoutePath,\n routeType,\n config,\n )\n ) {\n routeType = 'pathless_layout'\n }\n\n // Only show deprecation warning for .tsx/.ts files, not .vue files\n // Vue files using .component.vue is the Vue-native way\n const isVueFile = filePath.endsWith('.vue')\n if (!isVueFile) {\n ;(\n [\n ['component', 'component'],\n ['errorComponent', 'errorComponent'],\n ['notFoundComponent', 'notFoundComponent'],\n ['pendingComponent', 'pendingComponent'],\n ['loader', 'loader'],\n ] satisfies Array<[FsRouteType, string]>\n ).forEach(([matcher, type]) => {\n if (routeType === matcher) {\n logger.warn(\n `WARNING: The \\`.${type}.tsx\\` suffix used for the ${filePath} file is deprecated. Use the new \\`.lazy.tsx\\` suffix instead.`,\n )\n }\n })\n }\n\n // Get the last segment of originalRoutePath to check for escaping\n const originalSegments = originalRoutePath.split('/').filter(Boolean)\n const lastOriginalSegmentForSuffix =\n originalSegments[originalSegments.length - 1] || ''\n\n // List of special suffixes that can be escaped\n const specialSuffixes = [\n 'component',\n 'errorComponent',\n 'notFoundComponent',\n 'pendingComponent',\n 'loader',\n config.routeToken,\n 'lazy',\n ]\n\n // Only strip the suffix if it wasn't escaped (not wrapped in brackets)\n const suffixToStrip = specialSuffixes.find((suffix) => {\n const endsWithSuffix = routePath.endsWith(`/${suffix}`)\n const isEscaped = lastOriginalSegmentForSuffix === `[${suffix}]`\n return endsWithSuffix && !isEscaped\n })\n\n if (suffixToStrip) {\n routePath = routePath.replace(new RegExp(`/${suffixToStrip}$`), '')\n originalRoutePath = originalRoutePath.replace(\n new RegExp(`/${suffixToStrip}$`),\n '',\n )\n }\n\n // Check if the index token should be treated specially or as a literal path\n // If it's escaped (wrapped in brackets in originalRoutePath), it should be literal\n const lastOriginalSegment =\n originalRoutePath.split('/').filter(Boolean).pop() || ''\n const isIndexEscaped =\n lastOriginalSegment === `[${config.indexToken}]`\n\n if (!isIndexEscaped) {\n if (routePath === config.indexToken) {\n routePath = '/'\n }\n\n if (originalRoutePath === config.indexToken) {\n originalRoutePath = '/'\n }\n\n routePath =\n routePath.replace(new RegExp(`/${config.indexToken}$`), '/') ||\n '/'\n\n originalRoutePath =\n originalRoutePath.replace(\n new RegExp(`/${config.indexToken}$`),\n '/',\n ) || '/'\n }\n\n routeNodes.push({\n filePath,\n fullPath,\n routePath,\n variableName,\n _fsRouteType: routeType,\n originalRoutePath,\n })\n }\n }),\n )\n\n return routeNodes\n }\n\n await recurse('./')\n\n // Find the root route node - prefer the actual route file over component/loader files\n const rootRouteNode =\n routeNodes.find(\n (d) =>\n d.routePath === `/${rootPathId}` &&\n ![\n 'component',\n 'errorComponent',\n 'notFoundComponent',\n 'pendingComponent',\n 'loader',\n 'lazy',\n ].includes(d._fsRouteType),\n ) ?? routeNodes.find((d) => d.routePath === `/${rootPathId}`)\n if (rootRouteNode) {\n rootRouteNode._fsRouteType = '__root'\n rootRouteNode.variableName = 'root'\n }\n\n return {\n rootRouteNode,\n routeNodes,\n physicalDirectories: allPhysicalDirectories,\n }\n}\n\n/**\n * Determines the metadata for a given route path based on the provided configuration.\n *\n * @param routePath - The determined initial routePath (with brackets removed).\n * @param originalRoutePath - The original route path (may contain brackets for escaped content).\n * @param config - The user configuration object.\n * @returns An object containing the type of the route and the variable name derived from the route path.\n */\nexport function getRouteMeta(\n routePath: string,\n originalRoutePath: string,\n config: Pick<Config, 'routeToken' | 'indexToken'>,\n): {\n // `__root` is can be more easily determined by filtering down to routePath === /${rootPathId}\n // `pathless` is needs to determined after `lazy` has been cleaned up from the routePath\n fsRouteType: Extract<\n FsRouteType,\n | 'static'\n | 'layout'\n | 'api'\n | 'lazy'\n | 'loader'\n | 'component'\n | 'pendingComponent'\n | 'errorComponent'\n | 'notFoundComponent'\n >\n variableName: string\n} {\n let fsRouteType: FsRouteType = 'static'\n\n // Get the last segment from the original path to check for escaping\n const originalSegments = originalRoutePath.split('/').filter(Boolean)\n const lastOriginalSegment =\n originalSegments[originalSegments.length - 1] || ''\n\n // Helper to check if a specific suffix is escaped\n const isSuffixEscaped = (suffix: string): boolean => {\n return lastOriginalSegment === `[${suffix}]`\n }\n\n if (\n routePath.endsWith(`/${config.routeToken}`) &&\n !isSuffixEscaped(config.routeToken)\n ) {\n // layout routes, i.e `/foo/route.tsx` or `/foo/_layout/route.tsx`\n fsRouteType = 'layout'\n } else if (routePath.endsWith('/lazy') && !isSuffixEscaped('lazy')) {\n // lazy routes, i.e. `/foo.lazy.tsx`\n fsRouteType = 'lazy'\n } else if (routePath.endsWith('/loader') && !isSuffixEscaped('loader')) {\n // loader routes, i.e. `/foo.loader.tsx`\n fsRouteType = 'loader'\n } else if (\n routePath.endsWith('/component') &&\n !isSuffixEscaped('component')\n ) {\n // component routes, i.e. `/foo.component.tsx`\n fsRouteType = 'component'\n } else if (\n routePath.endsWith('/pendingComponent') &&\n !isSuffixEscaped('pendingComponent')\n ) {\n // pending component routes, i.e. `/foo.pendingComponent.tsx`\n fsRouteType = 'pendingComponent'\n } else if (\n routePath.endsWith('/errorComponent') &&\n !isSuffixEscaped('errorComponent')\n ) {\n // error component routes, i.e. `/foo.errorComponent.tsx`\n fsRouteType = 'errorComponent'\n } else if (\n routePath.endsWith('/notFoundComponent') &&\n !isSuffixEscaped('notFoundComponent')\n ) {\n // not found component routes, i.e. `/foo.notFoundComponent.tsx`\n fsRouteType = 'notFoundComponent'\n }\n\n const variableName = routePathToVariable(routePath)\n\n return { fsRouteType, variableName }\n}\n\n/**\n * Used to validate if a route is a pathless layout route\n * @param normalizedRoutePath Normalized route path, i.e `/foo/_layout/route.tsx` and `/foo._layout.route.tsx` to `/foo/_layout/route`\n * @param originalRoutePath Original route path with brackets for escaped content\n * @param routeType The route type determined from file extension\n * @param config The `router-generator` configuration object\n * @returns Boolean indicating if the route is a pathless layout route\n */\nfunction isValidPathlessLayoutRoute(\n normalizedRoutePath: string,\n originalRoutePath: string,\n routeType: FsRouteType,\n config: Pick<Config, 'routeToken' | 'indexToken'>,\n): boolean {\n if (routeType === 'lazy') {\n return false\n }\n\n const segments = normalizedRoutePath.split('/').filter(Boolean)\n const originalSegments = originalRoutePath.split('/').filter(Boolean)\n\n if (segments.length === 0) {\n return false\n }\n\n const lastRouteSegment = segments[segments.length - 1]!\n const lastOriginalSegment =\n originalSegments[originalSegments.length - 1] || ''\n const secondToLastRouteSegment = segments[segments.length - 2]\n const secondToLastOriginalSegment =\n originalSegments[originalSegments.length - 2]\n\n // If segment === __root, then exit as false\n if (lastRouteSegment === rootPathId) {\n return false\n }\n\n // If segment === config.routeToken and secondToLastSegment is a string that starts with _, then exit as true\n // Since the route is actually a configuration route for a layout/pathless route\n // i.e. /foo/_layout/route.tsx === /foo/_layout.tsx\n // But if the underscore is escaped, it's not a pathless layout\n if (\n lastRouteSegment === config.routeToken &&\n typeof secondToLastRouteSegment === 'string' &&\n typeof secondToLastOriginalSegment === 'string'\n ) {\n // Check if the underscore is escaped\n if (hasEscapedLeadingUnderscore(secondToLastOriginalSegment)) {\n return false\n }\n return secondToLastRouteSegment.startsWith('_')\n }\n\n // Segment starts with _ but check if it's escaped\n // If the original segment has [_] at the start, the underscore is escaped and it's not a pathless layout\n if (hasEscapedLeadingUnderscore(lastOriginalSegment)) {\n return false\n }\n\n return (\n lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment.startsWith('_')\n )\n}\n"],"names":["getRouteNodesVirtual"],"mappings":"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"}