@tanstack/router-generator 1.121.0-alpha.5 → 1.121.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 (90) hide show
  1. package/dist/cjs/config.cjs +23 -5
  2. package/dist/cjs/config.cjs.map +1 -1
  3. package/dist/cjs/config.d.cts +7 -3
  4. package/dist/cjs/filesystem/physical/getRouteNodes.cjs +5 -3
  5. package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
  6. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +1 -1
  7. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
  8. package/dist/cjs/generator.cjs +836 -668
  9. package/dist/cjs/generator.cjs.map +1 -1
  10. package/dist/cjs/generator.d.cts +71 -1
  11. package/dist/cjs/index.cjs +5 -2
  12. package/dist/cjs/index.cjs.map +1 -1
  13. package/dist/cjs/index.d.cts +7 -3
  14. package/dist/cjs/logger.cjs +37 -0
  15. package/dist/cjs/logger.cjs.map +1 -0
  16. package/dist/cjs/logger.d.cts +10 -0
  17. package/dist/cjs/plugin/default-generator-plugin.cjs +88 -0
  18. package/dist/cjs/plugin/default-generator-plugin.cjs.map +1 -0
  19. package/dist/cjs/plugin/default-generator-plugin.d.cts +2 -0
  20. package/dist/cjs/plugin/types.d.cts +46 -0
  21. package/dist/cjs/template.cjs +10 -10
  22. package/dist/cjs/template.cjs.map +1 -1
  23. package/dist/cjs/template.d.cts +2 -2
  24. package/dist/cjs/transform/default-transform-plugin.cjs +95 -0
  25. package/dist/cjs/transform/default-transform-plugin.cjs.map +1 -0
  26. package/dist/cjs/transform/default-transform-plugin.d.cts +2 -0
  27. package/dist/cjs/transform/transform.cjs +351 -0
  28. package/dist/cjs/transform/transform.cjs.map +1 -0
  29. package/dist/cjs/transform/transform.d.cts +4 -0
  30. package/dist/cjs/transform/types.d.cts +43 -0
  31. package/dist/cjs/transform/utils.cjs +36 -0
  32. package/dist/cjs/transform/utils.cjs.map +1 -0
  33. package/dist/cjs/transform/utils.d.cts +2 -0
  34. package/dist/cjs/types.d.cts +22 -0
  35. package/dist/cjs/utils.cjs +262 -40
  36. package/dist/cjs/utils.cjs.map +1 -1
  37. package/dist/cjs/utils.d.cts +82 -9
  38. package/dist/esm/config.d.ts +7 -3
  39. package/dist/esm/config.js +21 -3
  40. package/dist/esm/config.js.map +1 -1
  41. package/dist/esm/filesystem/physical/getRouteNodes.js +3 -1
  42. package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
  43. package/dist/esm/filesystem/virtual/getRouteNodes.js +1 -1
  44. package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
  45. package/dist/esm/generator.d.ts +71 -1
  46. package/dist/esm/generator.js +827 -658
  47. package/dist/esm/generator.js.map +1 -1
  48. package/dist/esm/index.d.ts +7 -3
  49. package/dist/esm/index.js +7 -4
  50. package/dist/esm/index.js.map +1 -1
  51. package/dist/esm/logger.d.ts +10 -0
  52. package/dist/esm/logger.js +37 -0
  53. package/dist/esm/logger.js.map +1 -0
  54. package/dist/esm/plugin/default-generator-plugin.d.ts +2 -0
  55. package/dist/esm/plugin/default-generator-plugin.js +88 -0
  56. package/dist/esm/plugin/default-generator-plugin.js.map +1 -0
  57. package/dist/esm/plugin/types.d.ts +46 -0
  58. package/dist/esm/template.d.ts +2 -2
  59. package/dist/esm/template.js +10 -10
  60. package/dist/esm/template.js.map +1 -1
  61. package/dist/esm/transform/default-transform-plugin.d.ts +2 -0
  62. package/dist/esm/transform/default-transform-plugin.js +95 -0
  63. package/dist/esm/transform/default-transform-plugin.js.map +1 -0
  64. package/dist/esm/transform/transform.d.ts +4 -0
  65. package/dist/esm/transform/transform.js +351 -0
  66. package/dist/esm/transform/transform.js.map +1 -0
  67. package/dist/esm/transform/types.d.ts +43 -0
  68. package/dist/esm/transform/utils.d.ts +2 -0
  69. package/dist/esm/transform/utils.js +36 -0
  70. package/dist/esm/transform/utils.js.map +1 -0
  71. package/dist/esm/types.d.ts +22 -0
  72. package/dist/esm/utils.d.ts +82 -9
  73. package/dist/esm/utils.js +262 -40
  74. package/dist/esm/utils.js.map +1 -1
  75. package/package.json +9 -10
  76. package/src/config.ts +23 -2
  77. package/src/filesystem/physical/getRouteNodes.ts +2 -1
  78. package/src/filesystem/virtual/getRouteNodes.ts +1 -1
  79. package/src/generator.ts +1123 -945
  80. package/src/index.ts +25 -3
  81. package/src/logger.ts +43 -0
  82. package/src/plugin/default-generator-plugin.ts +96 -0
  83. package/src/plugin/types.ts +51 -0
  84. package/src/template.ts +33 -12
  85. package/src/transform/default-transform-plugin.ts +103 -0
  86. package/src/transform/transform.ts +430 -0
  87. package/src/transform/types.ts +50 -0
  88. package/src/transform/utils.ts +42 -0
  89. package/src/types.ts +25 -0
  90. package/src/utils.ts +385 -36
@@ -1,107 +1,828 @@
1
1
  import path from "node:path";
2
- import * as fs from "node:fs";
3
2
  import * as fsp from "node:fs/promises";
4
- import { logging, multiSortBy, replaceBackslash, removeExt, writeIfDifferent, format, resetRegex, trimPathLeft, removeUnderscores, routePathToVariable } from "./utils.js";
3
+ import { mkdtempSync } from "node:fs";
4
+ import crypto from "node:crypto";
5
+ import { deepEqual, rootRouteId } from "@tanstack/router-core";
6
+ import { logging } from "./logger.js";
5
7
  import { getRouteNodes as getRouteNodes$1 } from "./filesystem/physical/getRouteNodes.js";
6
8
  import { getRouteNodes } from "./filesystem/virtual/getRouteNodes.js";
7
9
  import { rootPathId } from "./filesystem/physical/rootPathId.js";
10
+ import { multiSortBy, format, mergeImportDeclarations, buildImportString, replaceBackslash, removeExt, checkFileExists, resetRegex, hasParentRoute, determineNodePath, trimPathLeft, removeGroups, removeUnderscores, removeLayoutSegments, removeLastSegmentFromPath, routePathToVariable, buildRouteTreeConfig, findParent, createRouteNodesByFullPath, createRouteNodesByTo, createRouteNodesById, getResolvedRouteNodeVariableName, buildFileRoutesByPathInterface, lowerCaseFirstChar, isRouteNodeValidForAugmentation } from "./utils.js";
8
11
  import { getTargetTemplate, fillTemplate } from "./template.js";
