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