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