@tanstack/router-generator 1.20.0 → 1.20.3-alpha.1

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 (74) hide show
  1. package/dist/cjs/config.cjs +93 -13
  2. package/dist/cjs/config.cjs.map +1 -1
  3. package/dist/cjs/config.d.cts +126 -9
  4. package/dist/cjs/filesystem/physical/getRouteNodes.cjs +191 -0
  5. package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -0
  6. package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +14 -0
  7. package/dist/cjs/filesystem/physical/rootPathId.cjs +5 -0
  8. package/dist/cjs/filesystem/physical/rootPathId.cjs.map +1 -0
  9. package/dist/cjs/filesystem/physical/rootPathId.d.cts +1 -0
  10. package/dist/cjs/filesystem/virtual/config.cjs +37 -0
  11. package/dist/cjs/filesystem/virtual/config.cjs.map +1 -0
  12. package/dist/cjs/filesystem/virtual/config.d.cts +3 -0
  13. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +186 -0
  14. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -0
  15. package/dist/cjs/filesystem/virtual/getRouteNodes.d.cts +5 -0
  16. package/dist/cjs/filesystem/virtual/loadConfigFile.cjs +11 -0
  17. package/dist/cjs/filesystem/virtual/loadConfigFile.cjs.map +1 -0
  18. package/dist/cjs/filesystem/virtual/loadConfigFile.d.cts +1 -0
  19. package/dist/cjs/generator.cjs +516 -328
  20. package/dist/cjs/generator.cjs.map +1 -1
  21. package/dist/cjs/generator.d.cts +1 -37
  22. package/dist/cjs/index.cjs +24 -0
  23. package/dist/cjs/index.cjs.map +1 -1
  24. package/dist/cjs/index.d.cts +7 -1
  25. package/dist/cjs/template.cjs +110 -0
  26. package/dist/cjs/template.cjs.map +1 -0
  27. package/dist/cjs/template.d.cts +33 -0
  28. package/dist/cjs/types.d.cts +20 -0
  29. package/dist/cjs/utils.cjs +177 -10
  30. package/dist/cjs/utils.cjs.map +1 -1
  31. package/dist/cjs/utils.d.cts +47 -5
  32. package/dist/esm/config.d.ts +126 -9
  33. package/dist/esm/config.js +88 -8
  34. package/dist/esm/config.js.map +1 -1
  35. package/dist/esm/filesystem/physical/getRouteNodes.d.ts +14 -0
  36. package/dist/esm/filesystem/physical/getRouteNodes.js +174 -0
  37. package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -0
  38. package/dist/esm/filesystem/physical/rootPathId.d.ts +1 -0
  39. package/dist/esm/filesystem/physical/rootPathId.js +5 -0
  40. package/dist/esm/filesystem/physical/rootPathId.js.map +1 -0
  41. package/dist/esm/filesystem/virtual/config.d.ts +3 -0
  42. package/dist/esm/filesystem/virtual/config.js +37 -0
  43. package/dist/esm/filesystem/virtual/config.js.map +1 -0
  44. package/dist/esm/filesystem/virtual/getRouteNodes.d.ts +5 -0
  45. package/dist/esm/filesystem/virtual/getRouteNodes.js +186 -0
  46. package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -0
  47. package/dist/esm/filesystem/virtual/loadConfigFile.d.ts +1 -0
  48. package/dist/esm/filesystem/virtual/loadConfigFile.js +11 -0
  49. package/dist/esm/filesystem/virtual/loadConfigFile.js.map +1 -0
  50. package/dist/esm/generator.d.ts +1 -37
  51. package/dist/esm/generator.js +509 -320
  52. package/dist/esm/generator.js.map +1 -1
  53. package/dist/esm/index.d.ts +7 -1
  54. package/dist/esm/index.js +26 -2
  55. package/dist/esm/index.js.map +1 -1
  56. package/dist/esm/template.d.ts +33 -0
  57. package/dist/esm/template.js +110 -0
  58. package/dist/esm/template.js.map +1 -0
  59. package/dist/esm/types.d.ts +20 -0
  60. package/dist/esm/utils.d.ts +47 -5
  61. package/dist/esm/utils.js +160 -11
  62. package/dist/esm/utils.js.map +1 -1
  63. package/package.json +27 -22
  64. package/src/config.ts +112 -8
  65. package/src/filesystem/physical/getRouteNodes.ts +295 -0
  66. package/src/filesystem/physical/rootPathId.ts +1 -0
  67. package/src/filesystem/virtual/config.ts +45 -0
  68. package/src/filesystem/virtual/getRouteNodes.ts +260 -0
  69. package/src/filesystem/virtual/loadConfigFile.ts +8 -0
  70. package/src/generator.ts +712 -395
  71. package/src/index.ts +33 -1
  72. package/src/template.ts +156 -0
  73. package/src/types.ts +31 -0
  74. package/src/utils.ts +237 -10
@@ -1,106 +1,24 @@
1
- import path from "path";
2
- import * as fs from "fs";
3
- import * as fsp from "fs/promises";
4
- import * as prettier from "prettier";
5
- import { logging, trimPathLeft, cleanPath } from "./utils.js";
1
+ import path from "node:path";
2
+ import * as fs from "node:fs";
3
+ import * as fsp from "node:fs/promises";
4
+ import { logging, multiSortBy, replaceBackslash, removeExt, writeIfDifferent, format, resetRegex, trimPathLeft, removeUnderscores, routePathToVariable } from "./utils.js";
5
+ import { getRouteNodes as getRouteNodes$1 } from "./filesystem/physical/getRouteNodes.js";
6
+ import { getRouteNodes } from "./filesystem/virtual/getRouteNodes.js";
7
+ import { rootPathId } from "./filesystem/physical/rootPathId.js";
8
+ import { getTargetTemplate, fillTemplate } from "./template.js";
9
+ const rootRouteId = "__root__";
6
10
  let latestTask = 0;
7
- const rootPathId = "__root";
8
11
  const routeGroupPatternRegex = /\(.+\)/g;
