@tanstack/router-generator 1.166.8 → 1.166.10

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 (58) hide show
  1. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/cjs/config.cjs +111 -147
  3. package/dist/cjs/config.cjs.map +1 -1
  4. package/dist/cjs/filesystem/physical/getRouteNodes.cjs +224 -303
  5. package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
  6. package/dist/cjs/filesystem/physical/rootPathId.cjs +5 -4
  7. package/dist/cjs/filesystem/physical/rootPathId.cjs.map +1 -1
  8. package/dist/cjs/filesystem/virtual/config.cjs +32 -30
  9. package/dist/cjs/filesystem/virtual/config.cjs.map +1 -1
  10. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +164 -209
  11. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
  12. package/dist/cjs/filesystem/virtual/loadConfigFile.cjs +9 -8
  13. package/dist/cjs/filesystem/virtual/loadConfigFile.cjs.map +1 -1
  14. package/dist/cjs/generator.cjs +766 -1106
  15. package/dist/cjs/generator.cjs.map +1 -1
  16. package/dist/cjs/index.cjs +32 -34
  17. package/dist/cjs/logger.cjs +28 -34
  18. package/dist/cjs/logger.cjs.map +1 -1
  19. package/dist/cjs/template.cjs +144 -151
  20. package/dist/cjs/template.cjs.map +1 -1
  21. package/dist/cjs/transform/transform.cjs +287 -426
  22. package/dist/cjs/transform/transform.cjs.map +1 -1
  23. package/dist/cjs/transform/utils.cjs +31 -33
  24. package/dist/cjs/transform/utils.cjs.map +1 -1
  25. package/dist/cjs/utils.cjs +534 -544
  26. package/dist/cjs/utils.cjs.map +1 -1
  27. package/dist/cjs/validate-route-params.cjs +66 -51
  28. package/dist/cjs/validate-route-params.cjs.map +1 -1
  29. package/dist/esm/config.js +106 -147
  30. package/dist/esm/config.js.map +1 -1
  31. package/dist/esm/filesystem/physical/getRouteNodes.js +220 -286
  32. package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
  33. package/dist/esm/filesystem/physical/rootPathId.js +6 -5
  34. package/dist/esm/filesystem/physical/rootPathId.js.map +1 -1
  35. package/dist/esm/filesystem/virtual/config.js +31 -30
  36. package/dist/esm/filesystem/virtual/config.js.map +1 -1
  37. package/dist/esm/filesystem/virtual/getRouteNodes.js +161 -208
  38. package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
  39. package/dist/esm/filesystem/virtual/loadConfigFile.js +7 -7
  40. package/dist/esm/filesystem/virtual/loadConfigFile.js.map +1 -1
  41. package/dist/esm/generator.js +756 -1083
  42. package/dist/esm/generator.js.map +1 -1
  43. package/dist/esm/index.js +4 -31
  44. package/dist/esm/logger.js +29 -35
  45. package/dist/esm/logger.js.map +1 -1
  46. package/dist/esm/template.js +144 -152
  47. package/dist/esm/template.js.map +1 -1
  48. package/dist/esm/transform/transform.js +285 -425
  49. package/dist/esm/transform/transform.js.map +1 -1
  50. package/dist/esm/transform/utils.js +31 -33
  51. package/dist/esm/transform/utils.js.map +1 -1
  52. package/dist/esm/utils.js +529 -564
  53. package/dist/esm/utils.js.map +1 -1
  54. package/dist/esm/validate-route-params.js +67 -52
  55. package/dist/esm/validate-route-params.js.map +1 -1
  56. package/package.json +5 -5
  57. package/dist/cjs/index.cjs.map +0 -1
  58. package/dist/esm/index.js.map +0 -1
package/dist/esm/utils.js CHANGED
@@ -1,644 +1,609 @@
1
- import * as fsp from "node:fs/promises";
1
+ import "./filesystem/physical/rootPathId.js";
2
2
  import path from "node:path";
3
+ import * as fsp from "node:fs/promises";
3
4
  import * as prettier from "prettier";