9
- const rootRouteId = "__root__";
10
- let latestTask = 0;
11
- const routeGroupPatternRegex = /\(.+\)/g;
12
- const possiblyNestedRouteGroupPatternRegex = /\([^/]+\)\/?/g;
13
- let isFirst = false;
14
- let skipMessage = false;
15
- async function generator(config, root) {
16
- const ROUTE_TEMPLATE = getTargetTemplate(config.target);
17
- const logger = logging({ disabled: config.disableLogging });
18
- logger.log("");
19
- if (!isFirst) {
20
- logger.log("♻️ Generating routes...");
21
- isFirst = true;
22
- } else if (skipMessage) {
23
- skipMessage = false;
24
- } else {
25
- logger.log("♻️ Regenerating routes...");
12
+ import { transform } from "./transform/transform.js";
13
+ import { defaultGeneratorPlugin } from "./plugin/default-generator-plugin.js";
14
+ const DefaultFileSystem = {
15
+ stat: (filePath) => fsp.stat(filePath, { bigint: true }),
16
+ mkdtempSync,
17
+ rename: (oldPath, newPath) => fsp.rename(oldPath, newPath),
18
+ writeFile: (filePath, content) => fsp.writeFile(filePath, content),
19
+ readFile: async (filePath) => {
20
+ try {
21
+ const fileHandle = await fsp.open(filePath, "r");
22
+ const stat = await fileHandle.stat({ bigint: true });
23
+ const fileContent = (await fileHandle.readFile()).toString();
24
+ await fileHandle.close();
25
+ return { stat, fileContent };
26
+ } catch (e) {
27
+ if ("code" in e) {
28
+ if (e.code === "ENOENT") {
29
+ return "file-not-existing";
30
+ }
31
+ }
32
+ throw e;
33
+ }
26
34
  }
27
- const taskId = latestTask + 1;
28
- latestTask = taskId;
29
- const checkLatest = () => {
30
- if (latestTask !== taskId) {
31
- skipMessage = true;
32
- return false;
33
- }
34
- return true;
35
- };
36
- const start = Date.now();
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);
35
+ };
36
+ function rerun(opts) {
37
+ const { event, ...rest } = opts;
38
+ return { rerun: true, event: event ?? { type: "rerun" }, ...rest };
39
+ }
40
+ function isRerun(result) {
41
+ return typeof result === "object" && result !== null && "rerun" in result && result.rerun === true;
42
+ }
43
+ class Generator {
44
+ constructor(opts) {
45
+ this.routeNodeCache = /* @__PURE__ */ new Map();
46
+ this.routeNodeShadowCache = /* @__PURE__ */ new Map();
47
+ this.fileEventQueue = [];
48
+ this.plugins = [defaultGeneratorPlugin()];
49
+ this.pluginsWithTransform = [];
50
+ this.transformPlugins = [];
51
+ this.routeGroupPatternRegex = /\(.+\)/g;
52
+ this.config = opts.config;
53
+ this.logger = logging({ disabled: this.config.disableLogging });
54
+ this.root = opts.root;
55
+ this.fs = opts.fs || DefaultFileSystem;
56
+ this.tmpDir = this.fs.mkdtempSync(
57
+ path.join(this.config.tmpDir, "router-generator-")
58
+ );
59
+ this.generatedRouteTreePath = path.resolve(this.config.generatedRouteTree);
60
+ this.targetTemplate = getTargetTemplate(this.config);
61
+ this.routesDirectoryPath = this.getRoutesDirectoryPath();
62
+ this.plugins.push(...opts.config.plugins || []);
63
+ this.plugins.forEach((plugin) => {
64
+ if ("transformPlugin" in plugin) {
65
+ if (this.pluginsWithTransform.find((p) => p.name === plugin.name)) {
66
+ throw new Error(
67
+ `Plugin with name "${plugin.name}" is already registered for export ${plugin.transformPlugin.exportName}!`
68
+ );
69
+ }
70
+ this.pluginsWithTransform.push(plugin);
71
+ this.transformPlugins.push(plugin.transformPlugin);
72
+ }
73
+ });
43
74
  }
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);
75
+ getRoutesDirectoryPath() {
76
+ return path.isAbsolute(this.config.routesDirectory) ? this.config.routesDirectory : path.resolve(this.root, this.config.routesDirectory);
53
77
  }
54
- const preRouteNodes = multiSortBy(beforeRouteNodes, [
55
- (d) => d.routePath === "/" ? -1 : 1,
56
- (d) => {
57
- var _a;
58
- return (_a = d.routePath) == null ? void 0 : _a.split("/").length;
59
- },
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,
65
- (d) => {
78
+ async run(event) {
79
+ if (event && event.type !== "rerun") {
80
+ if (!(event.path === this.generatedRouteTreePath || event.path.startsWith(this.routesDirectoryPath))) {
81
+ return;
82
+ }
83
+ }
84
+ this.fileEventQueue.push(event ?? { type: "rerun" });
85
+ if (this.runPromise) {
86
+ return this.runPromise;
87
+ }
88
+ this.runPromise = (async () => {
89
+ do {
90
+ const tempQueue = this.fileEventQueue;
91
+ this.fileEventQueue = [];
92
+ const remainingEvents = (await Promise.all(
93
+ tempQueue.map(async (e) => {
94
+ if (e.type === "update") {
95
+ let cacheEntry;
96
+ if (e.path === this.generatedRouteTreePath) {
97
+ cacheEntry = this.routeTreeFileCache;
98
+ } else {
99
+ cacheEntry = this.routeNodeCache.get(e.path);
100
+ }
101
+ const change = await this.didFileChangeComparedToCache(
102
+ { path: e.path },
103
+ cacheEntry
104
+ );
105
+ if (change.result === false) {
106
+ return null;
107
+ }
108
+ }
109
+ return e;
110
+ })
111
+ )).filter((e) => e !== null);
112
+ if (remainingEvents.length === 0) {
113
+ break;
114
+ }
115
+ try {
116
+ const start = performance.now();
117
+ await this.generatorInternal();
118
+ const end = performance.now();
119
+ this.logger.info(
120
+ `Generated route tree in ${Math.round(end - start)}ms`
121
+ );
122
+ } catch (err) {
123
+ const errArray = !Array.isArray(err) ? [err] : err;
124
+ const recoverableErrors = errArray.filter((e) => isRerun(e));
125
+ if (recoverableErrors.length === errArray.length) {
126
+ this.fileEventQueue.push(...recoverableErrors.map((e) => e.event));
127
+ recoverableErrors.forEach((e) => {
128
+ if (e.msg) {
129
+ this.logger.info(e.msg);
130
+ }
131
+ });
132
+ } else {
133
+ const unrecoverableErrors = errArray.filter((e) => !isRerun(e));
134
+ this.runPromise = void 0;
135
+ throw new Error(
136
+ unrecoverableErrors.map((e) => e.message).join()
137
+ );
138
+ }
139
+ }
140
+ } while (this.fileEventQueue.length);
141
+ this.runPromise = void 0;
142
+ })();
143
+ return this.runPromise;
144
+ }
145
+ async generatorInternal() {
146
+ let writeRouteTreeFile = false;
147
+ let getRouteNodesResult;
148
+ if (this.config.virtualRouteConfig) {
149
+ getRouteNodesResult = await getRouteNodes(this.config, this.root);
150
+ } else {
151
+ getRouteNodesResult = await getRouteNodes$1(this.config, this.root);
152
+ }
153
+ const { rootRouteNode, routeNodes: beforeRouteNodes } = getRouteNodesResult;
154
+ if (rootRouteNode === void 0) {
155
+ let errorMessage = `rootRouteNode must not be undefined. Make sure you've added your root route into the route-tree.`;
156
+ if (!this.config.virtualRouteConfig) {
157
+ errorMessage += `
158
+ Make sure that you add a "${rootPathId}.${this.config.disableTypes ? "js" : "tsx"}" file to your routes directory.
159
+ Add the file in: "${this.config.routesDirectory}/${rootPathId}.${this.config.disableTypes ? "js" : "tsx"}"`;
160
+ }
161
+ throw new Error(errorMessage);
162
+ }
163
+ writeRouteTreeFile = await this.handleRootNode(rootRouteNode);
164
+ const preRouteNodes = multiSortBy(beforeRouteNodes, [
165
+ (d) => d.routePath === "/" ? -1 : 1,
166
+ (d) => {
167
+ var _a;
168
+ return (_a = d.routePath) == null ? void 0 : _a.split("/").length;
169
+ },
170
+ (d) => d.filePath.match(new RegExp(`[./]${this.config.indexToken}[.]`)) ? 1 : -1,
171
+ (d) => d.filePath.match(
172
+ /[./](component|errorComponent|pendingComponent|loader|lazy)[.]/
173
+ ) ? 1 : -1,
174
+ (d) => d.filePath.match(new RegExp(`[./]${this.config.routeToken}[.]`)) ? -1 : 1,
175
+ (d) => {
176
+ var _a;
177
+ return ((_a = d.routePath) == null ? void 0 : _a.endsWith("/")) ? -1 : 1;
178
+ },
179
+ (d) => d.routePath
180
+ ]).filter((d) => ![`/${rootPathId}`].includes(d.routePath || ""));
181
+ const routeFileAllResult = await Promise.allSettled(
182
+ preRouteNodes.filter((n) => !n.isVirtualParentRoute && !n.isVirtual).map((n) => this.processRouteNodeFile(n))
183
+ );
184
+ const rejections = routeFileAllResult.filter(
185
+ (result) => result.status === "rejected"
186
+ );
187
+ if (rejections.length > 0) {
188
+ throw rejections.map((e) => e.reason);
189
+ }
190
+ const routeFileResult = routeFileAllResult.flatMap((result) => {
191
+ if (result.status === "fulfilled" && result.value !== null) {
192
+ return result.value;
193
+ }
194
+ return [];
195
+ });
196
+ routeFileResult.forEach((result) => {
66
197
  var _a;
67
- return ((_a = d.routePath) == null ? void 0 : _a.endsWith("/")) ? -1 : 1;
68
- },
69
- (d) => d.routePath
70
- ]).filter((d) => ![`/${rootPathId}`].includes(d.routePath || ""));
71
- const routeTree = [];
72
- const routePiecesByPath = {};
73
- const routeNodes = [];
74
- const handleRootNode = async (node) => {
75
- if (!node) {
198
+ if (!((_a = result.node.exports) == null ? void 0 : _a.length)) {
199
+ this.logger.warn(
200
+ `Route file "${result.cacheEntry.fileContent}" does not export any route piece. This is likely a mistake.`
201
+ );
202
+ }
203
+ });
204
+ if (routeFileResult.find((r) => r.shouldWriteTree)) {
205
+ writeRouteTreeFile = true;
206
+ }
207
+ if (!this.routeTreeFileCache) {
208
+ const routeTreeFile = await this.fs.readFile(this.generatedRouteTreePath);
209
+ if (routeTreeFile !== "file-not-existing") {
210
+ this.routeTreeFileCache = {
211
+ fileContent: routeTreeFile.fileContent,
212
+ mtimeMs: routeTreeFile.stat.mtimeMs
213
+ };
214
+ }
215
+ writeRouteTreeFile = true;
216
+ } else {
217
+ const routeTreeFileChange = await this.didFileChangeComparedToCache(
218
+ { path: this.generatedRouteTreePath },
219
+ this.routeTreeFileCache
220
+ );
221
+ if (routeTreeFileChange.result !== false) {
222
+ writeRouteTreeFile = "force";
223
+ if (routeTreeFileChange.result === true) {
224
+ const routeTreeFile = await this.fs.readFile(
225
+ this.generatedRouteTreePath
226
+ );
227
+ if (routeTreeFile !== "file-not-existing") {
228
+ this.routeTreeFileCache = {
229
+ fileContent: routeTreeFile.fileContent,
230
+ mtimeMs: routeTreeFile.stat.mtimeMs
231
+ };
232
+ }
233
+ }
234
+ }
235
+ }
236
+ if (!writeRouteTreeFile) {
237
+ for (const fullPath of this.routeNodeCache.keys()) {
238
+ if (!this.routeNodeShadowCache.has(fullPath)) {
239
+ writeRouteTreeFile = true;
240
+ break;
241
+ }
242
+ }
243
+ }
244
+ if (!writeRouteTreeFile) {
76
245
  return;
77
246
  }
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}`);
247
+ let routeTreeContent = this.buildRouteTreeFileContent(
248
+ rootRouteNode,
249
+ preRouteNodes,
250
+ routeFileResult
251
+ );
252
+ routeTreeContent = this.config.enableRouteTreeFormatting ? await format(routeTreeContent, this.config) : routeTreeContent;
253
+ let newMtimeMs;
254
+ if (this.routeTreeFileCache) {
255
+ if (writeRouteTreeFile !== "force" && this.routeTreeFileCache.fileContent === routeTreeContent) ;
256
+ else {
257
+ const newRouteTreeFileStat = await this.safeFileWrite({
258
+ filePath: this.generatedRouteTreePath,
259
+ newContent: routeTreeContent,
260
+ strategy: {
261
+ type: "mtime",
262
+ expectedMtimeMs: this.routeTreeFileCache.mtimeMs
95
263
  }
264
+ });
265
+ newMtimeMs = newRouteTreeFileStat.mtimeMs;
266
+ }
267
+ } else {
268
+ const newRouteTreeFileStat = await this.safeFileWrite({
269
+ filePath: this.generatedRouteTreePath,
270
+ newContent: routeTreeContent,
271
+ strategy: {
272
+ type: "new-file"
273
+ }
274
+ });
275
+ newMtimeMs = newRouteTreeFileStat.mtimeMs;
276
+ }
277
+ if (newMtimeMs !== void 0) {
278
+ this.routeTreeFileCache = {
279
+ fileContent: routeTreeContent,
280
+ mtimeMs: newMtimeMs
281
+ };
282
+ }
283
+ this.routeNodeCache = this.routeNodeShadowCache;
284
+ this.routeNodeShadowCache = /* @__PURE__ */ new Map();
285
+ }
286
+ buildRouteTreeFileContent(rootRouteNode, preRouteNodes, routeFileResult) {
287
+ const getImportForRouteNode = (node, exportName) => {
288
+ var _a;
289
+ if ((_a = node.exports) == null ? void 0 : _a.includes(exportName)) {
290
+ return {
291
+ source: `./${this.getImportPath(node)}`,
292
+ specifiers: [
293
+ {
294
+ imported: exportName,
295
+ local: `${node.variableName}${exportName}Import`
296
+ }
297
+ ]
298
+ };
299
+ }
300
+ return void 0;
301
+ };
302
+ const buildRouteTreeForExport = (plugin) => {
303
+ var _a, _b, _c;
304
+ const exportName = plugin.transformPlugin.exportName;
305
+ const acc = {
306
+ routeTree: [],
307
+ routeNodes: [],
308
+ routePiecesByPath: {}
309
+ };
310
+ for (const node of preRouteNodes) {
311
+ if ((_a = node.exports) == null ? void 0 : _a.includes(plugin.transformPlugin.exportName)) {
312
+ this.handleNode(node, acc);
96
313
  }
314
+ }
315
+ const sortedRouteNodes = multiSortBy(acc.routeNodes, [
316
+ (d) => {
317
+ var _a2;
318
+ return ((_a2 = d.routePath) == null ? void 0 : _a2.includes(`/${rootPathId}`)) ? -1 : 1;
319
+ },
320
+ (d) => {
321
+ var _a2;
322
+ return (_a2 = d.routePath) == null ? void 0 : _a2.split("/").length;
323
+ },
324
+ (d) => {
325
+ var _a2;
326
+ return ((_a2 = d.routePath) == null ? void 0 : _a2.endsWith(this.config.indexToken)) ? -1 : 1;
327
+ },
328
+ (d) => d
329
+ ]);
330
+ const pluginConfig = plugin.config({
331
+ generator: this,
332
+ rootRouteNode,
333
+ sortedRouteNodes
334
+ });
335
+ const routeImports2 = sortedRouteNodes.filter((d) => !d.isVirtual).flatMap((node) => getImportForRouteNode(node, exportName) ?? []);
336
+ const hasMatchingRouteFiles = acc.routeNodes.length > 0 || ((_b = rootRouteNode.exports) == null ? void 0 : _b.includes(exportName));
337
+ const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual).map((node) => {
338
+ return `const ${node.variableName}${exportName}Import = ${plugin.createVirtualRouteCode({ node })}`;
339
+ });
340
+ if (!((_c = rootRouteNode.exports) == null ? void 0 : _c.includes(exportName)) && pluginConfig.virtualRootRoute) {
341
+ virtualRouteNodes.unshift(
342
+ `const ${rootRouteNode.variableName}${exportName}Import = ${plugin.createRootRouteCode()}`
343
+ );
344
+ }
345
+ const imports = plugin.imports({
346
+ sortedRouteNodes,
347
+ acc,
348
+ generator: this,
349
+ rootRouteNode
350
+ });
351
+ const routeTreeConfig = buildRouteTreeConfig(
352
+ acc.routeTree,
353
+ exportName,
354
+ this.config.disableTypes
97
355
  );
356
+ const createUpdateRoutes = sortedRouteNodes.map((node) => {
357
+ var _a2, _b2, _c2, _d, _e;
358
+ const loaderNode = (_a2 = acc.routePiecesByPath[node.routePath]) == null ? void 0 : _a2.loader;
359
+ const componentNode = (_b2 = acc.routePiecesByPath[node.routePath]) == null ? void 0 : _b2.component;
360
+ const errorComponentNode = (_c2 = acc.routePiecesByPath[node.routePath]) == null ? void 0 : _c2.errorComponent;
361
+ const pendingComponentNode = (_d = acc.routePiecesByPath[node.routePath]) == null ? void 0 : _d.pendingComponent;
362
+ const lazyComponentNode = (_e = acc.routePiecesByPath[node.routePath]) == null ? void 0 : _e.lazy;
363
+ return [
364
+ [
365
+ `const ${node.variableName}${exportName} = ${node.variableName}${exportName}Import.update({
366
+ ${[
367
+ `id: '${node.path}'`,
368
+ !node.isNonPath ? `path: '${node.cleanedPath}'` : void 0,
369
+ `getParentRoute: () => ${findParent(node, exportName)}`
370
+ ].filter(Boolean).join(",")}
371
+ }${this.config.disableTypes ? "" : "as any"})`,
372
+ loaderNode ? `.updateLoader({ loader: lazyFn(() => import('./${replaceBackslash(
373
+ removeExt(
374
+ path.relative(
375
+ path.dirname(this.config.generatedRouteTree),
376
+ path.resolve(
377
+ this.config.routesDirectory,
378
+ loaderNode.filePath
379
+ )
380
+ ),
381
+ this.config.addExtensions
382
+ )
383
+ )}'), 'loader') })` : "",
384
+ componentNode || errorComponentNode || pendingComponentNode ? `.update({
385
+ ${[
386
+ ["component", componentNode],
387
+ ["errorComponent", errorComponentNode],
388
+ ["pendingComponent", pendingComponentNode]
389
+ ].filter((d) => d[1]).map((d) => {
390
+ return `${d[0]}: lazyRouteComponent(() => import('./${replaceBackslash(
391
+ removeExt(
392
+ path.relative(
393
+ path.dirname(this.config.generatedRouteTree),
394
+ path.resolve(
395
+ this.config.routesDirectory,
396
+ d[1].filePath
397
+ )
398
+ ),
399
+ this.config.addExtensions
400
+ )
401
+ )}'), '${d[0]}')`;
402
+ }).join("\n,")}
403
+ })` : "",
404
+ lazyComponentNode ? `.lazy(() => import('./${replaceBackslash(
405
+ removeExt(
406
+ path.relative(
407
+ path.dirname(this.config.generatedRouteTree),
408
+ path.resolve(
409
+ this.config.routesDirectory,
410
+ lazyComponentNode.filePath
411
+ )
412
+ ),
413
+ this.config.addExtensions
414
+ )
415
+ )}').then((d) => d.${exportName}))` : ""
416
+ ].join("")
417
+ ].join("\n\n");
418
+ });
419
+ let fileRoutesByPathInterfacePerPlugin = "";
420
+ let fileRoutesByFullPathPerPlugin = "";
421
+ if (!this.config.disableTypes && hasMatchingRouteFiles) {
422
+ fileRoutesByFullPathPerPlugin = [
423
+ `export interface File${exportName}sByFullPath {
424
+ ${[...createRouteNodesByFullPath(acc.routeNodes).entries()].map(
425
+ ([fullPath, routeNode]) => {
426
+ return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`;
427
+ }
428
+ )}
429
+ }`,
430
+ `export interface File${exportName}sByTo {
431
+ ${[...createRouteNodesByTo(acc.routeNodes).entries()].map(([to, routeNode]) => {
432
+ return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`;
433
+ })}
434
+ }`,
435
+ `export interface File${exportName}sById {
436
+ '${rootRouteId}': typeof root${exportName}Import,
437
+ ${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
438
+ return `'${id}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`;
439
+ })}
440
+ }`,
441
+ `export interface File${exportName}Types {
442
+ file${exportName}sByFullPath: File${exportName}sByFullPath
443
+ fullPaths: ${acc.routeNodes.length > 0 ? [...createRouteNodesByFullPath(acc.routeNodes).keys()].map((fullPath) => `'${fullPath}'`).join("|") : "never"}
444
+ file${exportName}sByTo: File${exportName}sByTo
445
+ to: ${acc.routeNodes.length > 0 ? [...createRouteNodesByTo(acc.routeNodes).keys()].map((to) => `'${to}'`).join("|") : "never"}
446
+ id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(acc.routeNodes).keys()].map((id) => `'${id}'`)].join("|")}
447
+ file${exportName}sById: File${exportName}sById
448
+ }`,
449
+ `export interface Root${exportName}Children {
450
+ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${getResolvedRouteNodeVariableName(child, exportName)}`).join(",")}
451
+ }`
452
+ ].join("\n");
453
+ fileRoutesByPathInterfacePerPlugin = buildFileRoutesByPathInterface({
454
+ ...plugin.moduleAugmentation({ generator: this }),
455
+ routeNodes: this.config.verboseFileRoutes !== false ? sortedRouteNodes : [
456
+ ...routeFileResult.map(({ node }) => node),
457
+ ...sortedRouteNodes.filter((d) => d.isVirtual)
458
+ ],
459
+ exportName
460
+ });
461
+ }
462
+ let routeTree = "";
463
+ if (hasMatchingRouteFiles) {
464
+ routeTree = [
465
+ `const root${exportName}Children${this.config.disableTypes ? "" : `: Root${exportName}Children`} = {
466
+ ${acc.routeTree.map(
467
+ (child) => `${child.variableName}${exportName}: ${getResolvedRouteNodeVariableName(child, exportName)}`
468
+ ).join(",")}
469
+ }`,
470
+ `export const ${lowerCaseFirstChar(exportName)}Tree = root${exportName}Import._addFileChildren(root${exportName}Children)${this.config.disableTypes ? "" : `._addFileTypes<File${exportName}Types>()`}`
471
+ ].join("\n");
472
+ }
473
+ return {
474
+ routeImports: routeImports2,
475
+ sortedRouteNodes,
476
+ acc,
477
+ virtualRouteNodes,
478
+ routeTreeConfig,
479
+ routeTree,
480
+ imports,
481
+ createUpdateRoutes,
482
+ fileRoutesByFullPathPerPlugin,
483
+ fileRoutesByPathInterfacePerPlugin
484
+ };
485
+ };
486
+ const routeTrees = this.pluginsWithTransform.map((plugin) => ({
487
+ exportName: plugin.transformPlugin.exportName,
488
+ ...buildRouteTreeForExport(plugin)
489
+ }));
490
+ this.plugins.map((plugin) => {
491
+ var _a;
492
+ return (_a = plugin.onRouteTreesChanged) == null ? void 0 : _a.call(plugin, {
493
+ routeTrees,
494
+ rootRouteNode,
495
+ generator: this
496
+ });
497
+ });
498
+ let mergedImports = mergeImportDeclarations(
499
+ routeTrees.flatMap((d) => d.imports)
500
+ );
501
+ if (this.config.disableTypes) {
502
+ mergedImports = mergedImports.filter((d) => d.importKind !== "type");
503
+ }
504
+ const importStatements = mergedImports.map(buildImportString);
505
+ let moduleAugmentation = "";
506
+ if (this.config.verboseFileRoutes === false && !this.config.disableTypes) {
507
+ moduleAugmentation = routeFileResult.map(({ node }) => {
508
+ const getModuleDeclaration = (routeNode) => {
509
+ if (!isRouteNodeValidForAugmentation(routeNode)) {
510
+ return "";
511
+ }
512
+ const moduleAugmentation2 = this.pluginsWithTransform.map((plugin) => {
513
+ return plugin.routeModuleAugmentation({
514
+ routeNode
515
+ });
516
+ }).filter(Boolean).join("\n");
517
+ return `declare module './${this.getImportPath(routeNode)}' {
518
+ ${moduleAugmentation2}
519
+ }`;
520
+ };
521
+ return getModuleDeclaration(node);
522
+ }).join("\n");
98
523
  }