9
- async function getRouteNodes(config) {
10
- const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } = config;
11
- const logger = logging({ disabled: config.disableLogging });
12
- const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? "", "g");
13
- let routeNodes = [];
14
- async function recurse(dir) {
15
- const fullDir = path.resolve(config.routesDirectory, dir);
16
- let dirList = await fsp.readdir(fullDir, { withFileTypes: true });
17
- dirList = dirList.filter((d) => {
18
- if (d.name.startsWith(".") || routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix)) {
19
- return false;
20
- }
21
- if (routeFilePrefix) {
22
- return d.name.startsWith(routeFilePrefix);
23
- }
24
- if (routeFileIgnorePattern) {
25
- return !d.name.match(routeFileIgnoreRegExp);
26
- }
27
- return true;
28
- });
29
- await Promise.all(
30
- dirList.map(async (dirent) => {
31
- var _a;
32
- const fullPath = path.join(fullDir, dirent.name);
33
- const relativePath = path.join(dir, dirent.name);
34
- if (dirent.isDirectory()) {
35
- await recurse(relativePath);
36
- } else if (fullPath.match(/\.(tsx|ts|jsx|js)$/)) {
37
- const filePath = replaceBackslash(path.join(dir, dirent.name));
38
- const filePathNoExt = removeExt(filePath);
39
- let routePath = cleanPath(`/${filePathNoExt.split(".").join("/")}`) || "";
40
- if (routeFilePrefix) {
41
- routePath = routePath.replaceAll(routeFilePrefix, "");
42
- }
43
- const variableName = routePathToVariable(routePath);
44
- let isLazy = routePath == null ? void 0 : routePath.endsWith("/lazy");
45
- if (isLazy) {
46
- routePath = routePath == null ? void 0 : routePath.replace(/\/lazy$/, "");
47
- }
48
- let isRoute = routePath == null ? void 0 : routePath.endsWith("/route");
49
- let isComponent = routePath == null ? void 0 : routePath.endsWith("/component");
50
- let isErrorComponent = routePath == null ? void 0 : routePath.endsWith("/errorComponent");
51
- let isPendingComponent = routePath == null ? void 0 : routePath.endsWith("/pendingComponent");
52
- let isLoader = routePath == null ? void 0 : routePath.endsWith("/loader");
53
- const segments = (routePath ?? "").split("/");
54
- let isLayout = ((_a = segments[segments.length - 1]) == null ? void 0 : _a.startsWith("_")) || false;
55
- [
56
- [isComponent, "component"],
57
- [isErrorComponent, "errorComponent"],
58
- [isPendingComponent, "pendingComponent"],
59
- [isLoader, "loader"]
60
- ].forEach(([isType, type]) => {
61
- if (isType) {
62
- logger.warn(
63
- `WARNING: The \`.${type}.tsx\` suffix used for the ${filePath} file is deprecated. Use the new \`.lazy.tsx\` suffix instead.`
64
- );
65
- }
66
- });
67
- routePath = routePath == null ? void 0 : routePath.replace(
68
- /\/(component|errorComponent|pendingComponent|loader|route|lazy)$/,
69
- ""
70
- );
71
- if (routePath === "index") {
72
- routePath = "/";
73
- }
74
- routePath = routePath.replace(/\/index$/, "/") || "/";
75
- routeNodes.push({
76
- filePath,
77
- fullPath,
78
- routePath,
79
- variableName,
80
- isRoute,
81
- isComponent,
82
- isErrorComponent,
83
- isPendingComponent,
84
- isLoader,
85
- isLazy,
86
- isLayout
87
- });
88
- }
89
- })
90
- );
91
- return routeNodes;
92
- }
93
- await recurse("./");
94
- return routeNodes;
95
- }
96
- let first = false;
12
+ const possiblyNestedRouteGroupPatternRegex = /\([^/]+\)\/?/g;
13
+ let isFirst = false;
97
14
  let skipMessage = false;