4
- import { rootPathId } from "./filesystem/physical/rootPathId.js";
5
- class RoutePrefixMap {
6
- constructor(routes) {
7
- this.prefixToRoute = /* @__PURE__ */ new Map();
8
- this.layoutRoutes = [];
9
- for (const route of routes) {
10
- if (!route.routePath || route.routePath === `/${rootPathId}`) continue;
11
- if (route._fsRouteType === "lazy" || route._fsRouteType === "loader" || route._fsRouteType === "component" || route._fsRouteType === "pendingComponent" || route._fsRouteType === "errorComponent" || route._fsRouteType === "notFoundComponent") {
12
- continue;
13
- }
14
- this.prefixToRoute.set(route.routePath, route);
15
- if (route._fsRouteType === "pathless_layout" || route._fsRouteType === "layout" || route._fsRouteType === "__root") {
16
- this.layoutRoutes.push(route);
17
- }
18
- }
19
- this.layoutRoutes.sort(
20
- (a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0)
21
- );
22
- }
23
- /**
24
- * Find the longest matching parent route for a given path.
25
- * O(k) where k is the number of path segments, not O(n) routes.
26
- */
27
- findParent(routePath) {
28
- if (!routePath || routePath === "/") return null;
29
- let searchPath = routePath;
30
- while (searchPath.length > 0) {
31
- const lastSlash = searchPath.lastIndexOf("/");
32
- if (lastSlash <= 0) break;
33
- searchPath = searchPath.substring(0, lastSlash);
34
- const parent = this.prefixToRoute.get(searchPath);
35
- if (parent && parent.routePath !== routePath) {
36
- return parent;
37
- }
38
- }
39
- return null;
40
- }
41
- /**
42
- * Check if a route exists at the given path.
43
- */
44
- has(routePath) {
45
- return this.prefixToRoute.has(routePath);
46
- }
47
- /**
48
- * Get a route by exact path.
49
- */
50
- get(routePath) {
51
- return this.prefixToRoute.get(routePath);
52
- }
53
- }
5
+ //#region src/utils.ts
6
+ /**
7
+ * Prefix map for O(1) parent route lookups.
8
+ * Maps each route path prefix to the route node that owns that prefix.
9
+ * Enables finding longest matching parent without linear search.
10
+ */
11
+ var RoutePrefixMap = class {
12
+ constructor(routes) {
13
+ this.prefixToRoute = /* @__PURE__ */ new Map();
14
+ this.layoutRoutes = [];
15
+ for (const route of routes) {
16
+ if (!route.routePath || route.routePath === `/__root`) continue;
17
+ if (route._fsRouteType === "lazy" || route._fsRouteType === "loader" || route._fsRouteType === "component" || route._fsRouteType === "pendingComponent" || route._fsRouteType === "errorComponent" || route._fsRouteType === "notFoundComponent") continue;
18
+ this.prefixToRoute.set(route.routePath, route);
19
+ if (route._fsRouteType === "pathless_layout" || route._fsRouteType === "layout" || route._fsRouteType === "__root") this.layoutRoutes.push(route);
20
+ }
21
+ this.layoutRoutes.sort((a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0));
22
+ }
23
+ /**
24
+ * Find the longest matching parent route for a given path.
25
+ * O(k) where k is the number of path segments, not O(n) routes.
26
+ */
27
+ findParent(routePath) {
28
+ if (!routePath || routePath === "/") return null;
29
+ let searchPath = routePath;
30
+ while (searchPath.length > 0) {
31
+ const lastSlash = searchPath.lastIndexOf("/");
32
+ if (lastSlash <= 0) break;
33
+ searchPath = searchPath.substring(0, lastSlash);
34
+ const parent = this.prefixToRoute.get(searchPath);
35
+ if (parent && parent.routePath !== routePath) return parent;
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Check if a route exists at the given path.
41
+ */
42
+ has(routePath) {
43
+ return this.prefixToRoute.has(routePath);
44
+ }
45
+ /**
46
+ * Get a route by exact path.
47
+ */
48
+ get(routePath) {
49
+ return this.prefixToRoute.get(routePath);
50
+ }
51
+ };
54
52
  function multiSortBy(arr, accessors = [(d) => d]) {
55
- const len = arr.length;
56
- const indexed = new Array(len);
57
- for (let i = 0; i < len; i++) {
58
- const item = arr[i];
59
- const keys = new Array(accessors.length);
60
- for (let j = 0; j < accessors.length; j++) {
61
- keys[j] = accessors[j](item);
62
- }
63
- indexed[i] = { item, index: i, keys };
64
- }
65
- indexed.sort((a, b) => {
66
- for (let j = 0; j < accessors.length; j++) {
67
- const ao = a.keys[j];
68
- const bo = b.keys[j];
69
- if (typeof ao === "undefined") {
70
- if (typeof bo === "undefined") {
71
- continue;
72
- }
73
- return 1;
74
- }
75
- if (ao === bo) {
76
- continue;
77
- }
78
- return ao > bo ? 1 : -1;
79
- }
80
- return a.index - b.index;
81
- });
82
- const result = new Array(len);
83
- for (let i = 0; i < len; i++) {
84
- result[i] = indexed[i].item;
85
- }
86
- return result;
87
- }
88
- function cleanPath(path2) {
89
- return path2.replace(/\/{2,}/g, "/");
90
- }
91
- function trimPathLeft(path2) {
92
- return path2 === "/" ? path2 : path2.replace(/^\/{1,}/, "");
93
- }
94
- function removeLeadingSlash(path2) {
95
- return path2.replace(/^\//, "");
53
+ const len = arr.length;
54
+ const indexed = new Array(len);
55
+ for (let i = 0; i < len; i++) {
56
+ const item = arr[i];
57
+ const keys = new Array(accessors.length);
58
+ for (let j = 0; j < accessors.length; j++) keys[j] = accessors[j](item);
59
+ indexed[i] = {
60
+ item,
61
+ index: i,
62
+ keys
63
+ };
64
+ }
65
+ indexed.sort((a, b) => {
66
+ for (let j = 0; j < accessors.length; j++) {
67
+ const ao = a.keys[j];
68
+ const bo = b.keys[j];
69
+ if (typeof ao === "undefined") {
70
+ if (typeof bo === "undefined") continue;
71
+ return 1;
72
+ }
73
+ if (ao === bo) continue;
74
+ return ao > bo ? 1 : -1;
75
+ }
76
+ return a.index - b.index;
77
+ });
78
+ const result = new Array(len);
79
+ for (let i = 0; i < len; i++) result[i] = indexed[i].item;
80
+ return result;
81
+ }
82
+ function cleanPath(path) {
83
+ return path.replace(/\/{2,}/g, "/");
84
+ }
85
+ function trimPathLeft(path) {
86
+ return path === "/" ? path : path.replace(/^\/{1,}/, "");
87
+ }
88
+ function removeLeadingSlash(path) {
89
+ return path.replace(/^\//, "");
96
90
  }
97
91
  function removeTrailingSlash(s) {
98
- return s.replace(/\/$/, "");
99
- }
100
- const BRACKET_CONTENT_RE = /\[(.*?)\]/g;
101
- const SPLIT_REGEX = new RegExp("(?<!\\[)\\.(?!\\])", "g");
102
- const DISALLOWED_ESCAPE_CHARS = /* @__PURE__ */ new Set([
103
- "/",
104
- "\\",
105
- "?",
106
- "#",
107
- ":",
108
- "*",
109
- "<",
110
- ">",
111
- "|",
112
- "!",
113
- "$",
114
- "%"
92
+ return s.replace(/\/$/, "");
93
+ }
94
+ var BRACKET_CONTENT_RE = /\[(.*?)\]/g;
95
+ var SPLIT_REGEX = /(?<!\[)\.(?!\])/g;
96
+ /**
97
+ * Characters that cannot be escaped in square brackets.
98
+ * These are characters that would cause issues in URLs or file systems.
99
+ */
100
+ var DISALLOWED_ESCAPE_CHARS = new Set([
101
+ "/",
102
+ "\\",
103
+ "?",
104
+ "#",
105
+ ":",
106
+ "*",
107
+ "<",
108
+ ">",
109
+ "|",
110
+ "!",
111
+ "$",
112
+ "%"
115
113
  ]);
116
114
  function determineInitialRoutePath(routePath) {
117
- const originalRoutePath = cleanPath(
118
- `/${(cleanPath(routePath) || "").split(SPLIT_REGEX).join("/")}`
119
- ) || "";
120
- const parts = routePath.split(SPLIT_REGEX);
121
- const escapedParts = parts.map((part) => {
122
- let match;
123
- while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {
124
- const character = match[1];
125
- if (character === void 0) continue;
126
- if (DISALLOWED_ESCAPE_CHARS.has(character)) {
127
- console.error(
128
- `Error: Disallowed character "${character}" found in square brackets in route path "${routePath}".
129
- You cannot use any of the following characters in square brackets: ${Array.from(
130
- DISALLOWED_ESCAPE_CHARS
131
- ).join(", ")}
132
- Please remove and/or replace them.`
133
- );
134
- process.exit(1);
135
- }
136
- }
137
- return part.replace(BRACKET_CONTENT_RE, "$1");
138
- });
139
- const final = cleanPath(`/${escapedParts.join("/")}`) || "";
140
- return {
141
- routePath: final,
142
- originalRoutePath
143
- };
144
- }
115
+ const originalRoutePath = cleanPath(`/${(cleanPath(routePath) || "").split(SPLIT_REGEX).join("/")}`) || "";
116
+ return {
117
+ routePath: cleanPath(`/${routePath.split(SPLIT_REGEX).map((part) => {
118
+ let match;
119
+ while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {
120
+ const character = match[1];
121
+ if (character === void 0) continue;
122
+ if (DISALLOWED_ESCAPE_CHARS.has(character)) {
123
+ console.error(`Error: Disallowed character "${character}" found in square brackets in route path "${routePath}".\nYou cannot use any of the following characters in square brackets: ${Array.from(DISALLOWED_ESCAPE_CHARS).join(", ")}\nPlease remove and/or replace them.`);
124
+ process.exit(1);
125
+ }
126
+ }
127
+ return part.replace(BRACKET_CONTENT_RE, "$1");
128
+ }).join("/")}`) || "",
129
+ originalRoutePath
130
+ };
131
+ }
132
+ /**
133
+ * Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).
134
+ * E.g., "[index]" -> true, "[_layout]" -> true, "foo[.]bar" -> false, "index" -> false
135
+ */
145
136
  function isFullyEscapedSegment(originalSegment) {
146
- return originalSegment.startsWith("[") && originalSegment.endsWith("]") && !originalSegment.slice(1, -1).includes("[") && !originalSegment.slice(1, -1).includes("]");
147
- }
137
+ return originalSegment.startsWith("[") && originalSegment.endsWith("]") && !originalSegment.slice(1, -1).includes("[") && !originalSegment.slice(1, -1).includes("]");
138
+ }
139
+ /**
140
+ * Checks if the leading underscore in a segment is escaped.
141
+ * Returns true if:
142
+ * - Segment starts with [_] pattern: "[_]layout" -> "_layout"
143
+ * - Segment is fully escaped and content starts with _: "[_1nd3x]" -> "_1nd3x"
144
+ */
148
145
  function hasEscapedLeadingUnderscore(originalSegment) {
149
- return originalSegment.startsWith("[_]") || originalSegment.startsWith("[_") && isFullyEscapedSegment(originalSegment);
150
- }
146
+ return originalSegment.startsWith("[_]") || originalSegment.startsWith("[_") && isFullyEscapedSegment(originalSegment);
147
+ }
148
+ /**
149
+ * Checks if the trailing underscore in a segment is escaped.
150
+ * Returns true if:
151
+ * - Segment ends with [_] pattern: "blog[_]" -> "blog_"
152
+ * - Segment is fully escaped and content ends with _: "[_r0ut3_]" -> "_r0ut3_"
153
+ */
151
154
  function hasEscapedTrailingUnderscore(originalSegment) {
152
- return originalSegment.endsWith("[_]") || originalSegment.endsWith("_]") && isFullyEscapedSegment(originalSegment);
155
+ return originalSegment.endsWith("[_]") || originalSegment.endsWith("_]") && isFullyEscapedSegment(originalSegment);
153
156
  }
154
- const backslashRegex = /\\/g;
157
+ var backslashRegex = /\\/g;
155
158
  function replaceBackslash(s) {
156
- return s.replace(backslashRegex, "/");
157
- }
158
- const alphanumericRegex = /[a-zA-Z0-9_]/;
159
- const splatSlashRegex = /\/\$\//g;
160
- const trailingSplatRegex = /\$$/g;
161
- const bracketSplatRegex = /\$\{\$\}/g;
162
- const dollarSignRegex = /\$/g;
163
- const splitPathRegex = /[/-]/g;
164
- const leadingDigitRegex = /^(\d)/g;
165
- const toVariableSafeChar = (char) => {
166
- if (alphanumericRegex.test(char)) {
167
- return char;
168
- }
169
- switch (char) {
170
- case ".":
171
- return "Dot";
172
- case "-":
173
- return "Dash";
174
- case "@":
175
- return "At";
176
- case "(":
177
- return "";
178
- // Removed since route groups use parentheses
179
- case ")":
180
- return "";
181
- // Removed since route groups use parentheses
182
- case " ":
183
- return "";
184
- // Remove spaces
185
- default:
186
- return `Char${char.charCodeAt(0)}`;
187
- }
159
+ return s.replace(backslashRegex, "/");
160
+ }
161
+ var alphanumericRegex = /[a-zA-Z0-9_]/;
162
+ var splatSlashRegex = /\/\$\//g;
163
+ var trailingSplatRegex = /\$$/g;
164
+ var bracketSplatRegex = /\$\{\$\}/g;
165
+ var dollarSignRegex = /\$/g;
166
+ var splitPathRegex = /[/-]/g;
167
+ var leadingDigitRegex = /^(\d)/g;
168
+ var toVariableSafeChar = (char) => {
169
+ if (alphanumericRegex.test(char)) return char;
170
+ switch (char) {
171
+ case ".": return "Dot";
172
+ case "-": return "Dash";
173
+ case "@": return "At";
174
+ case "(": return "";
175
+ case ")": return "";
176
+ case " ": return "";
177
+ default: return `Char${char.charCodeAt(0)}`;
178
+ }
188
179
  };
189
180
  function routePathToVariable(routePath) {
190
- const cleaned = removeUnderscores(routePath);
191
- if (!cleaned) return "";
192
- const parts = cleaned.replace(splatSlashRegex, "/splat/").replace(trailingSplatRegex, "splat").replace(bracketSplatRegex, "splat").replace(dollarSignRegex, "").split(splitPathRegex);
193
- let result = "";
194
- for (let i = 0; i < parts.length; i++) {
195
- const part = parts[i];
196
- const segment = i > 0 ? capitalize(part) : part;
197
- for (let j = 0; j < segment.length; j++) {
198
- result += toVariableSafeChar(segment[j]);
199
- }
200
- }
201
- return result.replace(leadingDigitRegex, "R$1");
202
- }
203
- const underscoreStartEndRegex = /(^_|_$)/gi;
204
- const underscoreSlashRegex = /(\/_|_\/)/gi;
181
+ const cleaned = removeUnderscores(routePath);
182
+ if (!cleaned) return "";
183
+ const parts = cleaned.replace(splatSlashRegex, "/splat/").replace(trailingSplatRegex, "splat").replace(bracketSplatRegex, "splat").replace(dollarSignRegex, "").split(splitPathRegex);
184
+ let result = "";
185
+ for (let i = 0; i < parts.length; i++) {
186
+ const part = parts[i];
187
+ const segment = i > 0 ? capitalize(part) : part;
188
+ for (let j = 0; j < segment.length; j++) result += toVariableSafeChar(segment[j]);
189
+ }
190
+ return result.replace(leadingDigitRegex, "R$1");
191
+ }
192
+ var underscoreStartEndRegex = /(^_|_$)/gi;
193
+ var underscoreSlashRegex = /(\/_|_\/)/gi;
205
194
  function removeUnderscores(s) {
206
- return s?.replace(underscoreStartEndRegex, "").replace(underscoreSlashRegex, "/");
207
- }
195
+ return s?.replace(underscoreStartEndRegex, "").replace(underscoreSlashRegex, "/");
196
+ }
197
+ /**
198
+ * Removes underscores from a path, but preserves underscores that were escaped
199
+ * in the original path (indicated by [_] syntax).
200
+ *
201
+ * @param routePath - The path with brackets removed
202
+ * @param originalPath - The original path that may contain [_] escape sequences
203
+ * @returns The path with non-escaped underscores removed
204
+ */
208
205
  function removeUnderscoresWithEscape(routePath, originalPath) {
209
- if (!routePath) return "";
210
- if (!originalPath) return removeUnderscores(routePath) ?? "";
211
- const routeSegments = routePath.split("/");
212
- const originalSegments = originalPath.split("/");
213
- const newSegments = routeSegments.map((segment, i) => {
214
- const originalSegment = originalSegments[i] || "";
215
- const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment);
216
- const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment);
217
- let result = segment;
218
- if (result.startsWith("_") && !leadingEscaped) {
219
- result = result.slice(1);
220
- }
221
- if (result.endsWith("_") && !trailingEscaped) {
222
- result = result.slice(0, -1);
223
- }
224
- return result;
225
- });
226
- return newSegments.join("/");
227
- }
206
+ if (!routePath) return "";
207
+ if (!originalPath) return removeUnderscores(routePath) ?? "";
208
+ const routeSegments = routePath.split("/");
209
+ const originalSegments = originalPath.split("/");
210
+ return routeSegments.map((segment, i) => {
211
+ const originalSegment = originalSegments[i] || "";
212
+ const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment);
213
+ const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment);
214
+ let result = segment;
215
+ if (result.startsWith("_") && !leadingEscaped) result = result.slice(1);
216
+ if (result.endsWith("_") && !trailingEscaped) result = result.slice(0, -1);
217
+ return result;
218
+ }).join("/");
219
+ }
220
+ /**
221
+ * Removes layout segments (segments starting with underscore) from a path,
222
+ * but preserves segments where the underscore was escaped.
223
+ *
224
+ * @param routePath - The path with brackets removed
225
+ * @param originalPath - The original path that may contain [_] escape sequences
226
+ * @returns The path with non-escaped layout segments removed
227
+ */
228
228
  function removeLayoutSegmentsWithEscape(routePath = "/", originalPath) {
229
- if (!originalPath) return removeLayoutSegments(routePath);
230
- const routeSegments = routePath.split("/");
231
- const originalSegments = originalPath.split("/");
232
- const newSegments = routeSegments.filter((segment, i) => {
233
- const originalSegment = originalSegments[i] || "";
234
- return !isSegmentPathless(segment, originalSegment);
235
- });
236
- return newSegments.join("/");
237
- }
229
+ if (!originalPath) return removeLayoutSegments(routePath);
230
+ const routeSegments = routePath.split("/");
231
+ const originalSegments = originalPath.split("/");
232
+ return routeSegments.filter((segment, i) => {
233
+ return !isSegmentPathless(segment, originalSegments[i] || "");
234
+ }).join("/");
235
+ }
236
+ /**
237
+ * Checks if a segment should be treated as a pathless/layout segment.
238
+ * A segment is pathless if it starts with underscore and the underscore is not escaped.
239
+ *
240
+ * @param segment - The segment from routePath (brackets removed)
241
+ * @param originalSegment - The segment from originalRoutePath (may contain brackets)
242
+ * @returns true if the segment is pathless (has non-escaped leading underscore)
243
+ */
238
244
  function isSegmentPathless(segment, originalSegment) {
239
- if (!segment.startsWith("_")) return false;
240
- return !hasEscapedLeadingUnderscore(originalSegment);
245
+ if (!segment.startsWith("_")) return false;
246
+ return !hasEscapedLeadingUnderscore(originalSegment);
241
247
  }