99
- };
100
- await handleRootNode(rootRouteNode);
101
- const handleNode = async (node) => {
524
+ const routeImports = routeTrees.flatMap((t) => t.routeImports);
525
+ const rootRouteImports = this.pluginsWithTransform.flatMap(
526
+ (p) => getImportForRouteNode(rootRouteNode, p.transformPlugin.exportName) ?? []
527
+ );
528
+ if (rootRouteImports.length > 0) {
529
+ routeImports.unshift(...rootRouteImports);
530
+ }
531
+ const routeTreeContent = [
532
+ ...this.config.routeTreeFileHeader,
533
+ `// This file was automatically generated by TanStack Router.
534
+ // You should NOT make any changes in this file as it will be overwritten.
535
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.`,
536
+ [...importStatements].join("\n"),
537
+ mergeImportDeclarations(routeImports).map(buildImportString).join("\n"),
538
+ routeTrees.flatMap((t) => t.virtualRouteNodes).join("\n"),
539
+ routeTrees.flatMap((t) => t.createUpdateRoutes).join("\n"),
540
+ routeTrees.map((t) => t.fileRoutesByFullPathPerPlugin).join("\n"),
541
+ routeTrees.map((t) => t.fileRoutesByPathInterfacePerPlugin).join("\n"),
542
+ moduleAugmentation,
543
+ routeTrees.flatMap((t) => t.routeTreeConfig).join("\n"),
544
+ routeTrees.map((t) => t.routeTree).join("\n"),
545
+ ...this.config.routeTreeFileFooter
546
+ ].filter(Boolean).join("\n\n");
547
+ return routeTreeContent;
548
+ }
549
+ getImportPath(node) {
550
+ return replaceBackslash(
551
+ removeExt(
552
+ path.relative(
553
+ path.dirname(this.config.generatedRouteTree),
554
+ path.resolve(this.config.routesDirectory, node.filePath)
555
+ ),
556
+ this.config.addExtensions
557
+ )
558
+ );
559
+ }
560
+ async processRouteNodeFile(node) {
102
561
  var _a, _b, _c, _d, _e;
103
- resetRegex(routeGroupPatternRegex);
104
- let parentRoute = hasParentRoute(routeNodes, node, node.routePath);
562
+ const result = await this.isRouteFileCacheFresh(node);
563
+ if (result.status === "fresh") {
564
+ node.exports = result.cacheEntry.exports;
565
+ return {
566
+ node,
567
+ shouldWriteTree: result.exportsChanged,
568
+ cacheEntry: result.cacheEntry
569
+ };
570
+ }
571
+ const existingRouteFile = await this.fs.readFile(node.fullPath);
572
+ if (existingRouteFile === "file-not-existing") {
573
+ throw new Error(`⚠️ File ${node.fullPath} does not exist`);
574
+ }
575
+ const updatedCacheEntry = {
576
+ fileContent: existingRouteFile.fileContent,
577
+ mtimeMs: existingRouteFile.stat.mtimeMs,
578
+ exports: []
579
+ };
580
+ const escapedRoutePath = ((_a = node.routePath) == null ? void 0 : _a.replaceAll("$", "$$")) ?? "";
581
+ let shouldWriteRouteFile = false;
582
+ if (!existingRouteFile.fileContent) {
583
+ shouldWriteRouteFile = true;
584
+ if (node._fsRouteType === "lazy") {
585
+ const tLazyRouteTemplate = this.targetTemplate.lazyRoute;
586
+ updatedCacheEntry.fileContent = await fillTemplate(
587
+ this.config,
588
+ (((_b = this.config.customScaffolding) == null ? void 0 : _b.lazyRouteTemplate) || ((_c = this.config.customScaffolding) == null ? void 0 : _c.routeTemplate)) ?? tLazyRouteTemplate.template(),
589
+ {
590
+ tsrImports: tLazyRouteTemplate.imports.tsrImports(),
591
+ tsrPath: escapedRoutePath.replaceAll(/\{(.+)\}/gm, "$1"),
592
+ tsrExportStart: tLazyRouteTemplate.imports.tsrExportStart(escapedRoutePath),
593
+ tsrExportEnd: tLazyRouteTemplate.imports.tsrExportEnd()
594
+ }
595
+ );
596
+ updatedCacheEntry.exports = ["Route"];
597
+ } else if (
598
+ // Creating a new normal route file
599
+ ["layout", "static"].some(
600
+ (d) => d === node._fsRouteType
601
+ ) || [
602
+ "component",
603
+ "pendingComponent",
604
+ "errorComponent",
605
+ "loader"
606
+ ].every((d) => d !== node._fsRouteType)
607
+ ) {
608
+ const tRouteTemplate = this.targetTemplate.route;
609
+ updatedCacheEntry.fileContent = await fillTemplate(
610
+ this.config,
611
+ ((_d = this.config.customScaffolding) == null ? void 0 : _d.routeTemplate) ?? tRouteTemplate.template(),
612
+ {
613
+ tsrImports: tRouteTemplate.imports.tsrImports(),
614
+ tsrPath: escapedRoutePath.replaceAll(/\{(.+)\}/gm, "$1"),
615
+ tsrExportStart: tRouteTemplate.imports.tsrExportStart(escapedRoutePath),
616
+ tsrExportEnd: tRouteTemplate.imports.tsrExportEnd()
617
+ }
618
+ );
619
+ updatedCacheEntry.exports = ["Route"];
620
+ } else {
621
+ return null;
622
+ }
623
+ } else {
624
+ const transformResult = await transform({
625
+ source: updatedCacheEntry.fileContent,
626
+ ctx: {
627
+ target: this.config.target,
628
+ routeId: escapedRoutePath,
629
+ lazy: node._fsRouteType === "lazy",
630
+ verboseFileRoutes: !(this.config.verboseFileRoutes === false)
631
+ },
632
+ plugins: this.transformPlugins
633
+ });
634
+ if (transformResult.result === "error") {
635
+ throw new Error(
636
+ `Error transforming route file ${node.fullPath}: ${transformResult.error}`
637
+ );
638
+ }
639
+ updatedCacheEntry.exports = transformResult.exports;
640
+ if (transformResult.result === "modified") {
641
+ updatedCacheEntry.fileContent = transformResult.output;
642
+ shouldWriteRouteFile = true;
643
+ }
644
+ }
645
+ if (shouldWriteRouteFile) {
646
+ const stats = await this.safeFileWrite({
647
+ filePath: node.fullPath,
648
+ newContent: updatedCacheEntry.fileContent,
649
+ strategy: {
650
+ type: "mtime",
651
+ expectedMtimeMs: updatedCacheEntry.mtimeMs
652
+ }
653
+ });
654
+ updatedCacheEntry.mtimeMs = stats.mtimeMs;
655
+ }
656
+ this.routeNodeShadowCache.set(node.fullPath, updatedCacheEntry);
657
+ node.exports = updatedCacheEntry.exports;
658
+ const shouldWriteTree = !deepEqual(
659
+ (_e = result.cacheEntry) == null ? void 0 : _e.exports,
660
+ updatedCacheEntry.exports
661
+ );
662
+ return {
663
+ node,
664
+ shouldWriteTree,
665
+ cacheEntry: updatedCacheEntry
666
+ };
667
+ }
668
+ async didRouteFileChangeComparedToCache(file, cache) {
669
+ const cacheEntry = this[cache].get(file.path);
670
+ return this.didFileChangeComparedToCache(file, cacheEntry);
671
+ }
672
+ async didFileChangeComparedToCache(file, cacheEntry) {
673
+ if (!cacheEntry) {
674
+ return { result: "file-not-in-cache" };
675
+ }
676
+ let mtimeMs = file.mtimeMs;
677
+ if (mtimeMs === void 0) {
678
+ try {
679
+ const currentStat = await this.fs.stat(file.path);
680
+ mtimeMs = currentStat.mtimeMs;
681
+ } catch {
682
+ return { result: "cannot-stat-file" };
683
+ }
684
+ }
685
+ return { result: mtimeMs !== cacheEntry.mtimeMs, mtimeMs, cacheEntry };
686
+ }
687
+ async safeFileWrite(opts) {
688
+ const tmpPath = this.getTempFileName(opts.filePath);
689
+ await this.fs.writeFile(tmpPath, opts.newContent);
690
+ if (opts.strategy.type === "mtime") {
691
+ const beforeStat = await this.fs.stat(opts.filePath);
692
+ if (beforeStat.mtimeMs !== opts.strategy.expectedMtimeMs) {
693
+ throw rerun({
694
+ msg: `File ${opts.filePath} was modified by another process during processing.`,
695
+ event: { type: "update", path: opts.filePath }
696
+ });
697
+ }
698
+ } else {
699
+ if (await checkFileExists(opts.filePath)) {
700
+ throw rerun({
701
+ msg: `File ${opts.filePath} already exists. Cannot overwrite.`,
702
+ event: { type: "update", path: opts.filePath }
703
+ });
704
+ }
705
+ }
706
+ const stat = await this.fs.stat(tmpPath);
707
+ await this.fs.rename(tmpPath, opts.filePath);
708
+ return stat;
709
+ }
710
+ getTempFileName(filePath) {
711
+ const absPath = path.resolve(filePath);
712
+ const hash = crypto.createHash("md5").update(absPath).digest("hex");
713
+ return path.join(this.tmpDir, hash);
714
+ }
715
+ async isRouteFileCacheFresh(node) {
716
+ const fileChangedCache = await this.didRouteFileChangeComparedToCache(
717
+ { path: node.fullPath },
718
+ "routeNodeCache"
719
+ );
720
+ if (fileChangedCache.result === false) {
721
+ this.routeNodeShadowCache.set(node.fullPath, fileChangedCache.cacheEntry);
722
+ return {
723
+ status: "fresh",
724
+ exportsChanged: false,
725
+ cacheEntry: fileChangedCache.cacheEntry
726
+ };
727
+ }
728
+ if (fileChangedCache.result === "cannot-stat-file") {
729
+ throw new Error(`⚠️ expected route file to exist at ${node.fullPath}`);
730
+ }
731
+ const mtimeMs = fileChangedCache.result === true ? fileChangedCache.mtimeMs : void 0;
732
+ const shadowCacheFileChange = await this.didRouteFileChangeComparedToCache(
733
+ { path: node.fullPath, mtimeMs },
734
+ "routeNodeShadowCache"
735
+ );
736
+ if (shadowCacheFileChange.result === "cannot-stat-file") {
737
+ throw new Error(`⚠️ expected route file to exist at ${node.fullPath}`);
738
+ }
739
+ if (shadowCacheFileChange.result === false) {
740
+ if (fileChangedCache.result === true) {
741
+ if (deepEqual(
742
+ fileChangedCache.cacheEntry.exports,
743
+ shadowCacheFileChange.cacheEntry.exports
744
+ )) {
745
+ return {
746
+ status: "fresh",
747
+ exportsChanged: false,
748
+ cacheEntry: shadowCacheFileChange.cacheEntry
749
+ };
750
+ }
751
+ return {
752
+ status: "fresh",
753
+ exportsChanged: true,
754
+ cacheEntry: shadowCacheFileChange.cacheEntry
755
+ };
756
+ }
757
+ }
758
+ if (fileChangedCache.result === "file-not-in-cache") {
759
+ return {
760
+ status: "stale"
761
+ };
762
+ }
763
+ return { status: "stale", cacheEntry: fileChangedCache.cacheEntry };
764
+ }
765
+ async handleRootNode(node) {
766
+ var _a;
767
+ const result = await this.isRouteFileCacheFresh(node);
768
+ if (result.status === "fresh") {
769
+ node.exports = result.cacheEntry.exports;
770
+ this.routeNodeShadowCache.set(node.fullPath, result.cacheEntry);
771
+ return result.exportsChanged;
772
+ }
773
+ const rootNodeFile = await this.fs.readFile(node.fullPath);
774
+ if (rootNodeFile === "file-not-existing") {
775
+ throw new Error(`⚠️ expected root route to exist at ${node.fullPath}`);
776
+ }
777
+ const updatedCacheEntry = {
778
+ fileContent: rootNodeFile.fileContent,
779
+ mtimeMs: rootNodeFile.stat.mtimeMs,
780
+ exports: []
781
+ };
782
+ if (!rootNodeFile.fileContent) {
783
+ const rootTemplate = this.targetTemplate.rootRoute;
784
+ const rootRouteContent = await fillTemplate(
785
+ this.config,
786
+ rootTemplate.template(),
787
+ {
788
+ tsrImports: rootTemplate.imports.tsrImports(),
789
+ tsrPath: rootPathId,
790
+ tsrExportStart: rootTemplate.imports.tsrExportStart(),
791
+ tsrExportEnd: rootTemplate.imports.tsrExportEnd()
792
+ }
793
+ );
794
+ this.logger.log(`🟡 Creating ${node.fullPath}`);
795
+ const stats = await this.safeFileWrite({
796
+ filePath: node.fullPath,
797
+ newContent: rootRouteContent,
798
+ strategy: {
799
+ type: "mtime",
800
+ expectedMtimeMs: rootNodeFile.stat.mtimeMs
801
+ }
802
+ });
803
+ updatedCacheEntry.fileContent = rootRouteContent;
804
+ updatedCacheEntry.mtimeMs = stats.mtimeMs;
805
+ }
806
+ const rootRouteExports = [];
807
+ for (const plugin of this.pluginsWithTransform) {
808
+ const exportName = plugin.transformPlugin.exportName;
809
+ if (rootNodeFile.fileContent.includes(`export const ${exportName}`)) {
810
+ rootRouteExports.push(exportName);
811
+ }
812
+ }
813
+ updatedCacheEntry.exports = rootRouteExports;
814
+ node.exports = rootRouteExports;
815
+ this.routeNodeShadowCache.set(node.fullPath, updatedCacheEntry);
816
+ const shouldWriteTree = !deepEqual(
817
+ (_a = result.cacheEntry) == null ? void 0 : _a.exports,
818
+ rootRouteExports
819
+ );
820
+ return shouldWriteTree;
821
+ }
822
+ handleNode(node, acc) {
823
+ var _a;
824
+ resetRegex(this.routeGroupPatternRegex);
825
+ let parentRoute = hasParentRoute(acc.routeNodes, node, node.routePath);
105
826
  if ((parentRoute == null ? void 0 : parentRoute.isVirtualParentRoute) && ((_a = parentRoute.children) == null ? void 0 : _a.length)) {
106
827
  const possibleParentRoute = hasParentRoute(
107
828
  parentRoute.children,
@@ -117,131 +838,10 @@ Add the file in: "${config.routesDirectory}/${rootPathId}.${config.disableTypes
117
838
  const trimmedPath = trimPathLeft(node.path ?? "");
118
839
  const split = trimmedPath.split("/");
119
840
  const lastRouteSegment = split[split.length - 1] ?? trimmedPath;
120
- node.isNonPath = lastRouteSegment.startsWith("_") || routeGroupPatternRegex.test(lastRouteSegment);
841
+ node.isNonPath = lastRouteSegment.startsWith("_") || this.routeGroupPatternRegex.test(lastRouteSegment);
121
842
  node.cleanedPath = removeGroups(
122
843
  removeUnderscores(removeLayoutSegments(node.path)) ?? ""
123
844
  );
124
- if (!node.isVirtualParentRoute && !node.isVirtual) {
125
- const routeCode = fs.readFileSync(node.fullPath, "utf-8");
126
- const escapedRoutePath = ((_b = node.routePath) == null ? void 0 : _b.replaceAll("$", "$$")) ?? "";
127
- let replaced = routeCode;
128
- const tRouteTemplate = ROUTE_TEMPLATE.route;
129
- const tLazyRouteTemplate = ROUTE_TEMPLATE.lazyRoute;
130
- if (!routeCode) {
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;
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
- );
208
- } else {
209
- if (!routeCode.split("\n").some((line) => line.trim().startsWith("export const Route"))) {
210
- return;
211
- }
212
- replaced = routeCode.replace(
213
- /(FileRoute\(\s*['"])([^\s]*)(['"],?\s*\))/g,
214
- (_, p1, __, p3) => `${p1}${escapedRoutePath}${p3}`
215
- ).replace(
216
- /((FileRoute)(\s*)(\({))/g,
217
- (_, __, p2, p3, p4) => `${p2}('${escapedRoutePath}')${p3}${p4}`
218
- ).replace(
219
- new RegExp(
220
- `(import\\s*\\{.*)(create(Lazy)?FileRoute)(.*\\}\\s*from\\s*['"]@tanstack\\/${ROUTE_TEMPLATE.subPkg}['"])`,
221
- "gs"
222
- ),
223
- (_, p1, __, ___, p4) => `${p1}${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}${p4}`
224
- ).replace(
225
- /create(Lazy)?FileRoute(\(\s*['"])([^\s]*)(['"],?\s*\))/g,
226
- (_, __, p2, ___, p4) => `${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}${p2}${escapedRoutePath}${p4}`
227
- );
228
- const regex = new RegExp(
229
- `(import\\s*\\{.*)(create(Lazy)?FileRoute)(.*\\}\\s*from\\s*['"]@tanstack\\/${ROUTE_TEMPLATE.subPkg}['"])`,
230
- "gm"
231
- );
232
- if (!replaced.match(regex)) {
233
- replaced = [
234
- `import { ${node._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"} } from '@tanstack/${ROUTE_TEMPLATE.subPkg}'`,
235
- ...replaced.split("\n")
236
- ].join("\n");
237
- }
238
- }
239
- await writeIfDifferent(node.fullPath, routeCode, replaced, {
240
- beforeWrite: () => {
241
- logger.log(`🟡 Updating ${node.fullPath}`);
242
- }
243
- });
244
- }
245
845
  if (!node.isVirtual && [
246
846
  "lazy",
247
847
  "loader",
@@ -249,15 +849,20 @@ Add the file in: "${config.routesDirectory}/${rootPathId}.${config.disableTypes
249
849
  "pendingComponent",
250
850
  "errorComponent"
251
851
  ].some((d) => d === node._fsRouteType)) {
252
- routePiecesByPath[node.routePath] = routePiecesByPath[node.routePath] || {};
253
- routePiecesByPath[node.routePath][node._fsRouteType === "lazy" ? "lazy" : node._fsRouteType === "loader" ? "loader" : node._fsRouteType === "errorComponent" ? "errorComponent" : node._fsRouteType === "pendingComponent" ? "pendingComponent" : "component"] = node;
254
- const anchorRoute = routeNodes.find((d) => d.routePath === node.routePath);
852
+ acc.routePiecesByPath[node.routePath] = acc.routePiecesByPath[node.routePath] || {};
853
+ acc.routePiecesByPath[node.routePath][node._fsRouteType === "lazy" ? "lazy" : node._fsRouteType === "loader" ? "loader" : node._fsRouteType === "errorComponent" ? "errorComponent" : node._fsRouteType === "pendingComponent" ? "pendingComponent" : "component"] = node;
854
+ const anchorRoute = acc.routeNodes.find(
855
+ (d) => d.routePath === node.routePath
856
+ );
255
857
  if (!anchorRoute) {
256
- await handleNode({
257
- ...node,
258
- isVirtual: true,
259
- _fsRouteType: "static"
260
- });
858
+ this.handleNode(
859
+ {
860
+ ...node,
861
+ isVirtual: true,
862
+ _fsRouteType: "static"
863
+ },
864
+ acc
865
+ );
261
866
  }
262
867
  return;
263
868
  }
@@ -267,7 +872,7 @@ Add the file in: "${config.routesDirectory}/${rootPathId}.${config.disableTypes
267
872
  if (!node.isVirtual && node.isVirtualParentRequired) {
268
873
  const parentRoutePath = removeLastSegmentFromPath(node.routePath) || "/";
269
874
  const parentVariableName = routePathToVariable(parentRoutePath);
270
- const anchorRoute = routeNodes.find(
875
+ const anchorRoute = acc.routeNodes.find(
271
876
  (d) => d.routePath === parentRoutePath
272
877
  );
273
878
  if (!anchorRoute) {
@@ -290,7 +895,7 @@ Add the file in: "${config.routesDirectory}/${rootPathId}.${config.disableTypes
290
895
  if (node._fsRouteType === "pathless_layout") {
291
896
  node.path = determineNodePath(node);
292
897
  }
293
- await handleNode(parentNode);
898
+ this.handleNode(parentNode, acc);
294
899
  } else {
295
900
  anchorRoute.children = anchorRoute.children ?? [];
296
901
  anchorRoute.children.push(node);
@@ -303,448 +908,12 @@ Add the file in: "${config.routesDirectory}/${rootPathId}.${config.disableTypes
303
908
  node.parent.children.push(node);
304
909
  }
305
910
  } else {
306
- routeTree.push(node);
307
- }
308
- routeNodes.push(node);
309
- };
310
- for (const node of preRouteNodes) {
311
- await handleNode(node);
312
- }
313
- checkRouteFullPathUniqueness(
314
- preRouteNodes.filter(
315
- (d) => d.children === void 0 && "lazy" !== d._fsRouteType
316
- ),
317
- config
318
- );
319
- function buildRouteTreeConfig(nodes, depth = 1) {
320
- const children = nodes.map((node) => {
321
- var _a, _b;
322
- if (node._fsRouteType === "__root") {
323
- return;
324
- }
325
- if (node._fsRouteType === "pathless_layout" && !((_a = node.children) == null ? void 0 : _a.length)) {
326
- return;
327
- }
328
- const route = `${node.variableName}Route`;
329
- if ((_b = node.children) == null ? void 0 : _b.length) {
330
- const childConfigs = buildRouteTreeConfig(node.children, depth + 1);
331
- const childrenDeclaration = TYPES_DISABLED ? "" : `interface ${route}Children {
332
- ${node.children.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(",")}
333
- }`;
334
- const children2 = `const ${route}Children${TYPES_DISABLED ? "" : `: ${route}Children`} = {
335
- ${node.children.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(",")}
336
- }`;
337
- const routeWithChildren = `const ${route}WithChildren = ${route}._addFileChildren(${route}Children)`;
338
- return [
339
- childConfigs,
340
- childrenDeclaration,
341
- children2,
342
- routeWithChildren
343
- ].join("\n\n");
344
- }
345
- return void 0;
346
- });
347
- return children.filter(Boolean).join("\n\n");
348
- }
349
- const routeConfigChildrenText = buildRouteTreeConfig(routeTree);
350
- const sortedRouteNodes = multiSortBy(routeNodes, [
351
- (d) => {
352
- var _a;
353
- return ((_a = d.routePath) == null ? void 0 : _a.includes(`/${rootPathId}`)) ? -1 : 1;
354
- },
355
- (d) => {
356
- var _a;
357
- return (_a = d.routePath) == null ? void 0 : _a.split("/").length;
358
- },
359
- (d) => {
360
- var _a;
361
- return ((_a = d.routePath) == null ? void 0 : _a.endsWith(config.indexToken)) ? -1 : 1;
362
- },
363
- (d) => d
364
- ]);
365
- const typeImports = Object.entries({
366
- // Used for augmentation of regular routes
367
- CreateFileRoute: config.verboseFileRoutes === false && sortedRouteNodes.some(
368
- (d) => isRouteNodeValidForAugmentation(d) && d._fsRouteType !== "lazy"
369
- ),
370
- // Used for augmentation of lazy (`.lazy`) routes
371
- CreateLazyFileRoute: config.verboseFileRoutes === false && sortedRouteNodes.some(
372
- (node) => {
373
- var _a;
374
- return ((_a = routePiecesByPath[node.routePath]) == null ? void 0 : _a.lazy) && isRouteNodeValidForAugmentation(node);
375
- }
376
- ),
377
- // Used in the process of augmenting the routes
378
- FileRoutesByPath: config.verboseFileRoutes === false && sortedRouteNodes.some((d) => isRouteNodeValidForAugmentation(d))
379
- }).filter((d) => d[1]).map((d) => d[0]).sort((a, b) => a.localeCompare(b));
380
- const imports = Object.entries({
381
- createFileRoute: sortedRouteNodes.some((d) => d.isVirtual),
382
- lazyFn: sortedRouteNodes.some(
383
- (node) => {
384
- var _a;
385
- return (_a = routePiecesByPath[node.routePath]) == null ? void 0 : _a.loader;
386
- }
387
- ),
388
- lazyRouteComponent: sortedRouteNodes.some(
389
- (node) => {
390
- var _a, _b, _c;
391
- return ((_a = routePiecesByPath[node.routePath]) == null ? void 0 : _a.component) || ((_b = routePiecesByPath[node.routePath]) == null ? void 0 : _b.errorComponent) || ((_c = routePiecesByPath[node.routePath]) == null ? void 0 : _c.pendingComponent);
392
- }
393
- )
394
- }).filter((d) => d[1]).map((d) => d[0]);
395
- const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual);
396
- function getImportPath(node) {
397
- return replaceBackslash(
398
- removeExt(
399
- path.relative(
400
- path.dirname(config.generatedRouteTree),
401
- path.resolve(config.routesDirectory, node.filePath)
402
- ),
403
- config.addExtensions
404
- )
405
- );
406
- }
407
- const routeImports = [
408
- ...config.routeTreeFileHeader,
409
- `// This file was automatically generated by TanStack Router.
410
- // You should NOT make any changes in this file as it will be overwritten.
411
- // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.`,
412
- [
413
- imports.length ? `import { ${imports.join(", ")} } from '${ROUTE_TEMPLATE.fullPkg}'` : "",
414
- !TYPES_DISABLED && typeImports.length ? `import type { ${typeImports.join(", ")} } from '${ROUTE_TEMPLATE.fullPkg}'` : ""
415
- ].filter(Boolean).join("\n"),
416
- "// Import Routes",
417
- [
418
- `import { Route as rootRoute } from './${getImportPath(rootRouteNode)}'`,
419
- ...sortedRouteNodes.filter((d) => !d.isVirtual).map((node) => {
420
- return `import { Route as ${node.variableName}RouteImport } from './${getImportPath(node)}'`;
421
- })
422
- ].join("\n"),
423
- virtualRouteNodes.length ? "// Create Virtual Routes" : "",
424
- virtualRouteNodes.map((node) => {
425
- return `const ${node.variableName}RouteImport = createFileRoute('${node.routePath}')()`;
426
- }).join("\n"),
427
- "// Create/Update Routes",
428
- sortedRouteNodes.map((node) => {
429
- var _a, _b, _c, _d, _e, _f;
430
- const loaderNode = (_a = routePiecesByPath[node.routePath]) == null ? void 0 : _a.loader;
431
- const componentNode = (_b = routePiecesByPath[node.routePath]) == null ? void 0 : _b.component;
432
- const errorComponentNode = (_c = routePiecesByPath[node.routePath]) == null ? void 0 : _c.errorComponent;
433
- const pendingComponentNode = (_d = routePiecesByPath[node.routePath]) == null ? void 0 : _d.pendingComponent;
434
- const lazyComponentNode = (_e = routePiecesByPath[node.routePath]) == null ? void 0 : _e.lazy;
435
- return [
436
- [
437
- `const ${node.variableName}Route = ${node.variableName}RouteImport.update({
438
- ${[
439
- `id: '${node.path}'`,
440
- !node.isNonPath ? `path: '${node.cleanedPath}'` : void 0,
441
- `getParentRoute: () => ${((_f = node.parent) == null ? void 0 : _f.variableName) ?? "root"}Route`
442
- ].filter(Boolean).join(",")}
443
- }${TYPES_DISABLED ? "" : "as any"})`,
444
- loaderNode ? `.updateLoader({ loader: lazyFn(() => import('./${replaceBackslash(
445
- removeExt(
446
- path.relative(
447
- path.dirname(config.generatedRouteTree),
448
- path.resolve(config.routesDirectory, loaderNode.filePath)
449
- ),
450
- config.addExtensions
451
- )
452
- )}'), 'loader') })` : "",
453
- componentNode || errorComponentNode || pendingComponentNode ? `.update({
454
- ${[
455
- ["component", componentNode],
456
- ["errorComponent", errorComponentNode],
457
- ["pendingComponent", pendingComponentNode]
458
- ].filter((d) => d[1]).map((d) => {
459
- return `${d[0]}: lazyRouteComponent(() => import('./${replaceBackslash(
460
- removeExt(
461
- path.relative(
462
- path.dirname(config.generatedRouteTree),
463
- path.resolve(config.routesDirectory, d[1].filePath)
464
- ),
465
- config.addExtensions
466
- )
467
- )}'), '${d[0]}')`;
468
- }).join("\n,")}
469
- })` : "",
470
- lazyComponentNode ? `.lazy(() => import('./${replaceBackslash(
471
- removeExt(
472
- path.relative(
473
- path.dirname(config.generatedRouteTree),
474
- path.resolve(
475
- config.routesDirectory,
476
- lazyComponentNode.filePath
477
- )
478
- ),
479
- config.addExtensions
480
- )
481
- )}').then((d) => d.Route))` : ""
482
- ].join("")
483
- ].join("\n\n");
484
- }).join("\n\n"),
485
- ...TYPES_DISABLED ? [] : [
486
- "// Populate the FileRoutesByPath interface",
487
- `declare module '${ROUTE_TEMPLATE.fullPkg}' {
488
- interface FileRoutesByPath {
489
- ${routeNodes.map((routeNode) => {
490
- var _a, _b;
491
- const filePathId = routeNode.routePath;
492
- return `'${filePathId}': {
493
- id: '${filePathId}'
494
- path: '${inferPath(routeNode)}'
495
- fullPath: '${inferFullPath(routeNode)}'
496
- preLoaderRoute: typeof ${routeNode.variableName}RouteImport
497
- 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"}
498
- }`;
499
- }).join("\n")}
500
- }
501
- }`
502
- ],
503
- ...TYPES_DISABLED ? [] : config.verboseFileRoutes !== false ? [] : [
504
- `// Add type-safety to the createFileRoute function across the route tree`,
505
- routeNodes.map((routeNode) => {
506
- var _a;
507
- function getModuleDeclaration(routeNode2) {
508
- if (!isRouteNodeValidForAugmentation(routeNode2)) {
509
- return "";
510
- }
511
- return `declare module './${getImportPath(routeNode2)}' {
512
- const ${routeNode2._fsRouteType === "lazy" ? "createLazyFileRoute" : "createFileRoute"}: ${routeNode2._fsRouteType === "lazy" ? `CreateLazyFileRoute<FileRoutesByPath['${routeNode2.routePath}']['preLoaderRoute']>}` : `CreateFileRoute<
513
- '${routeNode2.routePath}',
514
- FileRoutesByPath['${routeNode2.routePath}']['parentRoute'],
515
- FileRoutesByPath['${routeNode2.routePath}']['id'],
516
- FileRoutesByPath['${routeNode2.routePath}']['path'],
517
- FileRoutesByPath['${routeNode2.routePath}']['fullPath']
518
- >
519
- }`}`;
520
- }
521
- return getModuleDeclaration(routeNode) + getModuleDeclaration(
522
- (_a = routePiecesByPath[routeNode.routePath]) == null ? void 0 : _a.lazy
523
- );
524
- }).join("\n")
525
- ],
526
- "// Create and export the route tree",
527
- routeConfigChildrenText,
528
- ...TYPES_DISABLED ? [] : [
529
- `export interface FileRoutesByFullPath {
530
- ${[...createRouteNodesByFullPath(routeNodes).entries()].map(
531
- ([fullPath, routeNode]) => {
532
- return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
533
- }
534
- )}
535
- }`,
536
- `export interface FileRoutesByTo {
537
- ${[...createRouteNodesByTo(routeNodes).entries()].map(([to, routeNode]) => {
538
- return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
539
- })}
540
- }`,
541
- `export interface FileRoutesById {
542
- '${rootRouteId}': typeof rootRoute,
543
- ${[...createRouteNodesById(routeNodes).entries()].map(([id, routeNode]) => {
544
- return `'${id}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`;
545
- })}
546
- }`,
547
- `export interface FileRouteTypes {
548
- fileRoutesByFullPath: FileRoutesByFullPath
549
- fullPaths: ${routeNodes.length > 0 ? [...createRouteNodesByFullPath(routeNodes).keys()].map((fullPath) => `'${fullPath}'`).join("|") : "never"}
550
- fileRoutesByTo: FileRoutesByTo
551
- to: ${routeNodes.length > 0 ? [...createRouteNodesByTo(routeNodes).keys()].map((to) => `'${to}'`).join("|") : "never"}
552
- id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(routeNodes).keys()].map((id) => `'${id}'`)].join("|")}
553
- fileRoutesById: FileRoutesById
554
- }`,
555
- `export interface RootRouteChildren {
556
- ${routeTree.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(",")}
557
- }`
558
- ],
559
- `const rootRouteChildren${TYPES_DISABLED ? "" : ": RootRouteChildren"} = {
560
- ${routeTree.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(",")}
561
- }`,
562
- `export const routeTree = rootRoute._addFileChildren(rootRouteChildren)${TYPES_DISABLED ? "" : "._addFileTypes<FileRouteTypes>()"}`,
563
- ...config.routeTreeFileFooter
564
- ].filter(Boolean).join("\n\n");
565
- const createRouteManifest = () => {
566
- const routesManifest = {
567
- [rootRouteId]: {
568
- filePath: rootRouteNode.filePath,
569
- children: routeTree.map((d) => d.routePath)
570
- },
571
- ...Object.fromEntries(
572
- routeNodes.map((d) => {
573
- var _a, _b;
574
- const filePathId = d.routePath;
575
- return [
576
- filePathId,
577
- {
578
- filePath: d.filePath,
579
- parent: ((_a = d.parent) == null ? void 0 : _a.routePath) ? d.parent.routePath : void 0,
580
- children: (_b = d.children) == null ? void 0 : _b.map((childRoute) => childRoute.routePath)
581
- }
582
- ];
583
- })
584
- )
585
- };
586
- return JSON.stringify(
587
- {
588
- routes: routesManifest
589
- },
590
- null,
591
- 2
592
- );
593
- };
594
- const includeManifest = ["react", "solid"];
595
- const routeConfigFileContent = config.disableManifestGeneration || !includeManifest.includes(config.target) ? routeImports : [
596
- routeImports,
597
- "\n",
598
- "/* ROUTE_MANIFEST_START",
599
- createRouteManifest(),
600
- "ROUTE_MANIFEST_END */"
601
- ].join("\n");
602
- if (!checkLatest()) return;
603
- const existingRouteTreeContent = await fsp.readFile(path.resolve(config.generatedRouteTree), "utf-8").catch((err) => {
604
- if (err.code === "ENOENT") {
605
- return "";
606
- }
607
- throw err;
608
- });
609
- if (!checkLatest()) return;
610
- await fsp.mkdir(path.dirname(path.resolve(config.generatedRouteTree)), {
611
- recursive: true
612
- });
613
- if (!checkLatest()) return;
614
- const routeTreeWriteResult = await writeIfDifferent(
615
- path.resolve(config.generatedRouteTree),
616
- config.enableRouteTreeFormatting ? await format(existingRouteTreeContent, config) : existingRouteTreeContent,
617
- config.enableRouteTreeFormatting ? await format(routeConfigFileContent, config) : routeConfigFileContent,
618
- {
619
- beforeWrite: () => {
620
- logger.log(`🟡 Updating ${config.generatedRouteTree}`);
621
- }
622
- }
623
- );
624
- if (routeTreeWriteResult && !checkLatest()) {
625
- return;
626
- }
627
- logger.log(
628
- `✅ Processed ${routeNodes.length === 1 ? "route" : "routes"} in ${Date.now() - start}ms`
629
- );
630
- }
631
- function removeGroups(s) {
632
- return s.replace(possiblyNestedRouteGroupPatternRegex, "");
633
- }
634
- function isRouteNodeValidForAugmentation(routeNode) {
635
- if (!routeNode || routeNode.isVirtual) {
636
- return false;
637
- }
638
- return true;
639
- }
640
- function determineNodePath(node) {
641
- var _a;
642
- return node.path = node.parent ? ((_a = node.routePath) == null ? void 0 : _a.replace(node.parent.routePath ?? "", "")) || "/" : node.routePath;
643
- }
644
- function removeLastSegmentFromPath(routePath = "/") {
645
- const segments = routePath.split("/");
646
- segments.pop();
647
- return segments.join("/");
648
- }
649
- function removeLayoutSegments(routePath = "/") {
650
- const segments = routePath.split("/");
651
- const newSegments = segments.filter((segment) => !segment.startsWith("_"));
652
- return newSegments.join("/");
653
- }
654
- function hasParentRoute(routes, node, routePathToCheck) {
655
- if (!routePathToCheck || routePathToCheck === "/") {
656
- return null;
657
- }
658
- const sortedNodes = multiSortBy(routes, [
659
- (d) => d.routePath.length * -1,
660
- (d) => d.variableName
661
- ]).filter((d) => d.routePath !== `/${rootPathId}`);
662
- for (const route of sortedNodes) {
663
- if (route.routePath === "/") continue;
664
- if (routePathToCheck.startsWith(`${route.routePath}/`) && route.routePath !== routePathToCheck) {
665
- return route;
911
+ acc.routeTree.push(node);
666
912
  }
667
- }
668
- const segments = routePathToCheck.split("/");
669
- segments.pop();
670
- const parentRoutePath = segments.join("/");
671
- return hasParentRoute(routes, node, parentRoutePath);
672
- }
673
- const getResolvedRouteNodeVariableName = (routeNode) => {
674
- var _a;
675
- return ((_a = routeNode.children) == null ? void 0 : _a.length) ? `${routeNode.variableName}RouteWithChildren` : `${routeNode.variableName}Route`;
676
- };
677
- const createRouteNodesByFullPath = (routeNodes) => {
678
- return new Map(
679
- routeNodes.map((routeNode) => [inferFullPath(routeNode), routeNode])
680
- );
681
- };
682
- const createRouteNodesByTo = (routeNodes) => {
683
- return new Map(
684
- dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [
685
- inferTo(routeNode),
686
- routeNode
687
- ])
688
- );
689
- };
690
- const createRouteNodesById = (routeNodes) => {
691
- return new Map(
692
- routeNodes.map((routeNode) => {
693
- const id = routeNode.routePath ?? "";
694
- return [id, routeNode];
695
- })
696
- );
697
- };
698
- const inferFullPath = (routeNode) => {
699
- const fullPath = removeGroups(
700
- removeUnderscores(removeLayoutSegments(routeNode.routePath)) ?? ""
701
- );
702
- return routeNode.cleanedPath === "/" ? fullPath : fullPath.replace(/\/$/, "");
703
- };
704
- const inferPath = (routeNode) => {
705
- var _a;
706
- return routeNode.cleanedPath === "/" ? routeNode.cleanedPath : ((_a = routeNode.cleanedPath) == null ? void 0 : _a.replace(/\/$/, "")) ?? "";
707
- };
708
- const inferTo = (routeNode) => {
709
- const fullPath = inferFullPath(routeNode);
710
- if (fullPath === "/") return fullPath;
711
- return fullPath.replace(/\/$/, "");
712
- };
713
- const dedupeBranchesAndIndexRoutes = (routes) => {
714
- return routes.filter((route) => {
715
- var _a;
716
- if ((_a = route.children) == null ? void 0 : _a.find((child) => child.cleanedPath === "/")) return false;
717
- return true;
718
- });
719
- };
720
- function checkUnique(routes, key) {
721
- const keys = routes.map((d) => d[key]);
722
- const uniqueKeys = new Set(keys);
723
- if (keys.length !== uniqueKeys.size) {
724
- const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i);
725
- const conflictingFiles = routes.filter(
726
- (d) => duplicateKeys.includes(d[key])
727
- );
728
- return conflictingFiles;
729
- }
730
- return void 0;
731
- }
732
- function checkRouteFullPathUniqueness(_routes, config) {
733
- const routes = _routes.map((d) => {
734
- const inferredFullPath = inferFullPath(d);
735
- return { ...d, inferredFullPath };
736
- });
737
- const conflictingFiles = checkUnique(routes, "inferredFullPath");
738
- if (conflictingFiles !== void 0) {
739
- const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? "s" : ""}: ${conflictingFiles.map((p) => `"${p.inferredFullPath}"`).join(", ")}.
740
- Please ensure each Route has a unique full path.
741
- Conflicting files:
742
- ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join("\n ")}
743
- `;
744
- throw new Error(errorMessage);
913
+ acc.routeNodes.push(node);
745
914
  }
746
915
  }
747
916
  export {
748
- generator
917
+ Generator
749
918
  };
750
919
  //# sourceMappingURL=generator.js.map