98
- async function generator(config) {
15
+ async function generator(config, root) {
16
+ const ROUTE_TEMPLATE = getTargetTemplate(config.target);
99
17
  const logger = logging({ disabled: config.disableLogging });
100
18
  logger.log("");
101
- if (!first) {
19
+ if (!isFirst) {
102
20
  logger.log("♻️ Generating routes...");
103
- first = true;
21
+ isFirst = true;
104
22
  } else if (skipMessage) {
105
23
  skipMessage = false;
106
24
  } else {
@@ -116,31 +34,34 @@ async function generator(config) {
116
34
  return true;
117
35
  };
118
36
  const start = Date.now();
119
- const routePathIdPrefix = config.routeFilePrefix ?? "";
120
- const beforeRouteNodes = await getRouteNodes(config);
121
- const rootRouteNode = beforeRouteNodes.find(
122
- (d) => d.routePath === `/${rootPathId}`
123
- );
37
+ const TYPES_DISABLED = config.disableTypes;
38
+ let getRouteNodesResult;
39
+ if (config.virtualRouteConfig) {
40
+ getRouteNodesResult = await getRouteNodes(config, root);
41
+ } else {
42
+ getRouteNodesResult = await getRouteNodes$1(config, root);
43
+ }
44
+ const { rootRouteNode, routeNodes: beforeRouteNodes } = getRouteNodesResult;
45
+ if (rootRouteNode === void 0) {
46
+ let errorMessage = `rootRouteNode must not be undefined. Make sure you've added your root route into the route-tree.`;
47
+ if (!config.virtualRouteConfig) {
48
+ errorMessage += `
49
+ Make sure that you add a "${rootPathId}.${config.disableTypes ? "js" : "tsx"}" file to your routes directory.
50
+ Add the file in: "${config.routesDirectory}/${rootPathId}.${config.disableTypes ? "js" : "tsx"}"`;
51
+ }
52
+ throw new Error(errorMessage);
53
+ }
124
54
  const preRouteNodes = multiSortBy(beforeRouteNodes, [
125
55
  (d) => d.routePath === "/" ? -1 : 1,
126
56
  (d) => {
127
57
  var _a;
128
58
  return (_a = d.routePath) == null ? void 0 : _a.split("/").length;
129
59
  },
130
- (d) => {
131
- var _a;
132
- return ((_a = d.filePath) == null ? void 0 : _a.match(/[./]index[.]/)) ? 1 : -1;
133
- },
134
- (d) => {
135
- var _a;
136
- return ((_a = d.filePath) == null ? void 0 : _a.match(
137
- /[./](component|errorComponent|pendingComponent|loader|lazy)[.]/
138
- )) ? 1 : -1;
139
- },
140
- (d) => {
141
- var _a;
142
- return ((_a = d.filePath) == null ? void 0 : _a.match(/[./]route[.]/)) ? -1 : 1;
143
- },
60
+ (d) => d.filePath.match(new RegExp(`[./]${config.indexToken}[.]`)) ? 1 : -1,
61
+ (d) => d.filePath.match(
62
+ /[./](component|errorComponent|pendingComponent|loader|lazy)[.]/
63
+ ) ? 1 : -1,
64
+ (d) => d.filePath.match(new RegExp(`[./]${config.routeToken}[.]`)) ? -1 : 1,
144
65
  (d) => {
145
66
  var _a;
146
67
  return ((_a = d.routePath) == null ? void 0 : _a.endsWith("/")) ? -1 : 1;
@@ -149,9 +70,37 @@ async function generator(config) {
149
70
  ]).filter((d) => ![`/${rootPathId}`].includes(d.routePath || ""));
150
71
  const routeTree = [];
151
72
  const routePiecesByPath = {};
152
- let routeNodes = [];
73
+ const routeNodes = [];
74
+ const handleRootNode = async (node) => {
75
+ if (!node) {
76
+ return;
77
+ }
78
+ const routeCode = fs.readFileSync(node.fullPath, "utf-8");
79
+ if (!routeCode) {
80
+ const _rootTemplate = ROUTE_TEMPLATE.rootRoute;
81
+ const replaced = await fillTemplate(config, _rootTemplate.template(), {
82
+ tsrImports: _rootTemplate.imports.tsrImports(),
83
+ tsrPath: rootPathId,
84
+ tsrExportStart: _rootTemplate.imports.tsrExportStart(),
85
+ tsrExportEnd: _rootTemplate.imports.tsrExportEnd()
86
+ });
87
+ await writeIfDifferent(
88
+ node.fullPath,
89
+ "",
90
+ // Empty string because the file doesn't exist yet
91
+ replaced,
92
+ {
93
+ beforeWrite: () => {
94
+ logger.log(`🟡 Creating ${node.fullPath}`);
95
+ }
96
+ }
97
+ );
98
+ }
99
+ };
100
+ await handleRootNode(rootRouteNode);
153
101
  const handleNode = async (node) => {
154
- var _a, _b;
102
+ var _a, _b, _c, _d, _e;
103
+ resetRegex(routeGroupPatternRegex);
155
104
  let parentRoute = hasParentRoute(routeNodes, node, node.routePath);
156
105
  if ((parentRoute == null ? void 0 : parentRoute.isVirtualParentRoute) && ((_a = parentRoute.children) == null ? void 0 : _a.length)) {
157
106
  const possibleParentRoute = hasParentRoute(
@@ -163,76 +112,155 @@ async function generator(config) {
163
112
  parentRoute = possibleParentRoute;
164
113
  }
165
114
  }
166
- if (parentRoute)
167
- node.parent = parentRoute;
115
+ if (parentRoute) node.parent = parentRoute;
168
116
  node.path = determineNodePath(node);
169
117
  const trimmedPath = trimPathLeft(node.path ?? "");
170
- const split = (trimmedPath == null ? void 0 : trimmedPath.split("/")) ?? [];
171
- let first2 = split[0] ?? trimmedPath ?? "";
172
- const lastRouteSegment = split[split.length - 1] ?? trimmedPath ?? "";
173
- node.isNonPath = lastRouteSegment.startsWith("_");
174
- node.isNonLayout = first2.endsWith("_");
118
+ const split = trimmedPath.split("/");
119
+ const lastRouteSegment = split[split.length - 1] ?? trimmedPath;
120
+ node.isNonPath = lastRouteSegment.startsWith("_") || routeGroupPatternRegex.test(lastRouteSegment);
175
121
  node.cleanedPath = removeGroups(
176
122
  removeUnderscores(removeLayoutSegments(node.path)) ?? ""
177
123
  );
178
- if (!node.isVirtualParentRoute) {
124
+ if (!node.isVirtualParentRoute && !node.isVirtual) {
179
125
  const routeCode = fs.readFileSync(node.fullPath, "utf-8");
180
- const escapedRoutePath = removeTrailingUnderscores(
181
- ((_b = node.routePath) == null ? void 0 : _b.replaceAll("$", "$$")) ?? ""
182
- );
126
+ const escapedRoutePath = ((_b = node.routePath) == null ? void 0 : _b.replaceAll("$", "$$")) ?? "";
183
127
  let replaced = routeCode;
128
+ const tRouteTemplate = ROUTE_TEMPLATE.route;
129
+ const tLazyRouteTemplate = ROUTE_TEMPLATE.lazyRoute;
184
130
  if (!routeCode) {
185
- if (node.isLazy) {
186
- replaced = [
187
- `import { createLazyFileRoute } from '@tanstack/react-router'`,
188
- `export const Route = createLazyFileRoute('${escapedRoutePath}')({
189
- component: () => <div>Hello ${escapedRoutePath}!</div>
190
- })`
191
- ].join("\n\n");
192
- } else if (node.isRoute || !node.isComponent && !node.isErrorComponent && !node.isPendingComponent && !node.isLoader) {
193
- replaced = [
194
- `import { createFileRoute } from '@tanstack/react-router'`,
195
- `export const Route = createFileRoute('${escapedRoutePath}')({
196
- component: () => <div>Hello ${escapedRoutePath}!</div>
197
- })`
198
- ].join("\n\n");
131
+ if (node._fsRouteType === "lazy") {
132
+ replaced = await fillTemplate(
133
+ config,
134
+ (((_c = config.customScaffolding) == null ? void 0 : _c.lazyRouteTemplate) || ((_d = config.customScaffolding) == null ? void 0 : _d.routeTemplate)) ?? tLazyRouteTemplate.template(),
135
+ {
136
+ tsrImports: tLazyRouteTemplate.imports.tsrImports(),
137
+ tsrPath: escapedRoutePath.replaceAll(/\{(.+)\}/gm, "$1"),
138
+ tsrExportStart: tLazyRouteTemplate.imports.tsrExportStart(escapedRoutePath),
139
+ tsrExportEnd: tLazyRouteTemplate.imports.tsrExportEnd()
140
+ }
141
+ );
142
+ } else if (
143
+ // Creating a new normal route file
144
+ ["layout", "static"].some(
145
+ (d) => d === node._fsRouteType
146
+ ) || [
147
+ "component",
148
+ "pendingComponent",
149
+ "errorComponent",
150
+ "loader"
151
+ ].every((d) => d !== node._fsRouteType)
152
+ ) {
153
+ replaced = await fillTemplate(
154
+ config,
155
+ ((_e = config.customScaffolding) == null ? void 0 : _e.routeTemplate) ?? tRouteTemplate.template(),
156
+ {
157
+ tsrImports: tRouteTemplate.imports.tsrImports(),
158
+ tsrPath: escapedRoutePath.replaceAll(/\{(.+)\}/gm, "$1"),
159
+ tsrExportStart: tRouteTemplate.imports.tsrExportStart(escapedRoutePath),
160
+ tsrExportEnd: tRouteTemplate.imports.tsrExportEnd()
161
+ }
162
+ );
163
+ }
164
+ } else if (config.verboseFileRoutes === false) {
165
+ if (!routeCode.split("\n").some((line) => line.trim().startsWith("export const Route"))) {
166
+ return;
199
167
  }
168
+ replaced = routeCode.replace(
169
+ /(FileRoute\(\s*['"])([^\s]*)(['"],?\s*\))/g,
170
+ (_, p1, __, p3) => `${p1}${escapedRoutePath}${p3}`
171
+ ).replace(
172
+ new RegExp(
173
+ `(import\\s*\\{)(.*)(create(Lazy)?FileRoute)(.*)(\\}\\s*from\\s*['"]@tanstack\\/${ROUTE_TEMPLATE.subPkg}['"])`,
174
+ "gs"
175
+ ),
176
+ (_, p1, p2, ___, ____, p5, p6) => {
177
+ const beforeCreateFileRoute = () => {
178
+ if (!p2) return "";
179
+ let trimmed = p2.trim();
180
+ if (trimmed.endsWith(",")) {
181
+ trimmed = trimmed.slice(0, -1);
182
+ }
183
+ return trimmed;
184
+ };
185
+ const afterCreateFileRoute = () => {
186
+ if (!p5) return "";
187
+ let trimmed = p5.trim();
188
+ if (trimmed.startsWith(",")) {
189
+ trimmed = trimmed.slice(1);
190
+ }
191
+ return trimmed;
192
+ };
193
+ const newImport = () => {
194
+ const before = beforeCreateFileRoute();
195
+ const after = afterCreateFileRoute();
196
+ if (!before) return after;
197
+ if (!after) return before;
198
+ return `${before},${after}`;
199
+ };
200
+ const middle = newImport();
201
+ if (middle === "") return "";
202
+ return `${p1} ${newImport()} ${p6}`;
203
+ }
204
+ ).replace(
205
+ /create(Lazy)?FileRoute(\(\s*['"])([^\s]*)(['"],?\s*\))/g,
206
+ (_, __, p2, ___, p4) => `${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}`
207
+ );
200
208
  } else {
201
209
  replaced = routeCode.replace(
202
210
  /(FileRoute\(\s*['"])([^\s]*)(['"],?\s*\))/g,
203
- (match, p1, p2, p3) => `${p1}${escapedRoutePath}${p3}`
211
+ (_, p1, __, p3) => `${p1}${escapedRoutePath}${p3}`
212
+ ).replace(
213
+ /((FileRoute)(\s*)(\({))/g,
214
+ (_, __, p2, p3, p4) => `${p2}('${escapedRoutePath}')${p3}${p4}`
204
215
  ).replace(
205
- /(createFileRoute\(\s*['"])([^\s]*)(['"],?\s*\))/g,
206
- (match, p1, p2, p3) => `${p1}${escapedRoutePath}${p3}`
216
+ new RegExp(
217
+ `(import\\s*\\{.*)(create(Lazy)?FileRoute)(.*\\}\\s*from\\s*['"]@tanstack\\/${ROUTE_TEMPLATE.subPkg}['"])`,
218
+ "gs"
219
+ ),
220
+ (_, p1, __, ___, p4) => `${p1}${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}${p4}`
207
221
  ).replace(
208
- /(createLazyFileRoute\(\s*['"])([^\s]*)(['"],?\s*\))/g,
209
- (match, p1, p2, p3) => `${p1}${escapedRoutePath}${p3}`
222
+ /create(Lazy)?FileRoute(\(\s*['"])([^\s]*)(['"],?\s*\))/g,
223
+ (_, __, p2, ___, p4) => `${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}${p2}${escapedRoutePath}${p4}`
210
224
  );
225
+ const regex = new RegExp(
226
+ `(import\\s*\\{.*)(create(Lazy)?FileRoute)(.*\\}\\s*from\\s*['"]@tanstack\\/${ROUTE_TEMPLATE.subPkg}['"])`,
227
+ "gm"
228
+ );
229
+ if (!replaced.match(regex)) {
230
+ replaced = [
231
+ `import { ${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"} } from '@tanstack/${ROUTE_TEMPLATE.subPkg}'`,
232
+ ...replaced.split("\n")
233
+ ].join("\n");
234
+ }
211
235
  }
212
- if (replaced !== routeCode) {
213
- logger.log(`🟡 Updating ${node.fullPath}`);
214
- await fsp.writeFile(node.fullPath, replaced);
215
- }
236
+ await writeIfDifferent(node.fullPath, routeCode, replaced, {
237
+ beforeWrite: () => {
238
+ logger.log(`🟡 Updating ${node.fullPath}`);
239
+ }
240
+ });
216
241
  }
217
- if (!node.isVirtual && (node.isLoader || node.isComponent || node.isErrorComponent || node.isPendingComponent || node.isLazy)) {
242
+ if (!node.isVirtual && [
243
+ "lazy",
244
+ "loader",
245
+ "component",
246
+ "pendingComponent",
247
+ "errorComponent"
248
+ ].some((d) => d === node._fsRouteType)) {
218
249
  routePiecesByPath[node.routePath] = routePiecesByPath[node.routePath] || {};
219
- routePiecesByPath[node.routePath][node.isLazy ? "lazy" : node.isLoader ? "loader" : node.isErrorComponent ? "errorComponent" : node.isPendingComponent ? "pendingComponent" : "component"] = node;
250
+ routePiecesByPath[node.routePath][node._fsRouteType === "lazy" ? "lazy" : node._fsRouteType === "loader" ? "loader" : node._fsRouteType === "errorComponent" ? "errorComponent" : node._fsRouteType === "pendingComponent" ? "pendingComponent" : "component"] = node;
220
251
  const anchorRoute = routeNodes.find((d) => d.routePath === node.routePath);
221
252
  if (!anchorRoute) {
222
253
  await handleNode({
223
254
  ...node,
224
255
  isVirtual: true,
225
- isLazy: false,
226
- isLoader: false,
227
- isComponent: false,
228
- isErrorComponent: false,
229
- isPendingComponent: false
256
+ _fsRouteType: "static"
230
257
  });
231
258
  }
232
259
  return;
233
260
  }
234
261
  const cleanedPathIsEmpty = (node.cleanedPath || "").length === 0;
235
- node.isVirtualParentRequired = node.isLayout ? !cleanedPathIsEmpty : false;
262
+ const nonPathRoute = node._fsRouteType === "pathless_layout" && node.isNonPath;
263
+ node.isVirtualParentRequired = node._fsRouteType === "pathless_layout" || nonPathRoute ? !cleanedPathIsEmpty : false;
236
264
  if (!node.isVirtual && node.isVirtualParentRequired) {
237
265
  const parentRoutePath = removeLastSegmentFromPath(node.routePath) || "/";
238
266
  const parentVariableName = routePathToVariable(parentRoutePath);
@@ -248,14 +276,15 @@ async function generator(config) {
248
276
  routePath: parentRoutePath,
249
277
  variableName: parentVariableName,
250
278
  isVirtual: true,
251
- isLayout: false,
279
+ _fsRouteType: "layout",
280
+ // layout since this route will wrap other routes
252
281
  isVirtualParentRoute: true,
253
282
  isVirtualParentRequired: false
254
283
  };
255
284
  parentNode.children = parentNode.children ?? [];
256
285
  parentNode.children.push(node);
257
286
  node.parent = parentNode;
258
- if (node.isLayout) {
287
+ if (node._fsRouteType === "pathless_layout") {
259
288
  node.path = determineNodePath(node);
260
289
  }
261
290
  await handleNode(parentNode);
@@ -278,25 +307,43 @@ async function generator(config) {
278
307
  for (const node of preRouteNodes) {
279
308
  await handleNode(node);
280
309
  }
281
- function buildRouteConfig(nodes, depth = 1) {
310
+ checkRouteFullPathUniqueness(
311
+ preRouteNodes.filter(
312
+ (d) => d.children === void 0 && "lazy" !== d._fsRouteType
313
+ ),
314
+ config
315
+ );
316
+ function buildRouteTreeConfig(nodes, depth = 1) {
282
317
  const children = nodes.map((node) => {
283
318
  var _a, _b;
284
- if (node.isRoot) {
319
+ if (node._fsRouteType === "__root") {
285
320
  return;
286
321
  }
287
- if (node.isLayout && !((_a = node.children) == null ? void 0 : _a.length)) {
322
+ if (node._fsRouteType === "pathless_layout" && !((_a = node.children) == null ? void 0 : _a.length)) {
288
323
  return;
289
324
  }
290
325
  const route = `${node.variableName}Route`;
291
326
  if ((_b = node.children) == null ? void 0 : _b.length) {
292
- const childConfigs = buildRouteConfig(node.children, depth + 1);
293
- return `${route}.addChildren([${spaces(depth * 4)}${childConfigs}])`;
327
+ const childConfigs = buildRouteTreeConfig(node.children, depth + 1);
328
+ const childrenDeclaration = TYPES_DISABLED ? "" : `interface ${route}Children {
329
+ ${node.children.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(",")}
330
+ }`;
331
+ const children2 = `const ${route}Children${TYPES_DISABLED ? "" : `: ${route}Children`} = {
332
+ ${node.children.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(",")}
333
+ }`;
334
+ const routeWithChildren = `const ${route}WithChildren = ${route}._addFileChildren(${route}Children)`;
335
+ return [
336
+ childConfigs,
337
+ childrenDeclaration,
338
+ children2,
339
+ routeWithChildren
340
+ ].join("\n\n");
294
341
  }
295
- return route;
342
+ return void 0;
296
343
  });
297
- return children.filter(Boolean).join(`,`);
344
+ return children.filter(Boolean).join("\n\n");
298
345
  }
299
- const routeConfigChildrenText = buildRouteConfig(routeTree);
346
+ const routeConfigChildrenText = buildRouteTreeConfig(routeTree);
300
347
  const sortedRouteNodes = multiSortBy(routeNodes, [
301
348
  (d) => {
302
349
  var _a;
@@ -308,10 +355,25 @@ async function generator(config) {
308
355
  },
309
356
  (d) => {
310
357
  var _a;
311
- return ((_a = d.routePath) == null ? void 0 : _a.endsWith("index'")) ? -1 : 1;
358
+ return ((_a = d.routePath) == null ? void 0 : _a.endsWith(config.indexToken)) ? -1 : 1;
312
359
  },
313
360
  (d) => d
314
361
  ]);
362
+ const typeImports = Object.entries({
363
+ // Used for augmentation of regular routes
364
+ CreateFileRoute: config.verboseFileRoutes === false && sortedRouteNodes.some(
365
+ (d) => isRouteNodeValidForAugmentation(d) && d._fsRouteType !== "lazy"
366
+ ),
367
+ // Used for augmentation of lazy (`.lazy`) routes
368
+ CreateLazyFileRoute: config.verboseFileRoutes === false && sortedRouteNodes.some(
369
+ (node) => {
370
+ var _a;
371
+ return ((_a = routePiecesByPath[node.routePath]) == null ? void 0 : _a.lazy) && isRouteNodeValidForAugmentation(node);
372
+ }
373
+ ),
374
+ // Used in the process of augmenting the routes
375
+ FileRoutesByPath: config.verboseFileRoutes === false && sortedRouteNodes.some((d) => isRouteNodeValidForAugmentation(d))
376
+ }).filter((d) => d[1]).map((d) => d[0]).sort((a, b) => a.localeCompare(b));
315
377
  const imports = Object.entries({
316
378
  createFileRoute: sortedRouteNodes.some((d) => d.isVirtual),
317
379
  lazyFn: sortedRouteNodes.some(
@@ -328,43 +390,36 @@ async function generator(config) {
328
390
  )
329
391
  }).filter((d) => d[1]).map((d) => d[0]);
330
392
  const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual);
331
- const rootPathIdExtension = config.addExtensions && rootRouteNode ? path.extname(rootRouteNode.filePath) : "";
393
+ function getImportPath(node) {
394
+ return replaceBackslash(
395
+ removeExt(
396
+ path.relative(
397
+ path.dirname(config.generatedRouteTree),
398
+ path.resolve(config.routesDirectory, node.filePath)
399
+ ),
400
+ config.addExtensions
401
+ )
402
+ );
403
+ }
332
404
  const routeImports = [
333
- "/* prettier-ignore-start */",
334
- "/* eslint-disable */",
335
- "// @ts-nocheck",
336
- "// noinspection JSUnusedGlobalSymbols",
337
- "// This file is auto-generated by TanStack Router",
338
- imports.length ? `import { ${imports.join(", ")} } from '@tanstack/react-router'
339
- ` : "",
405
+ ...config.routeTreeFileHeader,
406
+ `// This file was automatically generated by TanStack Router.
407
+ // You should NOT make any changes in this file as it will be overwritten.
408
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.`,
409
+ [
410
+ imports.length ? `import { ${imports.join(", ")} } from '${ROUTE_TEMPLATE.fullPkg}'` : "",
411
+ !TYPES_DISABLED && typeImports.length ? `import type { ${typeImports.join(", ")} } from '${ROUTE_TEMPLATE.fullPkg}'` : ""
412
+ ].filter(Boolean).join("\n"),
340
413
  "// Import Routes",
341
414
  [
342
- `import { Route as rootRoute } from './${replaceBackslash(
343
- path.relative(
344
- path.dirname(config.generatedRouteTree),
345
- path.resolve(
346
- config.routesDirectory,
347
- `${routePathIdPrefix}${rootPathId}${rootPathIdExtension}`
348
- )
349
- )
350
- )}'`,
415
+ `import { Route as rootRoute } from './${getImportPath(rootRouteNode)}'`,
351
416
  ...sortedRouteNodes.filter((d) => !d.isVirtual).map((node) => {
352
- return `import { Route as ${node.variableName}Import } from './${replaceBackslash(
353
- removeExt(
354
- path.relative(
355
- path.dirname(config.generatedRouteTree),
356
- path.resolve(config.routesDirectory, node.filePath)
357
- ),
358
- config.addExtensions
359
- )
360
- )}'`;
417
+ return `import { Route as ${node.variableName}RouteImport } from './${getImportPath(node)}'`;
361
418
  })
362
419
  ].join("\n"),
363
420
  virtualRouteNodes.length ? "// Create Virtual Routes" : "",
364
421
  virtualRouteNodes.map((node) => {
365
- return `const ${node.variableName}Import = createFileRoute('${removeTrailingUnderscores(
366
- node.routePath
367
- )}')()`;
422
+ return `const ${node.variableName}RouteImport = createFileRoute('${node.routePath}')()`;
368
423
  }).join("\n"),
369
424
  "// Create/Update Routes",
370
425
  sortedRouteNodes.map((node) => {
@@ -375,155 +430,221 @@ async function generator(config) {
375
430
  const pendingComponentNode = (_d = routePiecesByPath[node.routePath]) == null ? void 0 : _d.pendingComponent;
376
431
  const lazyComponentNode = (_e = routePiecesByPath[node.routePath]) == null ? void 0 : _e.lazy;
377
432
  return [
378
- `const ${node.variableName}Route = ${node.variableName}Import.update({
433
+ [
434
+ `const ${node.variableName}Route = ${node.variableName}RouteImport.update({
379
435
  ${[
380
- node.isNonPath ? `id: '${node.path}'` : `path: '${node.cleanedPath}'`,
381
- `getParentRoute: () => ${((_f = node.parent) == null ? void 0 : _f.variableName) ?? "root"}Route`
382
- ].filter(Boolean).join(",")}
383
- }${config.disableTypes ? "" : "as any"})`,
384
- loaderNode ? `.updateLoader({ loader: lazyFn(() => import('./${replaceBackslash(
385
- removeExt(
386
- path.relative(
387
- path.dirname(config.generatedRouteTree),
388
- path.resolve(config.routesDirectory, loaderNode.filePath)
389
- ),
390
- config.addExtensions
391
- )
392
- )}'), 'loader') })` : "",
393
- componentNode || errorComponentNode || pendingComponentNode ? `.update({
394
- ${[
395
- ["component", componentNode],
396
- ["errorComponent", errorComponentNode],
397
- ["pendingComponent", pendingComponentNode]
398
- ].filter((d) => d[1]).map((d) => {
399
- return `${d[0]}: lazyRouteComponent(() => import('./${replaceBackslash(
436
+ `id: '${node.path}'`,
437
+ !node.isNonPath ? `path: '${node.cleanedPath}'` : void 0,
438
+ `getParentRoute: () => ${((_f = node.parent) == null ? void 0 : _f.variableName) ?? "root"}Route`
439
+ ].filter(Boolean).join(",")}
440
+ }${TYPES_DISABLED ? "" : "as any"})`,
441
+ loaderNode ? `.updateLoader({ loader: lazyFn(() => import('./${replaceBackslash(
400
442
  removeExt(
401
443
  path.relative(
402
444
  path.dirname(config.generatedRouteTree),
403
- path.resolve(config.routesDirectory, d[1].filePath)
445
+ path.resolve(config.routesDirectory, loaderNode.filePath)
404
446
  ),
405
447
  config.addExtensions
406
448
  )
407
- )}'), '${d[0]}')`;
408
- }).join("\n,")}
409
- })` : "",
410
- lazyComponentNode ? `.lazy(() => import('./${replaceBackslash(
411
- removeExt(
412
- path.relative(
413
- path.dirname(config.generatedRouteTree),
414
- path.resolve(
415
- config.routesDirectory,
416
- lazyComponentNode.filePath
449
+ )}'), 'loader') })` : "",
450
+ componentNode || errorComponentNode || pendingComponentNode ? `.update({
451
+ ${[
452
+ ["component", componentNode],
453
+ ["errorComponent", errorComponentNode],
454
+ ["pendingComponent", pendingComponentNode]
455
+ ].filter((d) => d[1]).map((d) => {
456
+ return `${d[0]}: lazyRouteComponent(() => import('./${replaceBackslash(
457
+ removeExt(
458
+ path.relative(
459
+ path.dirname(config.generatedRouteTree),
460
+ path.resolve(config.routesDirectory, d[1].filePath)
461
+ ),
462
+ config.addExtensions
417
463
  )
418
- ),
419
- config.addExtensions
420
- )
421
- )}').then((d) => d.Route))` : ""
422
- ].join("");
464
+ )}'), '${d[0]}')`;
465
+ }).join("\n,")}
466
+ })` : "",
467
+ lazyComponentNode ? `.lazy(() => import('./${replaceBackslash(
468
+ removeExt(
469
+ path.relative(
470
+ path.dirname(config.generatedRouteTree),
471
+ path.resolve(
472
+ config.routesDirectory,
473
+ lazyComponentNode.filePath
474
+ )
475
+ ),
476
+ config.addExtensions
477
+ )
478
+ )}').then((d) => d.Route))` : ""
479
+ ].join("")
480
+ ].join("\n\n");
423
481
  }).join("\n\n"),
424
- ...config.disableTypes ? [] : [
482
+ ...TYPES_DISABLED ? [] : [
425
483
  "// Populate the FileRoutesByPath interface",
426
- `declare module '@tanstack/react-router' {
484
+ `declare module '${ROUTE_TEMPLATE.fullPkg}' {
427
485
  interface FileRoutesByPath {
428
486
  ${routeNodes.map((routeNode) => {
429
- var _a, _b, _c;
430
- return `'${removeTrailingUnderscores(routeNode.routePath)}': {
431
- preLoaderRoute: typeof ${routeNode.variableName}Import
432
- parentRoute: typeof ${routeNode.isVirtualParentRequired ? `${(_a = routeNode.parent) == null ? void 0 : _a.variableName}Route` : ((_b = routeNode.parent) == null ? void 0 : _b.variableName) ? `${(_c = routeNode.parent) == null ? void 0 : _c.variableName}Import` : "rootRoute"}
487
+ var _a, _b;
488
+ const filePathId = routeNode.routePath;
489
+ return `'${filePathId}': {
490
+ id: '${filePathId}'
491
+ path: '${inferPath(routeNode)}'
492
+ fullPath: '${inferFullPath(routeNode)}'
493
+ preLoaderRoute: typeof ${routeNode.variableName}RouteImport
494
+ parentRoute: typeof ${routeNode.isVirtualParentRequired ? `${(_a = routeNode.parent) == null ? void 0 : _a.variableName}Route` : ((_b = routeNode.parent) == null ? void 0 : _b.variableName) ? `${routeNode.parent.variableName}RouteImport` : "rootRoute"}
433
495
  }`;
434
496
  }).join("\n")}
435
497
  }
436
498
  }`
437
499
  ],
500
+ ...TYPES_DISABLED ? [] : config.verboseFileRoutes !== false ? [] : [
501
+ `// Add type-safety to the createFileRoute function across the route tree`,
502
+ routeNodes.map((routeNode) => {
503
+ var _a;
504
+ function getModuleDeclaration(routeNode2) {
505
+ if (!isRouteNodeValidForAugmentation(routeNode2)) {
506
+ return "";
507
+ }
508
+ return `declare module './${getImportPath(routeNode2)}' {
509
+ const ${routeNode2._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}: ${routeNode2._fsRouteType === "lazy" ? `CreateLazyFileRoute<FileRoutesByPath['${routeNode2.routePath}']['preLoaderRoute']>}` : `CreateFileRoute<
510
+ '${routeNode2.routePath}',
511
+ FileRoutesByPath['${routeNode2.routePath}']['parentRoute'],
512
+ FileRoutesByPath['${routeNode2.routePath}']['id'],
513
+ FileRoutesByPath['${routeNode2.routePath}']['path'],
514
+ FileRoutesByPath['${routeNode2.routePath}']['fullPath']
515
+ >
516
+ }`}`;
517
+ }
518
+ return getModuleDeclaration(routeNode) + getModuleDeclaration(
519
+ (_a = routePiecesByPath[routeNode.routePath]) == null ? void 0 : _a.lazy
520
+ );
521
+ }).join("\n")
522
+ ],
438
523
  "// Create and export the route tree",
439
- `export const routeTree = rootRoute.addChildren([${routeConfigChildrenText}])`,
440
- "/* prettier-ignore-end */"
524
+ routeConfigChildrenText,
525
+ ...TYPES_DISABLED ? [] : [
526
+ `export interface FileRoutesByFullPath {
527
+ ${[...createRouteNodesByFullPath(routeNodes).entries()].map(
528
+ ([fullPath, routeNode]) => {
529
+ return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
530
+ }
531
+ )}
532
+ }`,
533
+ `export interface FileRoutesByTo {
534
+ ${[...createRouteNodesByTo(routeNodes).entries()].map(([to, routeNode]) => {
535
+ return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
536
+ })}
537
+ }`,
538
+ `export interface FileRoutesById {
539
+ '${rootRouteId}': typeof rootRoute,
540
+ ${[...createRouteNodesById(routeNodes).entries()].map(([id, routeNode]) => {
541
+ return `'${id}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
542
+ })}
543
+ }`,
544
+ `export interface FileRouteTypes {
545
+ fileRoutesByFullPath: FileRoutesByFullPath
546
+ fullPaths: ${routeNodes.length > 0 ? [...createRouteNodesByFullPath(routeNodes).keys()].map((fullPath) => `'${fullPath}'`).join("|") : "never"}
547
+ fileRoutesByTo: FileRoutesByTo
548
+ to: ${routeNodes.length > 0 ? [...createRouteNodesByTo(routeNodes).keys()].map((to) => `'${to}'`).join("|") : "never"}
549
+ id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(routeNodes).keys()].map((id) => `'${id}'`)].join("|")}
550
+ fileRoutesById: FileRoutesById
551
+ }`,
552
+ `export interface RootRouteChildren {
553
+ ${routeTree.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(",")}
554
+ }`
555
+ ],
556
+ `const rootRouteChildren${TYPES_DISABLED ? "" : ": RootRouteChildren"} = {
557
+ ${routeTree.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(",")}
558
+ }`,
559
+ `export const routeTree = rootRoute._addFileChildren(rootRouteChildren)${TYPES_DISABLED ? "" : "._addFileTypes<FileRouteTypes>()"}`,
560
+ ...config.routeTreeFileFooter
441
561
  ].filter(Boolean).join("\n\n");
442
- const routeConfigFileContent = await prettier.format(routeImports, {
443
- semi: false,
444
- singleQuote: config.quoteStyle === "single",
445
- parser: "typescript"
446
- });
447
- const routeTreeContent = await fsp.readFile(path.resolve(config.generatedRouteTree), "utf-8").catch((err) => {
562
+ const createRouteManifest = () => {
563
+ const routesManifest = {
564
+ [rootRouteId]: {
565
+ filePath: rootRouteNode.filePath,
566
+ children: routeTree.map((d) => d.routePath)
567
+ },
568
+ ...Object.fromEntries(
569
+ routeNodes.map((d) => {
570
+ var _a, _b;
571
+ const filePathId = d.routePath;
572
+ return [
573
+ filePathId,
574
+ {
575
+ filePath: d.filePath,
576
+ parent: ((_a = d.parent) == null ? void 0 : _a.routePath) ? d.parent.routePath : void 0,
577
+ children: (_b = d.children) == null ? void 0 : _b.map((childRoute) => childRoute.routePath)
578
+ }
579
+ ];
580
+ })
581
+ )
582
+ };
583
+ return JSON.stringify(
584
+ {
585
+ routes: routesManifest
586
+ },
587
+ null,
588
+ 2
589
+ );
590
+ };
591
+ const includeManifest = ["react", "solid"];
592
+ const routeConfigFileContent = config.disableManifestGeneration || !includeManifest.includes(config.target) ? routeImports : [
593
+ routeImports,
594
+ "\n",
595
+ "/* ROUTE_MANIFEST_START",
596
+ createRouteManifest(),
597
+ "ROUTE_MANIFEST_END */"
598
+ ].join("\n");
599
+ if (!checkLatest()) return;
600
+ const existingRouteTreeContent = await fsp.readFile(path.resolve(config.generatedRouteTree), "utf-8").catch((err) => {
448
601
  if (err.code === "ENOENT") {
449
- return void 0;
602
+ return "";
450
603
  }
451
604
  throw err;
452
605
  });
453
- if (!checkLatest())
606
+ if (!checkLatest()) return;
607
+ await fsp.mkdir(path.dirname(path.resolve(config.generatedRouteTree)), {
608
+ recursive: true
609
+ });
610
+ if (!checkLatest()) return;
611
+ const routeTreeWriteResult = await writeIfDifferent(
612
+ path.resolve(config.generatedRouteTree),
613
+ config.enableRouteTreeFormatting ? await format(existingRouteTreeContent, config) : existingRouteTreeContent,
614
+ config.enableRouteTreeFormatting ? await format(routeConfigFileContent, config) : routeConfigFileContent,
615
+ {
616
+ beforeWrite: () => {
617
+ logger.log(`🟡 Updating ${config.generatedRouteTree}`);
618
+ }
619
+ }
620
+ );
621
+ if (routeTreeWriteResult && !checkLatest()) {
454
622
  return;
455
- if (routeTreeContent !== routeConfigFileContent) {
456
- await fsp.mkdir(path.dirname(path.resolve(config.generatedRouteTree)), {
457
- recursive: true
458
- });
459
- if (!checkLatest())
460
- return;
461
- await fsp.writeFile(
462
- path.resolve(config.generatedRouteTree),
463
- routeConfigFileContent
464
- );
465
623
  }
466
624
  logger.log(
467
625
  `✅ Processed ${routeNodes.length === 1 ? "route" : "routes"} in ${Date.now() - start}ms`
468
626
  );
469
627
  }
470
- function routePathToVariable(d) {
471
- var _a, _b, _c, _d;
472
- return ((_d = (_c = (_b = (_a = removeUnderscores(d)) == null ? void 0 : _a.replace(/\/\$\//g, "/splat/")) == null ? void 0 : _b.replace(/\$$/g, "splat")) == null ? void 0 : _c.replace(/\$/g, "")) == null ? void 0 : _d.split(/[/-]/g).map((d2, i) => i > 0 ? capitalize(d2) : d2).join("").replace(/([^a-zA-Z0-9]|[\.])/gm, "").replace(/^(\d)/g, "R$1")) ?? "";
473
- }
474
- function removeExt(d, keepExtension = false) {
475
- return keepExtension ? d : d.substring(0, d.lastIndexOf(".")) || d;
476
- }
477
- function spaces(d) {
478
- return Array.from({ length: d }).map(() => " ").join("");
479
- }
480
- function multiSortBy(arr, accessors = [(d) => d]) {
481
- return arr.map((d, i) => [d, i]).sort(([a, ai], [b, bi]) => {
482
- for (const accessor of accessors) {
483
- const ao = accessor(a);
484
- const bo = accessor(b);
485
- if (typeof ao === "undefined") {
486
- if (typeof bo === "undefined") {
487
- continue;
488
- }
489
- return 1;
490
- }
491
- if (ao === bo) {
492
- continue;
493
- }
494
- return ao > bo ? 1 : -1;
495
- }
496
- return ai - bi;
497
- }).map(([d]) => d);
498
- }
499
- function capitalize(s) {
500
- if (typeof s !== "string")
501
- return "";
502
- return s.charAt(0).toUpperCase() + s.slice(1);
503
- }
504
- function removeUnderscores(s) {
505
- return s == null ? void 0 : s.replaceAll(/(^_|_$)/gi, "").replaceAll(/(\/_|_\/)/gi, "/");
506
- }
507
- function removeTrailingUnderscores(s) {
508
- return s == null ? void 0 : s.replaceAll(/(_$)/gi, "").replaceAll(/(_\/)/gi, "/");
509
- }
510
- function replaceBackslash(s) {
511
- return s.replaceAll(/\\/gi, "/");
512
- }
513
628
  function removeGroups(s) {
514
- return s.replaceAll(routeGroupPatternRegex, "").replaceAll("//", "/");
629
+ return s.replace(possiblyNestedRouteGroupPatternRegex, "");
630
+ }
631
+ function isRouteNodeValidForAugmentation(routeNode) {
632
+ if (!routeNode || routeNode.isVirtual) {
633
+ return false;
634
+ }
635
+ return true;
515
636
  }
516
637
  function determineNodePath(node) {
517
638
  var _a;
518
- return node.path = node.parent ? ((_a = node.routePath) == null ? void 0 : _a.replace(node.parent.routePath, "")) || "/" : node.routePath;
639
+ return node.path = node.parent ? ((_a = node.routePath) == null ? void 0 : _a.replace(node.parent.routePath ?? "", "")) || "/" : node.routePath;
519
640
  }
520
- function removeLastSegmentFromPath(path2 = "/") {
521
- const segments = path2.split("/");
641
+ function removeLastSegmentFromPath(routePath = "/") {
642
+ const segments = routePath.split("/");
522
643
  segments.pop();
523
644
  return segments.join("/");
524
645
  }
525
- function removeLayoutSegments(path2 = "/") {
526
- const segments = path2.split("/");
646
+ function removeLayoutSegments(routePath = "/") {
647
+ const segments = routePath.split("/");
527
648
  const newSegments = segments.filter((segment) => !segment.startsWith("_"));
528
649
  return newSegments.join("/");
529
650
  }
@@ -536,8 +657,7 @@ function hasParentRoute(routes, node, routePathToCheck) {
536
657
  (d) => d.variableName
537
658
  ]).filter((d) => d.routePath !== `/${rootPathId}`);
538
659
  for (const route of sortedNodes) {
539
- if (route.routePath === "/")
540
- continue;
660
+ if (route.routePath === "/") continue;
541
661
  if (routePathToCheck.startsWith(`${route.routePath}/`) && route.routePath !== routePathToCheck) {
542
662
  return route;
543
663
  }
@@ -547,12 +667,81 @@ function hasParentRoute(routes, node, routePathToCheck) {
547
667
  const parentRoutePath = segments.join("/");
548
668
  return hasParentRoute(routes, node, parentRoutePath);
549
669
  }
670
+ const getResolvedRouteNodeVariableName = (routeNode) => {
671
+ var _a;
672
+ return ((_a = routeNode.children) == null ? void 0 : _a.length) ? `${routeNode.variableName}RouteWithChildren` : `${routeNode.variableName}Route`;
673
+ };
674
+ const createRouteNodesByFullPath = (routeNodes) => {
675
+ return new Map(
676
+ routeNodes.map((routeNode) => [inferFullPath(routeNode), routeNode])
677
+ );
678
+ };
679
+ const createRouteNodesByTo = (routeNodes) => {
680
+ return new Map(
681
+ dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [
682
+ inferTo(routeNode),
683
+ routeNode
684
+ ])
685
+ );
686
+ };
687
+ const createRouteNodesById = (routeNodes) => {
688
+ return new Map(
689
+ routeNodes.map((routeNode) => {
690
+ const id = routeNode.routePath ?? "";
691
+ return [id, routeNode];
692
+ })
693
+ );
694
+ };
695
+ const inferFullPath = (routeNode) => {
696
+ const fullPath = removeGroups(
697
+ removeUnderscores(removeLayoutSegments(routeNode.routePath)) ?? ""
698
+ );
699
+ return routeNode.cleanedPath === "/" ? fullPath : fullPath.replace(/\/$/, "");
700
+ };
701
+ const inferPath = (routeNode) => {
702
+ var _a;
703
+ return routeNode.cleanedPath === "/" ? routeNode.cleanedPath : ((_a = routeNode.cleanedPath) == null ? void 0 : _a.replace(/\/$/, "")) ?? "";
704
+ };
705
+ const inferTo = (routeNode) => {
706
+ const fullPath = inferFullPath(routeNode);
707
+ if (fullPath === "/") return fullPath;
708
+ return fullPath.replace(/\/$/, "");
709
+ };
710
+ const dedupeBranchesAndIndexRoutes = (routes) => {
711
+ return routes.filter((route) => {
712
+ var _a;
713
+ if ((_a = route.children) == null ? void 0 : _a.find((child) => child.cleanedPath === "/")) return false;
714
+ return true;
715
+ });
716
+ };
717
+ function checkUnique(routes, key) {
718
+ const keys = routes.map((d) => d[key]);
719
+ const uniqueKeys = new Set(keys);
720
+ if (keys.length !== uniqueKeys.size) {
721
+ const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i);
722
+ const conflictingFiles = routes.filter(
723
+ (d) => duplicateKeys.includes(d[key])
724
+ );
725
+ return conflictingFiles;
726
+ }
727
+ return void 0;
728
+ }
729
+ function checkRouteFullPathUniqueness(_routes, config) {
730
+ const routes = _routes.map((d) => {
731
+ const inferredFullPath = inferFullPath(d);
732
+ return { ...d, inferredFullPath };
733
+ });
734
+ const conflictingFiles = checkUnique(routes, "inferredFullPath");
735
+ if (conflictingFiles !== void 0) {
736
+ const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? "s" : ""}: ${conflictingFiles.map((p) => `"${p.inferredFullPath}"`).join(", ")}.
737
+ Please ensure each Route has a unique full path.
738
+ Conflicting files:
739
+ ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join("\n ")}
740
+ `;
741
+ throw new Error(errorMessage);
742
+ }
743
+ }
550
744
  export {
551
- generator,
552
- hasParentRoute,
553
- multiSortBy,
554
- removeExt,
555
- removeLastSegmentFromPath,
556
- rootPathId
745
+ generator
557
746
  };
558
747
  //# sourceMappingURL=generator.js.map