242
248
  function escapeRegExp(s) {
243
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
249
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
244
250
  }
245
251
  function sanitizeTokenFlags(flags) {
246
- if (!flags) return flags;
247
- return flags.replace(/[gy]/g, "");
252
+ if (!flags) return flags;
253
+ return flags.replace(/[gy]/g, "");
248
254
  }
249
255
  function createTokenRegex(token, opts) {
250
- if (token === void 0 || token === null) {
251
- throw new Error(
252
- `createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`
253
- );
254
- }
255
- try {
256
- if (typeof token === "string") {
257
- return opts.type === "segment" ? new RegExp(`^${escapeRegExp(token)}$`) : new RegExp(`[./]${escapeRegExp(token)}[.]`);
258
- }
259
- if (token instanceof RegExp) {
260
- const flags = sanitizeTokenFlags(token.flags);
261
- return opts.type === "segment" ? new RegExp(`^(?:${token.source})$`, flags) : new RegExp(`[./](?:${token.source})[.]`, flags);
262
- }
263
- if (typeof token === "object" && "regex" in token) {
264
- const flags = sanitizeTokenFlags(token.flags);
265
- return opts.type === "segment" ? new RegExp(`^(?:${token.regex})$`, flags) : new RegExp(`[./](?:${token.regex})[.]`, flags);
266
- }
267
- throw new Error(
268
- `createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`
269
- );
270
- } catch (e) {
271
- if (e instanceof SyntaxError) {
272
- const pattern = typeof token === "string" ? token : token instanceof RegExp ? token.source : token.regex;
273
- throw new Error(
274
- `Invalid regex pattern in token config: "${pattern}". ${e.message}`
275
- );
276
- }
277
- throw e;
278
- }
256
+ if (token === void 0 || token === null) throw new Error(`createTokenRegex: token is ${token}. This usually means the config was not properly parsed with defaults.`);
257
+ try {
258
+ if (typeof token === "string") return opts.type === "segment" ? new RegExp(`^${escapeRegExp(token)}$`) : new RegExp(`[./]${escapeRegExp(token)}[.]`);
259
+ if (token instanceof RegExp) {
260
+ const flags = sanitizeTokenFlags(token.flags);
261
+ return opts.type === "segment" ? new RegExp(`^(?:${token.source})$`, flags) : new RegExp(`[./](?:${token.source})[.]`, flags);
262
+ }
263
+ if (typeof token === "object" && "regex" in token) {
264
+ const flags = sanitizeTokenFlags(token.flags);
265
+ return opts.type === "segment" ? new RegExp(`^(?:${token.regex})$`, flags) : new RegExp(`[./](?:${token.regex})[.]`, flags);
266
+ }
267
+ throw new Error(`createTokenRegex: invalid token type. Expected string, RegExp, or { regex, flags } object, got: ${typeof token}`);
268
+ } catch (e) {
269
+ if (e instanceof SyntaxError) {
270
+ const pattern = typeof token === "string" ? token : token instanceof RegExp ? token.source : token.regex;
271
+ throw new Error(`Invalid regex pattern in token config: "${pattern}". ${e.message}`);
272
+ }
273
+ throw e;
274
+ }
279
275
  }
280
276
  function isBracketWrappedSegment(segment) {
281
- return segment.startsWith("[") && segment.endsWith("]");
277
+ return segment.startsWith("[") && segment.endsWith("]");
282
278
  }
283
279
  function unwrapBracketWrappedSegment(segment) {
284
- return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment;
280
+ return isBracketWrappedSegment(segment) ? segment.slice(1, -1) : segment;
285
281
  }
286
282
  function capitalize(s) {
287
- if (typeof s !== "string") return "";
288
- return s.charAt(0).toUpperCase() + s.slice(1);
283
+ if (typeof s !== "string") return "";
284
+ return s.charAt(0).toUpperCase() + s.slice(1);
289
285
  }
290
286
  function removeExt(d, addExtensions = false) {
291
- if (typeof addExtensions === "string") {
292
- const dotIndex = d.lastIndexOf(".");
293
- if (dotIndex === -1) return d;
294
- return d.substring(0, dotIndex) + addExtensions;
295
- }
296
- return addExtensions ? d : d.substring(0, d.lastIndexOf(".")) || d;
297
- }
287
+ if (typeof addExtensions === "string") {
288
+ const dotIndex = d.lastIndexOf(".");
289
+ if (dotIndex === -1) return d;
290
+ return d.substring(0, dotIndex) + addExtensions;
291
+ }
292
+ return addExtensions ? d : d.substring(0, d.lastIndexOf(".")) || d;
293
+ }
294
+ /**
295
+ * This function writes to a file if the content is different.
296
+ *
297
+ * @param filepath The path to the file
298
+ * @param content Original content
299
+ * @param incomingContent New content
300
+ * @param callbacks Callbacks to run before and after writing
301
+ * @returns Whether the file was written
302
+ */
298
303
  async function writeIfDifferent(filepath, content, incomingContent, callbacks) {
299
- if (content !== incomingContent) {
300
- callbacks?.beforeWrite?.();
301
- await fsp.writeFile(filepath, incomingContent);
302
- callbacks?.afterWrite?.();
303
- return true;
304
- }
305
- return false;
306
- }
304
+ if (content !== incomingContent) {
305
+ callbacks?.beforeWrite?.();
306
+ await fsp.writeFile(filepath, incomingContent);
307
+ callbacks?.afterWrite?.();
308
+ return true;
309
+ }
310
+ return false;
311
+ }
312
+ /**
313
+ * This function formats the source code using the default formatter (Prettier).
314
+ *
315
+ * @param source The content to format
316
+ * @param config The configuration object
317
+ * @returns The formatted content
318
+ */
307
319
  async function format(source, config) {
308
- const prettierOptions = {
309
- semi: config.semicolons,
310
- singleQuote: config.quoteStyle === "single",
311
- parser: "typescript"
312
- };
313
- return prettier.format(source, prettierOptions);
314
- }
320
+ const prettierOptions = {
321
+ semi: config.semicolons,
322
+ singleQuote: config.quoteStyle === "single",
323
+ parser: "typescript"
324
+ };
325
+ return prettier.format(source, prettierOptions);
326
+ }
327
+ /**
328
+ * This function resets the regex index to 0 so that it can be reused
329
+ * without having to create a new regex object or worry about the last
330
+ * state when using the global flag.
331
+ *
332
+ * @param regex The regex object to reset
333
+ * @returns
334
+ */
315
335
  function resetRegex(regex) {
316
- regex.lastIndex = 0;
317
- return;
318
- }
336
+ regex.lastIndex = 0;
337
+ }
338
+ /**
339
+ * This function checks if a file exists.
340
+ *
341
+ * @param file The path to the file
342
+ * @returns Whether the file exists
343
+ */
319
344
  async function checkFileExists(file) {
320
- try {
321
- await fsp.access(file, fsp.constants.F_OK);
322
- return true;
323
- } catch {
324
- return false;
325
- }
326
- }
327
- const possiblyNestedRouteGroupPatternRegex = /\([^/]+\)\/?/g;
345
+ try {
346
+ await fsp.access(file, fsp.constants.F_OK);
347
+ return true;
348
+ } catch {
349
+ return false;
350
+ }
351
+ }
352
+ var possiblyNestedRouteGroupPatternRegex = /\([^/]+\)\/?/g;
328
353
  function removeGroups(s) {
329
- return s.replace(possiblyNestedRouteGroupPatternRegex, "");
330
- }
354
+ return s.replace(possiblyNestedRouteGroupPatternRegex, "");
355
+ }
356
+ /**
357
+ * Removes all segments from a given path that start with an underscore ('_').
358
+ *
359
+ * @param {string} routePath - The path from which to remove segments. Defaults to '/'.
360
+ * @returns {string} The path with all underscore-prefixed segments removed.
361
+ * @example
362
+ * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'
363
+ */
331
364
  function removeLayoutSegments(routePath = "/") {
332
- const segments = routePath.split("/");
333
- const newSegments = segments.filter((segment) => !segment.startsWith("_"));
334
- return newSegments.join("/");
335
- }
365
+ return routePath.split("/").filter((segment) => !segment.startsWith("_")).join("/");
366
+ }
367
+ /**
368
+ * The `node.path` is used as the `id` in the route definition.
369
+ * This function checks if the given node has a parent and if so, it determines the correct path for the given node.
370
+ * @param node - The node to determine the path for.
371
+ * @returns The correct path for the given node.
372
+ */
336
373
  function determineNodePath(node) {
337
- return node.path = node.parent ? node.routePath?.replace(node.parent.routePath ?? "", "") || "/" : node.routePath;
338
- }
374
+ return node.path = node.parent ? node.routePath?.replace(node.parent.routePath ?? "", "") || "/" : node.routePath;
375
+ }
376
+ /**
377
+ * Removes the last segment from a given path. Segments are considered to be separated by a '/'.
378
+ *
379
+ * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.
380
+ * @returns {string} The path with the last segment removed.
381
+ * @example
382
+ * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'
383
+ */
339
384
  function removeLastSegmentFromPath(routePath = "/") {
340
- const segments = routePath.split("/");
341
- segments.pop();
342
- return segments.join("/");
385
+ const segments = routePath.split("/");
386
+ segments.pop();
387
+ return segments.join("/");
343
388
  }
389
+ /**
390
+ * Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).
391
+ */
344
392
  function hasParentRoute(prefixMap, node, routePathToCheck) {
345
- if (!routePathToCheck || routePathToCheck === "/") {
346
- return null;
347
- }
348
- return prefixMap.findParent(routePathToCheck);
349
- }
350
- const getResolvedRouteNodeVariableName = (routeNode) => {
351
- return routeNode.children?.length ? `${routeNode.variableName}RouteWithChildren` : `${routeNode.variableName}Route`;
393
+ if (!routePathToCheck || routePathToCheck === "/") return null;
394
+ return prefixMap.findParent(routePathToCheck);
395
+ }
396
+ /**
397
+ * Gets the final variable name for a route
398
+ */
399
+ var getResolvedRouteNodeVariableName = (routeNode) => {
400
+ return routeNode.children?.length ? `${routeNode.variableName}RouteWithChildren` : `${routeNode.variableName}Route`;
352
401
  };
402
+ /**
403
+ * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.
404
+ * Also asserts that the RouteNode is defined.
405
+ *
406
+ * @param routeNode - The RouteNode to check.
407
+ * @returns A boolean indicating whether the RouteNode is defined.
408
+ */
353
409
  function isRouteNodeValidForAugmentation(routeNode) {
354
- if (!routeNode || routeNode.isVirtual) {
355
- return false;
356
- }
357
- return true;
358
- }
359
- const inferPath = (routeNode) => {
360
- if (routeNode.cleanedPath === "/") {
361
- return routeNode.cleanedPath ?? "";
362
- }
363
- return routeNode.cleanedPath?.replace(/\/$/, "") ?? "";
410
+ if (!routeNode || routeNode.isVirtual) return false;
411
+ return true;
412
+ }
413
+ /**
414
+ * Infers the path for use by TS
415
+ */
416
+ var inferPath = (routeNode) => {
417
+ if (routeNode.cleanedPath === "/") return routeNode.cleanedPath ?? "";
418
+ return routeNode.cleanedPath?.replace(/\/$/, "") ?? "";
364
419
  };
365
- const inferFullPath = (routeNode) => {
366
- const fullPath = removeGroups(
367
- removeUnderscoresWithEscape(
368
- removeLayoutSegmentsWithEscape(
369
- routeNode.routePath,
370
- routeNode.originalRoutePath
371
- ),
372
- routeNode.originalRoutePath
373
- )
374
- );
375
- if (fullPath === "") {
376
- return "/";
377
- }
378
- const isIndexRoute = routeNode.routePath?.endsWith("/");
379
- if (isIndexRoute) {
380
- return fullPath;
381
- }
382
- return fullPath.replace(/\/$/, "");
420
+ /**
421
+ * Infers the full path for use by TS
422
+ */
423
+ var inferFullPath = (routeNode) => {
424
+ const fullPath = removeGroups(removeUnderscoresWithEscape(removeLayoutSegmentsWithEscape(routeNode.routePath, routeNode.originalRoutePath), routeNode.originalRoutePath));
425
+ if (fullPath === "") return "/";
426
+ if (routeNode.routePath?.endsWith("/")) return fullPath;
427
+ return fullPath.replace(/\/$/, "");
383
428
  };
384
- const shouldPreferIndexRoute = (current, existing) => {
385
- return existing.cleanedPath === "/" && current.cleanedPath !== "/";
429
+ var shouldPreferIndexRoute = (current, existing) => {
430
+ return existing.cleanedPath === "/" && current.cleanedPath !== "/";
386
431
  };
387
- const createRouteNodesByFullPath = (routeNodes) => {
388
- const map = /* @__PURE__ */ new Map();
389
- for (const routeNode of routeNodes) {
390
- const fullPath = inferFullPath(routeNode);
391
- if (fullPath === "/" && map.has("/")) {
392
- const existing = map.get("/");
393
- if (shouldPreferIndexRoute(routeNode, existing)) {
394
- continue;
395
- }
396
- }
397
- map.set(fullPath, routeNode);
398
- }
399
- return map;
432
+ /**
433
+ * Creates a map from fullPath to routeNode
434
+ */
435
+ var createRouteNodesByFullPath = (routeNodes) => {
436
+ const map = /* @__PURE__ */ new Map();
437
+ for (const routeNode of routeNodes) {
438
+ const fullPath = inferFullPath(routeNode);
439
+ if (fullPath === "/" && map.has("/")) {
440
+ if (shouldPreferIndexRoute(routeNode, map.get("/"))) continue;
441
+ }
442
+ map.set(fullPath, routeNode);
443
+ }
444
+ return map;
400
445
  };
401
- const createRouteNodesByTo = (routeNodes) => {
402
- const map = /* @__PURE__ */ new Map();
403
- for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {
404
- const to = inferTo(routeNode);
405
- if (to === "/" && map.has("/")) {
406
- const existing = map.get("/");
407
- if (shouldPreferIndexRoute(routeNode, existing)) {
408
- continue;
409
- }
410
- }
411
- map.set(to, routeNode);
412
- }
413
- return map;
446
+ /**
447
+ * Create a map from 'to' to a routeNode
448
+ */
449
+ var createRouteNodesByTo = (routeNodes) => {
450
+ const map = /* @__PURE__ */ new Map();
451
+ for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) {
452
+ const to = inferTo(routeNode);
453
+ if (to === "/" && map.has("/")) {
454
+ if (shouldPreferIndexRoute(routeNode, map.get("/"))) continue;
455
+ }
456
+ map.set(to, routeNode);
457
+ }
458
+ return map;
414
459
  };
415
- const createRouteNodesById = (routeNodes) => {
416
- return new Map(
417
- routeNodes.map((routeNode) => {
418
- const id = routeNode.routePath ?? "";
419
- return [id, routeNode];
420
- })
421
- );
460
+ /**
461
+ * Create a map from 'id' to a routeNode
462
+ */
463
+ var createRouteNodesById = (routeNodes) => {
464
+ return new Map(routeNodes.map((routeNode) => {
465
+ return [routeNode.routePath ?? "", routeNode];
466
+ }));
422
467
  };
423
- const inferTo = (routeNode) => {
424
- const fullPath = inferFullPath(routeNode);
425
- if (fullPath === "/") return fullPath;
426
- return fullPath.replace(/\/$/, "");
468
+ /**
469
+ * Infers to path
470
+ */
471
+ var inferTo = (routeNode) => {
472
+ const fullPath = inferFullPath(routeNode);
473
+ if (fullPath === "/") return fullPath;
474
+ return fullPath.replace(/\/$/, "");
427
475
  };
428
- const dedupeBranchesAndIndexRoutes = (routes) => {
429
- return routes.filter((route) => {
430
- if (route.children?.find((child) => child.cleanedPath === "/")) return false;
431
- return true;
432
- });
476
+ /**
477
+ * Dedupes branches and index routes
478
+ */
479
+ var dedupeBranchesAndIndexRoutes = (routes) => {
480
+ return routes.filter((route) => {
481
+ if (route.children?.find((child) => child.cleanedPath === "/")) return false;
482
+ return true;
483
+ });
433
484
  };
434
485
  function checkUnique(routes, key) {
435
- const keys = routes.map((d) => d[key]);
436
- const uniqueKeys = new Set(keys);
437
- if (keys.length !== uniqueKeys.size) {
438
- const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i);
439
- const conflictingFiles = routes.filter(
440
- (d) => duplicateKeys.includes(d[key])
441
- );
442
- return conflictingFiles;
443
- }
444
- return void 0;
486
+ const keys = routes.map((d) => d[key]);
487
+ const uniqueKeys = new Set(keys);
488
+ if (keys.length !== uniqueKeys.size) {
489
+ const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i);
490
+ return routes.filter((d) => duplicateKeys.includes(d[key]));
491
+ }
445
492
  }
446
493
  function checkRouteFullPathUniqueness(_routes, config) {
447
- const emptyPathRoutes = _routes.filter((d) => d.routePath === "");
448
- if (emptyPathRoutes.length) {
449
- const errorMessage = `Invalid route path "" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.
450
- Conflicting files:
451
- ${emptyPathRoutes.map((d) => path.resolve(config.routesDirectory, d.filePath)).join("\n ")}
452
- `;
453
- throw new Error(errorMessage);
454
- }
455
- const routes = _routes.map((d) => {
456
- const inferredFullPath = inferFullPath(d);
457
- return { ...d, inferredFullPath };
458
- });
459
- const conflictingFiles = checkUnique(routes, "inferredFullPath");
460
- if (conflictingFiles !== void 0) {
461
- const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? "s" : ""}: ${conflictingFiles.map((p) => `"${p.inferredFullPath}"`).join(", ")}.
494
+ const emptyPathRoutes = _routes.filter((d) => d.routePath === "");
495
+ if (emptyPathRoutes.length) {
496
+ const errorMessage = `Invalid route path "" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path.
497
+ Conflicting files: \n ${emptyPathRoutes.map((d) => path.resolve(config.routesDirectory, d.filePath)).join("\n ")}\n`;
498
+ throw new Error(errorMessage);
499
+ }
500
+ const conflictingFiles = checkUnique(_routes.map((d) => {
501
+ const inferredFullPath = inferFullPath(d);
502
+ return {
503
+ ...d,
504
+ inferredFullPath
505
+ };
506
+ }), "inferredFullPath");
507
+ if (conflictingFiles !== void 0) {
508
+ const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? "s" : ""}: ${conflictingFiles.map((p) => `"${p.inferredFullPath}"`).join(", ")}.
462
509
  Please ensure each Route has a unique full path.
463
- Conflicting files:
464
- ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join("\n ")}
465
- `;
466
- throw new Error(errorMessage);
467
- }
510
+ Conflicting files: \n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join("\n ")}\n`;
511
+ throw new Error(errorMessage);
512
+ }
468
513
  }
469
514
  function buildRouteTreeConfig(nodes, disableTypes, depth = 1) {
470
- const children = nodes.map((node) => {
471
- if (node._fsRouteType === "__root") {
472
- return;
473
- }
474
- if (node._fsRouteType === "pathless_layout" && !node.children?.length) {
475
- return;
476
- }
477
- const route = `${node.variableName}`;
478
- if (node.children?.length) {
479
- const childConfigs = buildRouteTreeConfig(
480
- node.children,
481
- disableTypes,
482
- depth + 1
483
- );
484
- const childrenDeclaration = disableTypes ? "" : `interface ${route}RouteChildren {
485
- ${node.children.map(
486
- (child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`
487
- ).join(",")}
515
+ return nodes.map((node) => {
516
+ if (node._fsRouteType === "__root") return;
517
+ if (node._fsRouteType === "pathless_layout" && !node.children?.length) return;
518
+ const route = `${node.variableName}`;
519
+ if (node.children?.length) {
520
+ const childConfigs = buildRouteTreeConfig(node.children, disableTypes, depth + 1);
521
+ const childrenDeclaration = disableTypes ? "" : `interface ${route}RouteChildren {
522
+ ${node.children.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(",")}
488
523
  }`;
489
- const children2 = `const ${route}RouteChildren${disableTypes ? "" : `: ${route}RouteChildren`} = {
490
- ${node.children.map(
491
- (child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`
492
- ).join(",")}
524
+ const children = `const ${route}RouteChildren${disableTypes ? "" : `: ${route}RouteChildren`} = {
525
+ ${node.children.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(",")}
493
526
  }`;
494
- const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`;
495
- return [
496
- childConfigs.join("\n"),
497
- childrenDeclaration,
498
- children2,
499
- routeWithChildren
500
- ].join("\n\n");
501
- }
502
- return void 0;
503
- });
504
- return children.filter((x) => x !== void 0);
527
+ const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`;
528
+ return [
529
+ childConfigs.join("\n"),
530
+ childrenDeclaration,
531
+ children,
532
+ routeWithChildren
533
+ ].join("\n\n");
534
+ }
535
+ }).filter((x) => x !== void 0);
505
536
  }
506
537
  function buildImportString(importDeclaration) {
507
- const { source, specifiers, importKind } = importDeclaration;
508
- return specifiers.length ? `import ${importKind === "type" ? "type " : ""}{ ${specifiers.map((s) => s.local ? `${s.imported} as ${s.local}` : s.imported).join(", ")} } from '${source}'` : "";
538
+ const { source, specifiers, importKind } = importDeclaration;
539
+ return specifiers.length ? `import ${importKind === "type" ? "type " : ""}{ ${specifiers.map((s) => s.local ? `${s.imported} as ${s.local}` : s.imported).join(", ")} } from '${source}'` : "";
509
540
  }
510
541
  function mergeImportDeclarations(imports) {
511
- const merged = /* @__PURE__ */ new Map();
512
- for (const imp of imports) {
513
- const key = `${imp.source}-${imp.importKind ?? ""}`;
514
- let existing = merged.get(key);
515
- if (!existing) {
516
- existing = { ...imp, specifiers: [] };
517
- merged.set(key, existing);
518
- }
519
- const existingSpecs = existing.specifiers;
520
- for (const specifier of imp.specifiers) {
521
- let found = false;
522
- for (let i = 0; i < existingSpecs.length; i++) {
523
- const e = existingSpecs[i];
524
- if (e.imported === specifier.imported && e.local === specifier.local) {
525
- found = true;
526
- break;
527
- }
528
- }
529
- if (!found) {
530
- existingSpecs.push(specifier);
531
- }
532
- }
533
- }
534
- return [...merged.values()];
535
- }
536
- const findParent = (node) => {
537
- if (!node) {
538
- return `rootRouteImport`;
539
- }
540
- if (node.parent) {
541
- return `${node.parent.variableName}Route`;
542
- }
543
- return findParent(node.parent);
542
+ const merged = /* @__PURE__ */ new Map();
543
+ for (const imp of imports) {
544
+ const key = `${imp.source}-${imp.importKind ?? ""}`;
545
+ let existing = merged.get(key);
546
+ if (!existing) {
547
+ existing = {
548
+ ...imp,
549
+ specifiers: []
550
+ };
551
+ merged.set(key, existing);
552
+ }
553
+ const existingSpecs = existing.specifiers;
554
+ for (const specifier of imp.specifiers) {
555
+ let found = false;
556
+ for (let i = 0; i < existingSpecs.length; i++) {
557
+ const e = existingSpecs[i];
558
+ if (e.imported === specifier.imported && e.local === specifier.local) {
559
+ found = true;
560
+ break;
561
+ }
562
+ }
563
+ if (!found) existingSpecs.push(specifier);
564
+ }
565
+ }
566
+ return [...merged.values()];
567
+ }
568
+ var findParent = (node) => {
569
+ if (!node) return `rootRouteImport`;
570
+ if (node.parent) return `${node.parent.variableName}Route`;
571
+ return findParent(node.parent);
544
572
  };
545
573
  function buildFileRoutesByPathInterface(opts) {
546
- return `declare module '${opts.module}' {
574
+ return `declare module '${opts.module}' {
547
575
  interface ${opts.interfaceName} {
548
576
  ${opts.routeNodes.map((routeNode) => {
549
- const filePathId = routeNode.routePath;
550
- const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`;
551
- const parent = findParent(routeNode);
552
- return `'${filePathId}': {
577
+ const filePathId = routeNode.routePath;
578
+ const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`;
579
+ const parent = findParent(routeNode);
580
+ return `'${filePathId}': {
553
581
  id: '${filePathId}'
554
582
  path: '${inferPath(routeNode)}'
555
583
  fullPath: '${inferFullPath(routeNode)}'
556
584
  preLoaderRoute: ${preloaderRoute}
557
585
  parentRoute: typeof ${parent}
558
586
  }`;
559
- }).join("\n")}
587
+ }).join("\n")}
560
588
  }
561
589
  }`;
562
590
  }
563
591
  function getImportPath(node, config, generatedRouteTreePath) {
564
- return replaceBackslash(
565
- removeExt(
566
- path.relative(
567
- path.dirname(generatedRouteTreePath),
568
- path.resolve(config.routesDirectory, node.filePath)
569
- ),
570
- config.addExtensions
571
- )
572
- );
592
+ return replaceBackslash(removeExt(path.relative(path.dirname(generatedRouteTreePath), path.resolve(config.routesDirectory, node.filePath)), config.addExtensions));
573
593
  }
574
594
  function getImportForRouteNode(node, config, generatedRouteTreePath, root) {
575
- let source = "";
576
- if (config.importRoutesUsingAbsolutePaths) {
577
- source = replaceBackslash(
578
- removeExt(
579
- path.resolve(root, config.routesDirectory, node.filePath),
580
- config.addExtensions
581
- )
582
- );
583
- } else {
584
- source = `./${getImportPath(node, config, generatedRouteTreePath)}`;
585
- }
586
- return {
587
- source,
588
- specifiers: [
589
- {
590
- imported: "Route",
591
- local: `${node.variableName}RouteImport`
592
- }
593
- ]
594
- };
595
- }
596
- export {
597
- RoutePrefixMap,
598
- buildFileRoutesByPathInterface,
599
- buildImportString,
600
- buildRouteTreeConfig,
601
- capitalize,
602
- checkFileExists,
603
- checkRouteFullPathUniqueness,
604
- cleanPath,
605
- createRouteNodesByFullPath,
606
- createRouteNodesById,
607
- createRouteNodesByTo,
608
- createTokenRegex,
609
- dedupeBranchesAndIndexRoutes,
610
- determineInitialRoutePath,
611
- determineNodePath,
612
- findParent,
613
- format,
614
- getImportForRouteNode,
615
- getImportPath,
616
- getResolvedRouteNodeVariableName,
617
- hasEscapedLeadingUnderscore,
618
- hasEscapedTrailingUnderscore,
619
- hasParentRoute,
620
- inferFullPath,
621
- inferPath,
622
- inferTo,
623
- isBracketWrappedSegment,
624
- isRouteNodeValidForAugmentation,
625
- isSegmentPathless,
626
- mergeImportDeclarations,
627
- multiSortBy,
628
- removeExt,
629
- removeGroups,
630
- removeLastSegmentFromPath,
631
- removeLayoutSegments,
632
- removeLayoutSegmentsWithEscape,
633
- removeLeadingSlash,
634
- removeTrailingSlash,
635
- removeUnderscores,
636
- removeUnderscoresWithEscape,
637
- replaceBackslash,
638
- resetRegex,
639
- routePathToVariable,
640
- trimPathLeft,
641
- unwrapBracketWrappedSegment,
642
- writeIfDifferent
643
- };
644
- //# sourceMappingURL=utils.js.map
595
+ let source = "";
596
+ if (config.importRoutesUsingAbsolutePaths) source = replaceBackslash(removeExt(path.resolve(root, config.routesDirectory, node.filePath), config.addExtensions));
597
+ else source = `./${getImportPath(node, config, generatedRouteTreePath)}`;
598
+ return {
599
+ source,
600
+ specifiers: [{
601
+ imported: "Route",
602
+ local: `${node.variableName}RouteImport`
603
+ }]
604
+ };
605
+ }
606
+ //#endregion
607
+ export { RoutePrefixMap, buildFileRoutesByPathInterface, buildImportString, buildRouteTreeConfig, capitalize, checkFileExists, checkRouteFullPathUniqueness, cleanPath, createRouteNodesByFullPath, createRouteNodesById, createRouteNodesByTo, createTokenRegex, determineInitialRoutePath, determineNodePath, findParent, format, getImportForRouteNode, getImportPath, getResolvedRouteNodeVariableName, hasEscapedLeadingUnderscore, hasParentRoute, inferFullPath, isRouteNodeValidForAugmentation, isSegmentPathless, mergeImportDeclarations, multiSortBy, removeExt, removeGroups, removeLastSegmentFromPath, removeLayoutSegmentsWithEscape, removeLeadingSlash, removeTrailingSlash, removeUnderscores, removeUnderscoresWithEscape, replaceBackslash, resetRegex, routePathToVariable, trimPathLeft, unwrapBracketWrappedSegment, writeIfDifferent };
608
+
609
+ //# sourceMappingURL=utils.js.map