@tanstack/router-generator 1.133.20 → 1.133.27
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.
- package/dist/cjs/config.cjs +3 -1
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.cts +11 -6
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs +9 -2
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +1 -1
- package/dist/cjs/generator.cjs +24 -12
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/types.d.cts +2 -0
- package/dist/cjs/utils.cjs +112 -16
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +20 -6
- package/dist/esm/config.d.ts +11 -6
- package/dist/esm/config.js +3 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.d.ts +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.js +9 -2
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
- package/dist/esm/generator.js +25 -13
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/types.d.ts +2 -0
- package/dist/esm/utils.d.ts +20 -6
- package/dist/esm/utils.js +112 -16
- package/dist/esm/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/config.ts +2 -0
- package/src/filesystem/physical/getRouteNodes.ts +10 -1
- package/src/generator.ts +32 -9
- package/src/types.ts +2 -0
- package/src/utils.ts +205 -13
package/dist/esm/utils.js
CHANGED
|
@@ -34,7 +34,8 @@ function removeTrailingSlash(s) {
|
|
|
34
34
|
return s.replace(/\/$/, "");
|
|
35
35
|
}
|
|
36
36
|
const BRACKET_CONTENT_RE = /\[(.*?)\]/g;
|
|
37
|
-
|
|
37
|
+
const SPLIT_REGEX = new RegExp("(?<!\\[)\\.(?!\\])", "g");
|
|
38
|
+
function determineInitialRoutePath(routePath, config) {
|
|
38
39
|
const DISALLOWED_ESCAPE_CHARS = /* @__PURE__ */ new Set([
|
|
39
40
|
"/",
|
|
40
41
|
"\\",
|
|
@@ -49,7 +50,23 @@ function determineInitialRoutePath(routePath) {
|
|
|
49
50
|
"$",
|
|
50
51
|
"%"
|
|
51
52
|
]);
|
|
52
|
-
const
|
|
53
|
+
const originalRoutePath = cleanPath(
|
|
54
|
+
`/${(cleanPath(routePath) || "").split(SPLIT_REGEX).join("/")}`
|
|
55
|
+
) || "";
|
|
56
|
+
const isExperimentalNonNestedRoute = isValidNonNestedRoute(
|
|
57
|
+
originalRoutePath,
|
|
58
|
+
config
|
|
59
|
+
);
|
|
60
|
+
let cleanedRoutePath = routePath;
|
|
61
|
+
if (config?.experimental?.nonNestedRoutes) {
|
|
62
|
+
if (originalRoutePath !== `/${rootPathId}`) {
|
|
63
|
+
cleanedRoutePath = removeTrailingUnderscores(
|
|
64
|
+
originalRoutePath,
|
|
65
|
+
config.routeToken
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const parts = cleanedRoutePath.split(SPLIT_REGEX);
|
|
53
70
|
const escapedParts = parts.map((part) => {
|
|
54
71
|
let match;
|
|
55
72
|
while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {
|
|
@@ -69,7 +86,11 @@ Please remove and/or replace them.`
|
|
|
69
86
|
return part.replace(BRACKET_CONTENT_RE, "$1");
|
|
70
87
|
});
|
|
71
88
|
const final = cleanPath(`/${escapedParts.join("/")}`) || "";
|
|
72
|
-
return
|
|
89
|
+
return {
|
|
90
|
+
routePath: final,
|
|
91
|
+
isExperimentalNonNestedRoute,
|
|
92
|
+
originalRoutePath
|
|
93
|
+
};
|
|
73
94
|
}
|
|
74
95
|
function replaceBackslash(s) {
|
|
75
96
|
return s.replaceAll(/\\/gi, "/");
|
|
@@ -104,6 +125,25 @@ function routePathToVariable(routePath) {
|
|
|
104
125
|
function removeUnderscores(s) {
|
|
105
126
|
return s?.replaceAll(/(^_|_$)/gi, "").replaceAll(/(\/_|_\/)/gi, "/");
|
|
106
127
|
}
|
|
128
|
+
function escapeRegExp(s) {
|
|
129
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
130
|
+
}
|
|
131
|
+
function removeLeadingUnderscores(s, routeToken) {
|
|
132
|
+
if (!s) return s;
|
|
133
|
+
const hasLeadingUnderscore = routeToken[0] === "_";
|
|
134
|
+
const routeTokenToExclude = hasLeadingUnderscore ? routeToken.slice(1) : routeToken;
|
|
135
|
+
const escapedRouteToken = escapeRegExp(routeTokenToExclude);
|
|
136
|
+
const leadingUnderscoreRegex = hasLeadingUnderscore ? new RegExp(`(?<=^|\\/)_(?!${escapedRouteToken})`, "g") : new RegExp(`(?<=^|\\/)_`, "g");
|
|
137
|
+
return s.replaceAll(leadingUnderscoreRegex, "");
|
|
138
|
+
}
|
|
139
|
+
function removeTrailingUnderscores(s, routeToken) {
|
|
140
|
+
if (!s) return s;
|
|
141
|
+
const hasTrailingUnderscore = routeToken.slice(-1) === "_";
|
|
142
|
+
const routeTokenToExclude = hasTrailingUnderscore ? routeToken.slice(0, -1) : routeToken;
|
|
143
|
+
const escapedRouteToken = escapeRegExp(routeTokenToExclude);
|
|
144
|
+
const trailingUnderscoreRegex = hasTrailingUnderscore ? new RegExp(`(?<!${escapedRouteToken})_(?=\\/|$)`, "g") : new RegExp(`_(?=\\/)|_$`, "g");
|
|
145
|
+
return s.replaceAll(trailingUnderscoreRegex, "");
|
|
146
|
+
}
|
|
107
147
|
function capitalize(s) {
|
|
108
148
|
if (typeof s !== "string") return "";
|
|
109
149
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
@@ -157,7 +197,16 @@ function removeLastSegmentFromPath(routePath = "/") {
|
|
|
157
197
|
segments.pop();
|
|
158
198
|
return segments.join("/");
|
|
159
199
|
}
|
|
160
|
-
function hasParentRoute(routes, node, routePathToCheck) {
|
|
200
|
+
function hasParentRoute(routes, node, routePathToCheck, originalRoutePathToCheck) {
|
|
201
|
+
const getNonNestedSegments = (routePath) => {
|
|
202
|
+
const regex = /_(?=\/|$)/g;
|
|
203
|
+
return [...routePath.matchAll(regex)].filter((match) => {
|
|
204
|
+
const beforeStr = routePath.substring(0, match.index);
|
|
205
|
+
const openBrackets = (beforeStr.match(/\[/g) || []).length;
|
|
206
|
+
const closeBrackets = (beforeStr.match(/\]/g) || []).length;
|
|
207
|
+
return openBrackets === closeBrackets;
|
|
208
|
+
}).map((match) => routePath.substring(0, match.index + 1)).reverse();
|
|
209
|
+
};
|
|
161
210
|
if (!routePathToCheck || routePathToCheck === "/") {
|
|
162
211
|
return null;
|
|
163
212
|
}
|
|
@@ -165,7 +214,23 @@ function hasParentRoute(routes, node, routePathToCheck) {
|
|
|
165
214
|
(d) => d.routePath.length * -1,
|
|
166
215
|
(d) => d.variableName
|
|
167
216
|
]).filter((d) => d.routePath !== `/${rootPathId}`);
|
|
168
|
-
|
|
217
|
+
const filteredNodes = node._isExperimentalNonNestedRoute ? [] : [...sortedNodes];
|
|
218
|
+
if (node._isExperimentalNonNestedRoute) {
|
|
219
|
+
const nonNestedSegments = getNonNestedSegments(
|
|
220
|
+
originalRoutePathToCheck ?? ""
|
|
221
|
+
);
|
|
222
|
+
for (const route of sortedNodes) {
|
|
223
|
+
if (route.routePath === "/") continue;
|
|
224
|
+
if (routePathToCheck.startsWith(`${route.routePath}/`) && route._isExperimentalNonNestedRoute && route.routePath !== routePathToCheck) {
|
|
225
|
+
return route;
|
|
226
|
+
}
|
|
227
|
+
if (nonNestedSegments.find((seg) => seg === `${route.routePath}_`) || !(route._fsRouteType === "pathless_layout" || route._fsRouteType === "layout" || route._fsRouteType === "__root")) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
filteredNodes.push(route);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const route of filteredNodes) {
|
|
169
234
|
if (route.routePath === "/") continue;
|
|
170
235
|
if (routePathToCheck.startsWith(`${route.routePath}/`) && route.routePath !== routePathToCheck) {
|
|
171
236
|
return route;
|
|
@@ -174,7 +239,7 @@ function hasParentRoute(routes, node, routePathToCheck) {
|
|
|
174
239
|
const segments = routePathToCheck.split("/");
|
|
175
240
|
segments.pop();
|
|
176
241
|
const parentRoutePath = segments.join("/");
|
|
177
|
-
return hasParentRoute(routes, node, parentRoutePath);
|
|
242
|
+
return hasParentRoute(routes, node, parentRoutePath, originalRoutePathToCheck);
|
|
178
243
|
}
|
|
179
244
|
const getResolvedRouteNodeVariableName = (routeNode) => {
|
|
180
245
|
return routeNode.children?.length ? `${routeNode.variableName}RouteWithChildren` : `${routeNode.variableName}Route`;
|
|
@@ -188,21 +253,24 @@ function isRouteNodeValidForAugmentation(routeNode) {
|
|
|
188
253
|
const inferPath = (routeNode) => {
|
|
189
254
|
return routeNode.cleanedPath === "/" ? routeNode.cleanedPath : routeNode.cleanedPath?.replace(/\/$/, "") ?? "";
|
|
190
255
|
};
|
|
191
|
-
const inferFullPath = (routeNode) => {
|
|
256
|
+
const inferFullPath = (routeNode, config) => {
|
|
192
257
|
const fullPath = removeGroups(
|
|
193
|
-
removeUnderscores(removeLayoutSegments(routeNode.routePath)) ?? ""
|
|
258
|
+
(config?.experimental?.nonNestedRoutes ? removeLayoutSegments(routeNode.routePath) : removeUnderscores(removeLayoutSegments(routeNode.routePath))) ?? ""
|
|
194
259
|
);
|
|
195
260
|
return routeNode.cleanedPath === "/" ? fullPath : fullPath.replace(/\/$/, "");
|
|
196
261
|
};
|
|
197
|
-
const createRouteNodesByFullPath = (routeNodes) => {
|
|
262
|
+
const createRouteNodesByFullPath = (routeNodes, config) => {
|
|
198
263
|
return new Map(
|
|
199
|
-
routeNodes.map((routeNode) => [
|
|
264
|
+
routeNodes.map((routeNode) => [
|
|
265
|
+
inferFullPath(routeNode, config),
|
|
266
|
+
routeNode
|
|
267
|
+
])
|
|
200
268
|
);
|
|
201
269
|
};
|
|
202
|
-
const createRouteNodesByTo = (routeNodes) => {
|
|
270
|
+
const createRouteNodesByTo = (routeNodes, config) => {
|
|
203
271
|
return new Map(
|
|
204
272
|
dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [
|
|
205
|
-
inferTo(routeNode),
|
|
273
|
+
inferTo(routeNode, config),
|
|
206
274
|
routeNode
|
|
207
275
|
])
|
|
208
276
|
);
|
|
@@ -215,8 +283,8 @@ const createRouteNodesById = (routeNodes) => {
|
|
|
215
283
|
})
|
|
216
284
|
);
|
|
217
285
|
};
|
|
218
|
-
const inferTo = (routeNode) => {
|
|
219
|
-
const fullPath = inferFullPath(routeNode);
|
|
286
|
+
const inferTo = (routeNode, config) => {
|
|
287
|
+
const fullPath = inferFullPath(routeNode, config);
|
|
220
288
|
if (fullPath === "/") return fullPath;
|
|
221
289
|
return fullPath.replace(/\/$/, "");
|
|
222
290
|
};
|
|
@@ -240,7 +308,7 @@ function checkUnique(routes, key) {
|
|
|
240
308
|
}
|
|
241
309
|
function checkRouteFullPathUniqueness(_routes, config) {
|
|
242
310
|
const routes = _routes.map((d) => {
|
|
243
|
-
const inferredFullPath = inferFullPath(d);
|
|
311
|
+
const inferredFullPath = inferFullPath(d, config);
|
|
244
312
|
return { ...d, inferredFullPath };
|
|
245
313
|
});
|
|
246
314
|
const conflictingFiles = checkUnique(routes, "inferredFullPath");
|
|
@@ -334,7 +402,7 @@ function buildFileRoutesByPathInterface(opts) {
|
|
|
334
402
|
return `'${filePathId}': {
|
|
335
403
|
id: '${filePathId}'
|
|
336
404
|
path: '${inferPath(routeNode)}'
|
|
337
|
-
fullPath: '${inferFullPath(routeNode)}'
|
|
405
|
+
fullPath: '${inferFullPath(routeNode, opts.config)}'
|
|
338
406
|
preLoaderRoute: ${preloaderRoute}
|
|
339
407
|
parentRoute: typeof ${parent}
|
|
340
408
|
}`;
|
|
@@ -375,6 +443,31 @@ function getImportForRouteNode(node, config, generatedRouteTreePath, root) {
|
|
|
375
443
|
]
|
|
376
444
|
};
|
|
377
445
|
}
|
|
446
|
+
function isValidNonNestedRoute(normalizedRoutePath, config) {
|
|
447
|
+
if (!config?.experimental?.nonNestedRoutes) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const segments = normalizedRoutePath.split("/").filter(Boolean);
|
|
451
|
+
if (segments.length === 0) {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
const lastRouteSegment = segments[segments.length - 1];
|
|
455
|
+
if (lastRouteSegment === rootPathId) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
if (lastRouteSegment !== config.indexToken && lastRouteSegment !== config.routeToken && lastRouteSegment.endsWith("_")) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
for (const segment of segments.slice(0, -1).reverse()) {
|
|
462
|
+
if (segment === config.routeToken) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
if (segment.endsWith("_")) {
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
378
471
|
export {
|
|
379
472
|
buildFileRoutesByPathInterface,
|
|
380
473
|
buildImportString,
|
|
@@ -399,6 +492,7 @@ export {
|
|
|
399
492
|
inferPath,
|
|
400
493
|
inferTo,
|
|
401
494
|
isRouteNodeValidForAugmentation,
|
|
495
|
+
isValidNonNestedRoute,
|
|
402
496
|
mergeImportDeclarations,
|
|
403
497
|
multiSortBy,
|
|
404
498
|
removeExt,
|
|
@@ -406,7 +500,9 @@ export {
|
|
|
406
500
|
removeLastSegmentFromPath,
|
|
407
501
|
removeLayoutSegments,
|
|
408
502
|
removeLeadingSlash,
|
|
503
|
+
removeLeadingUnderscores,
|
|
409
504
|
removeTrailingSlash,
|
|
505
|
+
removeTrailingUnderscores,
|
|
410
506
|
removeUnderscores,
|
|
411
507
|
replaceBackslash,
|
|
412
508
|
resetRegex,
|
package/dist/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n return arr\n .map((d, i) => [d, i] as const)\n .sort(([a, ai], [b, bi]) => {\n for (const accessor of accessors) {\n const ao = accessor(a)\n const bo = accessor(b)\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return ai - bi\n })\n .map(([d]) => d)\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\n\nexport function determineInitialRoutePath(routePath: string) {\n const DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n ])\n\n const parts = routePath.split(/(?<!\\[)\\.(?!\\])/g)\n\n // Escape any characters that in square brackets\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `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(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return final\n}\n\nexport function replaceBackslash(s: string) {\n return s.replaceAll(/\\\\/gi, '/')\n}\n\nexport function routePathToVariable(routePath: string): string {\n const toVariableSafeChar = (char: string): string => {\n if (/[a-zA-Z0-9_]/.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n }\n\n return (\n removeUnderscores(routePath)\n ?.replace(/\\/\\$\\//g, '/splat/')\n .replace(/\\$$/g, 'splat')\n .replace(/\\$\\{\\$\\}/g, 'splat')\n .replace(/\\$/g, '')\n .split(/[/-]/g)\n .map((d, i) => (i > 0 ? capitalize(d) : d))\n .join('')\n .split('')\n .map(toVariableSafeChar)\n .join('')\n // .replace(/([^a-zA-Z0-9]|[.])/gm, '')\n .replace(/^(\\d)/g, 'R$1') ?? ''\n )\n}\n\nexport function removeUnderscores(s?: string) {\n return s?.replaceAll(/(^_|_$)/gi, '').replaceAll(/(\\/_|_\\/)/gi, '/')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, keepExtension: boolean = false) {\n return keepExtension ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\nexport function hasParentRoute(\n routes: Array<RouteNode>,\n node: RouteNode,\n routePathToCheck: string | undefined,\n): RouteNode | null {\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n const sortedNodes = multiSortBy(routes, [\n (d) => d.routePath!.length * -1,\n (d) => d.variableName,\n ]).filter((d) => d.routePath !== `/${rootPathId}`)\n\n for (const route of sortedNodes) {\n if (route.routePath === '/') continue\n\n if (\n routePathToCheck.startsWith(`${route.routePath}/`) &&\n route.routePath !== routePathToCheck\n ) {\n return route\n }\n }\n\n const segments = routePathToCheck.split('/')\n segments.pop() // Remove the last segment\n const parentRoutePath = segments.join('/')\n\n return hasParentRoute(routes, node, parentRoutePath)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.\n * Also asserts that the RouteNode is defined.\n *\n * @param routeNode - The RouteNode to check.\n * @returns A boolean indicating whether the RouteNode is defined.\n */\nexport function isRouteNodeValidForAugmentation(\n routeNode?: RouteNode,\n): routeNode is RouteNode {\n if (!routeNode || routeNode.isVirtual) {\n return false\n }\n return true\n}\n\n/**\n * Infers the path for use by TS\n */\nexport const inferPath = (routeNode: RouteNode): string => {\n return routeNode.cleanedPath === '/'\n ? routeNode.cleanedPath\n : (routeNode.cleanedPath?.replace(/\\/$/, '') ?? '')\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (routeNode: RouteNode): string => {\n const fullPath = removeGroups(\n removeUnderscores(removeLayoutSegments(routeNode.routePath)) ?? '',\n )\n\n return routeNode.cleanedPath === '/' ? fullPath : fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => [inferFullPath(routeNode), routeNode]),\n )\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [\n inferTo(routeNode),\n routeNode,\n ]),\n )\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nexport const inferTo = (routeNode: RouteNode): string => {\n const fullPath = inferFullPath(routeNode)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nexport const dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function lowerCaseFirstChar(value: string) {\n if (!value[0]) {\n return value\n }\n\n return value[0].toLowerCase() + value.slice(1)\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged: Record<string, ImportDeclaration> = {}\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind}`\n if (!merged[key]) {\n merged[key] = { ...imp, specifiers: [] }\n }\n for (const specifier of imp.specifiers) {\n // check if the specifier already exists in the merged import\n if (\n !merged[key].specifiers.some(\n (existing) =>\n existing.imported === specifier.imported &&\n existing.local === specifier.local,\n )\n ) {\n merged[key].specifiers.push(specifier)\n }\n }\n }\n\n return Object.values(merged)\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n if (node.isVirtualParentRequired) {\n return `${node.parent.variableName}Route`\n } else {\n return `${node.parent.variableName}Route`\n }\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nexport function getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n"],"names":["path","children"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import * as fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport * as prettier from 'prettier'\nimport { rootPathId } from './filesystem/physical/rootPathId'\nimport type { Config } from './config'\nimport type { ImportDeclaration, RouteNode } from './types'\n\nexport function multiSortBy<T>(\n arr: Array<T>,\n accessors: Array<(item: T) => any> = [(d) => d],\n): Array<T> {\n return arr\n .map((d, i) => [d, i] as const)\n .sort(([a, ai], [b, bi]) => {\n for (const accessor of accessors) {\n const ao = accessor(a)\n const bo = accessor(b)\n\n if (typeof ao === 'undefined') {\n if (typeof bo === 'undefined') {\n continue\n }\n return 1\n }\n\n if (ao === bo) {\n continue\n }\n\n return ao > bo ? 1 : -1\n }\n\n return ai - bi\n })\n .map(([d]) => d)\n}\n\nexport function cleanPath(path: string) {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/')\n}\n\nexport function trimPathLeft(path: string) {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '')\n}\n\nexport function removeLeadingSlash(path: string): string {\n return path.replace(/^\\//, '')\n}\n\nexport function removeTrailingSlash(s: string) {\n return s.replace(/\\/$/, '')\n}\n\nconst BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\nconst SPLIT_REGEX = /(?<!\\[)\\.(?!\\])/g\n\nexport function determineInitialRoutePath(\n routePath: string,\n config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>,\n) {\n const DISALLOWED_ESCAPE_CHARS = new Set([\n '/',\n '\\\\',\n '?',\n '#',\n ':',\n '*',\n '<',\n '>',\n '|',\n '!',\n '$',\n '%',\n ])\n\n const originalRoutePath =\n cleanPath(\n `/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,\n ) || ''\n\n // check if the route path is a valid non-nested path,\n // TODO with new major rename to reflect not experimental anymore\n const isExperimentalNonNestedRoute = isValidNonNestedRoute(\n originalRoutePath,\n config,\n )\n\n let cleanedRoutePath = routePath\n\n // we already identified the path as non-nested and can now remove the trailing underscores\n // we need to do this now before we encounter any escaped trailing underscores\n // this way we can be sure any remaining trailing underscores should remain\n // TODO with new major we can remove check and always remove leading underscores\n if (config?.experimental?.nonNestedRoutes) {\n // we should leave trailing underscores if the route path is the root path\n if (originalRoutePath !== `/${rootPathId}`) {\n // remove trailing underscores on various path segments\n cleanedRoutePath = removeTrailingUnderscores(\n originalRoutePath,\n config.routeToken,\n )\n }\n }\n\n const parts = cleanedRoutePath.split(SPLIT_REGEX)\n\n // Escape any characters that in square brackets\n // we keep the original path untouched\n const escapedParts = parts.map((part) => {\n // Check if any disallowed characters are used in brackets\n\n let match\n while ((match = BRACKET_CONTENT_RE.exec(part)) !== null) {\n const character = match[1]\n if (character === undefined) continue\n if (DISALLOWED_ESCAPE_CHARS.has(character)) {\n console.error(\n `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(\n DISALLOWED_ESCAPE_CHARS,\n ).join(', ')}\\nPlease remove and/or replace them.`,\n )\n process.exit(1)\n }\n }\n\n // Since this split segment is safe at this point, we can\n // remove the brackets and replace them with the content inside\n return part.replace(BRACKET_CONTENT_RE, '$1')\n })\n\n // If the syntax for prefix/suffix is different, from the path\n // matching internals of router-core, we'd perform those changes here\n // on the `escapedParts` array before it is joined back together in\n // `final`\n\n const final = cleanPath(`/${escapedParts.join('/')}`) || ''\n\n return {\n routePath: final,\n isExperimentalNonNestedRoute,\n originalRoutePath,\n }\n}\n\nexport function replaceBackslash(s: string) {\n return s.replaceAll(/\\\\/gi, '/')\n}\n\nexport function routePathToVariable(routePath: string): string {\n const toVariableSafeChar = (char: string): string => {\n if (/[a-zA-Z0-9_]/.test(char)) {\n return char // Keep alphanumeric characters and underscores as is\n }\n\n // Replace special characters with meaningful text equivalents\n switch (char) {\n case '.':\n return 'Dot'\n case '-':\n return 'Dash'\n case '@':\n return 'At'\n case '(':\n return '' // Removed since route groups use parentheses\n case ')':\n return '' // Removed since route groups use parentheses\n case ' ':\n return '' // Remove spaces\n default:\n return `Char${char.charCodeAt(0)}` // For any other characters\n }\n }\n\n return (\n removeUnderscores(routePath)\n ?.replace(/\\/\\$\\//g, '/splat/')\n .replace(/\\$$/g, 'splat')\n .replace(/\\$\\{\\$\\}/g, 'splat')\n .replace(/\\$/g, '')\n .split(/[/-]/g)\n .map((d, i) => (i > 0 ? capitalize(d) : d))\n .join('')\n .split('')\n .map(toVariableSafeChar)\n .join('')\n // .replace(/([^a-zA-Z0-9]|[.])/gm, '')\n .replace(/^(\\d)/g, 'R$1') ?? ''\n )\n}\n\nexport function removeUnderscores(s?: string) {\n return s?.replaceAll(/(^_|_$)/gi, '').replaceAll(/(\\/_|_\\/)/gi, '/')\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nexport function removeLeadingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasLeadingUnderscore = routeToken[0] === '_'\n\n const routeTokenToExclude = hasLeadingUnderscore\n ? routeToken.slice(1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const leadingUnderscoreRegex = hasLeadingUnderscore\n ? new RegExp(`(?<=^|\\\\/)_(?!${escapedRouteToken})`, 'g')\n : new RegExp(`(?<=^|\\\\/)_`, 'g')\n\n return s.replaceAll(leadingUnderscoreRegex, '')\n}\n\nexport function removeTrailingUnderscores(s: string, routeToken: string) {\n if (!s) return s\n\n const hasTrailingUnderscore = routeToken.slice(-1) === '_'\n\n const routeTokenToExclude = hasTrailingUnderscore\n ? routeToken.slice(0, -1)\n : routeToken\n\n const escapedRouteToken = escapeRegExp(routeTokenToExclude)\n\n const trailingUnderscoreRegex = hasTrailingUnderscore\n ? new RegExp(`(?<!${escapedRouteToken})_(?=\\\\/|$)`, 'g')\n : new RegExp(`_(?=\\\\/)|_$`, 'g')\n\n return s.replaceAll(trailingUnderscoreRegex, '')\n}\n\nexport function capitalize(s: string) {\n if (typeof s !== 'string') return ''\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n\nexport function removeExt(d: string, keepExtension: boolean = false) {\n return keepExtension ? d : d.substring(0, d.lastIndexOf('.')) || d\n}\n\n/**\n * This function writes to a file if the content is different.\n *\n * @param filepath The path to the file\n * @param content Original content\n * @param incomingContent New content\n * @param callbacks Callbacks to run before and after writing\n * @returns Whether the file was written\n */\nexport async function writeIfDifferent(\n filepath: string,\n content: string,\n incomingContent: string,\n callbacks?: { beforeWrite?: () => void; afterWrite?: () => void },\n): Promise<boolean> {\n if (content !== incomingContent) {\n callbacks?.beforeWrite?.()\n await fsp.writeFile(filepath, incomingContent)\n callbacks?.afterWrite?.()\n return true\n }\n return false\n}\n\n/**\n * This function formats the source code using the default formatter (Prettier).\n *\n * @param source The content to format\n * @param config The configuration object\n * @returns The formatted content\n */\nexport async function format(\n source: string,\n config: {\n quoteStyle: 'single' | 'double'\n semicolons: boolean\n },\n): Promise<string> {\n const prettierOptions: prettier.Config = {\n semi: config.semicolons,\n singleQuote: config.quoteStyle === 'single',\n parser: 'typescript',\n }\n return prettier.format(source, prettierOptions)\n}\n\n/**\n * This function resets the regex index to 0 so that it can be reused\n * without having to create a new regex object or worry about the last\n * state when using the global flag.\n *\n * @param regex The regex object to reset\n * @returns\n */\nexport function resetRegex(regex: RegExp) {\n regex.lastIndex = 0\n return\n}\n\n/**\n * This function checks if a file exists.\n *\n * @param file The path to the file\n * @returns Whether the file exists\n */\nexport async function checkFileExists(file: string) {\n try {\n await fsp.access(file, fsp.constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nconst possiblyNestedRouteGroupPatternRegex = /\\([^/]+\\)\\/?/g\nexport function removeGroups(s: string) {\n return s.replace(possiblyNestedRouteGroupPatternRegex, '')\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string = '/'): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\n/**\n * The `node.path` is used as the `id` in the route definition.\n * This function checks if the given node has a parent and if so, it determines the correct path for the given node.\n * @param node - The node to determine the path for.\n * @returns The correct path for the given node.\n */\nexport function determineNodePath(node: RouteNode) {\n return (node.path = node.parent\n ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'\n : node.routePath)\n}\n\n/**\n * Removes the last segment from a given path. Segments are considered to be separated by a '/'.\n *\n * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.\n * @returns {string} The path with the last segment removed.\n * @example\n * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'\n */\nexport function removeLastSegmentFromPath(routePath: string = '/'): string {\n const segments = routePath.split('/')\n segments.pop() // Remove the last segment\n return segments.join('/')\n}\n\nexport function hasParentRoute(\n routes: Array<RouteNode>,\n node: RouteNode,\n routePathToCheck: string | undefined,\n originalRoutePathToCheck?: string,\n): RouteNode | null {\n const getNonNestedSegments = (routePath: string) => {\n const regex = /_(?=\\/|$)/g\n\n return [...routePath.matchAll(regex)]\n .filter((match) => {\n const beforeStr = routePath.substring(0, match.index)\n const openBrackets = (beforeStr.match(/\\[/g) || []).length\n const closeBrackets = (beforeStr.match(/\\]/g) || []).length\n return openBrackets === closeBrackets\n })\n .map((match) => routePath.substring(0, match.index + 1))\n .reverse()\n }\n\n if (!routePathToCheck || routePathToCheck === '/') {\n return null\n }\n\n const sortedNodes = multiSortBy(routes, [\n (d) => d.routePath!.length * -1,\n (d) => d.variableName,\n ]).filter((d) => d.routePath !== `/${rootPathId}`)\n\n const filteredNodes = node._isExperimentalNonNestedRoute\n ? []\n : [...sortedNodes]\n\n if (node._isExperimentalNonNestedRoute) {\n const nonNestedSegments = getNonNestedSegments(\n originalRoutePathToCheck ?? '',\n )\n\n for (const route of sortedNodes) {\n if (route.routePath === '/') continue\n\n if (\n routePathToCheck.startsWith(`${route.routePath}/`) &&\n route._isExperimentalNonNestedRoute &&\n route.routePath !== routePathToCheck\n ) {\n return route\n }\n\n if (\n nonNestedSegments.find((seg) => seg === `${route.routePath}_`) ||\n !(\n route._fsRouteType === 'pathless_layout' ||\n route._fsRouteType === 'layout' ||\n route._fsRouteType === '__root'\n )\n ) {\n continue\n }\n\n filteredNodes.push(route)\n }\n }\n\n for (const route of filteredNodes) {\n if (route.routePath === '/') continue\n\n if (\n routePathToCheck.startsWith(`${route.routePath}/`) &&\n route.routePath !== routePathToCheck\n ) {\n return route\n }\n }\n\n const segments = routePathToCheck.split('/')\n segments.pop() // Remove the last segment\n const parentRoutePath = segments.join('/')\n\n return hasParentRoute(routes, node, parentRoutePath, originalRoutePathToCheck)\n}\n\n/**\n * Gets the final variable name for a route\n */\nexport const getResolvedRouteNodeVariableName = (\n routeNode: RouteNode,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}RouteWithChildren`\n : `${routeNode.variableName}Route`\n}\n\n/**\n * Checks if a given RouteNode is valid for augmenting it with typing based on conditions.\n * Also asserts that the RouteNode is defined.\n *\n * @param routeNode - The RouteNode to check.\n * @returns A boolean indicating whether the RouteNode is defined.\n */\nexport function isRouteNodeValidForAugmentation(\n routeNode?: RouteNode,\n): routeNode is RouteNode {\n if (!routeNode || routeNode.isVirtual) {\n return false\n }\n return true\n}\n\n/**\n * Infers the path for use by TS\n */\nexport const inferPath = (routeNode: RouteNode): string => {\n return routeNode.cleanedPath === '/'\n ? routeNode.cleanedPath\n : (routeNode.cleanedPath?.replace(/\\/$/, '') ?? '')\n}\n\n/**\n * Infers the full path for use by TS\n */\nexport const inferFullPath = (\n routeNode: RouteNode,\n config?: Pick<Config, 'experimental' | 'routeToken'>,\n): string => {\n // with new nonNestedPaths feature we can be sure any remaining trailing underscores are escaped and should remain\n // TODO with new major we can remove check and only remove leading underscores\n const fullPath = removeGroups(\n (config?.experimental?.nonNestedRoutes\n ? removeLayoutSegments(routeNode.routePath)\n : removeUnderscores(removeLayoutSegments(routeNode.routePath))) ?? '',\n )\n\n return routeNode.cleanedPath === '/' ? fullPath : fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Creates a map from fullPath to routeNode\n */\nexport const createRouteNodesByFullPath = (\n routeNodes: Array<RouteNode>,\n config?: Pick<Config, 'experimental' | 'routeToken'>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => [\n inferFullPath(routeNode, config),\n routeNode,\n ]),\n )\n}\n\n/**\n * Create a map from 'to' to a routeNode\n */\nexport const createRouteNodesByTo = (\n routeNodes: Array<RouteNode>,\n config?: Pick<Config, 'experimental' | 'routeToken'>,\n): Map<string, RouteNode> => {\n return new Map(\n dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [\n inferTo(routeNode, config),\n routeNode,\n ]),\n )\n}\n\n/**\n * Create a map from 'id' to a routeNode\n */\nexport const createRouteNodesById = (\n routeNodes: Array<RouteNode>,\n): Map<string, RouteNode> => {\n return new Map(\n routeNodes.map((routeNode) => {\n const id = routeNode.routePath ?? ''\n return [id, routeNode]\n }),\n )\n}\n\n/**\n * Infers to path\n */\nexport const inferTo = (\n routeNode: RouteNode,\n config?: Pick<Config, 'experimental' | 'routeToken'>,\n): string => {\n const fullPath = inferFullPath(routeNode, config)\n\n if (fullPath === '/') return fullPath\n\n return fullPath.replace(/\\/$/, '')\n}\n\n/**\n * Dedupes branches and index routes\n */\nexport const dedupeBranchesAndIndexRoutes = (\n routes: Array<RouteNode>,\n): Array<RouteNode> => {\n return routes.filter((route) => {\n if (route.children?.find((child) => child.cleanedPath === '/')) return false\n return true\n })\n}\n\nfunction checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {\n // Check no two routes have the same `key`\n // if they do, throw an error with the conflicting filePaths\n const keys = routes.map((d) => d[key])\n const uniqueKeys = new Set(keys)\n if (keys.length !== uniqueKeys.size) {\n const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)\n const conflictingFiles = routes.filter((d) =>\n duplicateKeys.includes(d[key]),\n )\n return conflictingFiles\n }\n return undefined\n}\n\nexport function checkRouteFullPathUniqueness(\n _routes: Array<RouteNode>,\n config: Config,\n) {\n const routes = _routes.map((d) => {\n const inferredFullPath = inferFullPath(d, config)\n return { ...d, inferredFullPath }\n })\n\n const conflictingFiles = checkUnique(routes, 'inferredFullPath')\n\n if (conflictingFiles !== undefined) {\n const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles\n .map((p) => `\"${p.inferredFullPath}\"`)\n .join(', ')}.\nPlease ensure each Route has a unique full path.\nConflicting files: \\n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\\n ')}\\n`\n throw new Error(errorMessage)\n }\n}\n\nexport function buildRouteTreeConfig(\n nodes: Array<RouteNode>,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes.map((node) => {\n if (node._fsRouteType === '__root') {\n return\n }\n\n if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {\n return\n }\n\n const route = `${node.variableName}`\n\n if (node.children?.length) {\n const childConfigs = buildRouteTreeConfig(\n node.children,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}RouteChildren {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}RouteChildren${disableTypes ? '' : `: ${route}RouteChildren`} = {\n ${node.children\n .map(\n (child) =>\n `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}RouteWithChildren = ${route}Route._addFileChildren(${route}RouteChildren)`\n\n return [\n childConfigs.join('\\n'),\n childrenDeclaration,\n children,\n routeWithChildren,\n ].join('\\n\\n')\n }\n\n return undefined\n })\n\n return children.filter((x) => x !== undefined)\n}\n\nexport function buildImportString(\n importDeclaration: ImportDeclaration,\n): string {\n const { source, specifiers, importKind } = importDeclaration\n return specifiers.length\n ? `import ${importKind === 'type' ? 'type ' : ''}{ ${specifiers.map((s) => (s.local ? `${s.imported} as ${s.local}` : s.imported)).join(', ')} } from '${source}'`\n : ''\n}\n\nexport function lowerCaseFirstChar(value: string) {\n if (!value[0]) {\n return value\n }\n\n return value[0].toLowerCase() + value.slice(1)\n}\n\nexport function mergeImportDeclarations(\n imports: Array<ImportDeclaration>,\n): Array<ImportDeclaration> {\n const merged: Record<string, ImportDeclaration> = {}\n\n for (const imp of imports) {\n const key = `${imp.source}-${imp.importKind}`\n if (!merged[key]) {\n merged[key] = { ...imp, specifiers: [] }\n }\n for (const specifier of imp.specifiers) {\n // check if the specifier already exists in the merged import\n if (\n !merged[key].specifiers.some(\n (existing) =>\n existing.imported === specifier.imported &&\n existing.local === specifier.local,\n )\n ) {\n merged[key].specifiers.push(specifier)\n }\n }\n }\n\n return Object.values(merged)\n}\n\nexport const findParent = (node: RouteNode | undefined): string => {\n if (!node) {\n return `rootRouteImport`\n }\n if (node.parent) {\n if (node.isVirtualParentRequired) {\n return `${node.parent.variableName}Route`\n } else {\n return `${node.parent.variableName}Route`\n }\n }\n return findParent(node.parent)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n config?: Pick<Config, 'experimental' | 'routeToken'>\n}): string {\n return `declare module '${opts.module}' {\n interface ${opts.interfaceName} {\n ${opts.routeNodes\n .map((routeNode) => {\n const filePathId = routeNode.routePath\n const preloaderRoute = `typeof ${routeNode.variableName}RouteImport`\n\n const parent = findParent(routeNode)\n\n return `'${filePathId}': {\n id: '${filePathId}'\n path: '${inferPath(routeNode)}'\n fullPath: '${inferFullPath(routeNode, opts.config)}'\n preLoaderRoute: ${preloaderRoute}\n parentRoute: typeof ${parent}\n }`\n })\n .join('\\n')}\n }\n}`\n}\n\nexport function getImportPath(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n): string {\n return replaceBackslash(\n removeExt(\n path.relative(\n path.dirname(generatedRouteTreePath),\n path.resolve(config.routesDirectory, node.filePath),\n ),\n config.addExtensions,\n ),\n )\n}\n\nexport function getImportForRouteNode(\n node: RouteNode,\n config: Config,\n generatedRouteTreePath: string,\n root: string,\n): ImportDeclaration {\n let source = ''\n if (config.importRoutesUsingAbsolutePaths) {\n source = replaceBackslash(\n removeExt(\n path.resolve(root, config.routesDirectory, node.filePath),\n config.addExtensions,\n ),\n )\n } else {\n source = `./${getImportPath(node, config, generatedRouteTreePath)}`\n }\n return {\n source,\n specifiers: [\n {\n imported: 'Route',\n local: `${node.variableName}RouteImport`,\n },\n ],\n } satisfies ImportDeclaration\n}\n\n/**\n * Used to validate if a route is a pathless layout route\n * @param normalizedRoutePath Normalized route path, i.e `/foo/_layout/route.tsx` and `/foo._layout.route.tsx` to `/foo/_layout/route`\n * @param config The `router-generator` configuration object\n * @returns Boolean indicating if the route is a pathless layout route\n */\nexport function isValidNonNestedRoute(\n normalizedRoutePath: string,\n config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>,\n): boolean {\n if (!config?.experimental?.nonNestedRoutes) {\n return false\n }\n\n const segments = normalizedRoutePath.split('/').filter(Boolean)\n\n if (segments.length === 0) {\n return false\n }\n\n const lastRouteSegment = segments[segments.length - 1]!\n\n // If segment === __root, then exit as false\n if (lastRouteSegment === rootPathId) {\n return false\n }\n\n if (\n lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment.endsWith('_')\n ) {\n return true\n }\n\n for (const segment of segments.slice(0, -1).reverse()) {\n if (segment === config.routeToken) {\n return false\n }\n\n if (segment.endsWith('_')) {\n return true\n }\n }\n\n return false\n}\n"],"names":["path","children"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/router-generator",
|
|
3
|
-
"version": "1.133.
|
|
3
|
+
"version": "1.133.27",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"source-map": "^0.7.4",
|
|
55
55
|
"tsx": "^4.19.2",
|
|
56
56
|
"zod": "^3.24.2",
|
|
57
|
-
"@tanstack/router-
|
|
57
|
+
"@tanstack/router-utils": "1.133.19",
|
|
58
58
|
"@tanstack/virtual-file-routes": "1.133.19",
|
|
59
|
-
"@tanstack/router-
|
|
59
|
+
"@tanstack/router-core": "1.133.27"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@tanstack/react-router": "1.133.
|
|
62
|
+
"@tanstack/react-router": "1.133.27"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"clean": "rimraf ./dist && rimraf ./coverage",
|
package/src/config.ts
CHANGED
|
@@ -54,6 +54,8 @@ export const configSchema = baseConfigSchema.extend({
|
|
|
54
54
|
.object({
|
|
55
55
|
// TODO: This has been made stable and is now "autoCodeSplitting". Remove in next major version.
|
|
56
56
|
enableCodeSplitting: z.boolean().optional(),
|
|
57
|
+
// TODO: This resolves issues with non-nested paths in file-based routing. To be made default in next major version.
|
|
58
|
+
nonNestedRoutes: z.boolean().optional(),
|
|
57
59
|
})
|
|
58
60
|
.optional(),
|
|
59
61
|
plugins: z.array(z.custom<GeneratorPlugin>()).optional(),
|
|
@@ -34,6 +34,7 @@ export async function getRouteNodes(
|
|
|
34
34
|
| 'disableLogging'
|
|
35
35
|
| 'routeToken'
|
|
36
36
|
| 'indexToken'
|
|
37
|
+
| 'experimental'
|
|
37
38
|
>,
|
|
38
39
|
root: string,
|
|
39
40
|
): Promise<GetRouteNodesResult> {
|
|
@@ -131,7 +132,13 @@ export async function getRouteNodes(
|
|
|
131
132
|
} else if (fullPath.match(/\.(tsx|ts|jsx|js)$/)) {
|
|
132
133
|
const filePath = replaceBackslash(path.join(dir, dirent.name))
|
|
133
134
|
const filePathNoExt = removeExt(filePath)
|
|
134
|
-
|
|
135
|
+
const {
|
|
136
|
+
routePath: initialRoutePath,
|
|
137
|
+
originalRoutePath,
|
|
138
|
+
isExperimentalNonNestedRoute,
|
|
139
|
+
} = determineInitialRoutePath(filePathNoExt, config)
|
|
140
|
+
|
|
141
|
+
let routePath = initialRoutePath
|
|
135
142
|
|
|
136
143
|
if (routeFilePrefix) {
|
|
137
144
|
routePath = routePath.replaceAll(routeFilePrefix, '')
|
|
@@ -192,6 +199,8 @@ export async function getRouteNodes(
|
|
|
192
199
|
routePath,
|
|
193
200
|
variableName,
|
|
194
201
|
_fsRouteType: routeType,
|
|
202
|
+
_isExperimentalNonNestedRoute: isExperimentalNonNestedRoute,
|
|
203
|
+
originalRoutePath,
|
|
195
204
|
})
|
|
196
205
|
}
|
|
197
206
|
}),
|
package/src/generator.ts
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
removeGroups,
|
|
34
34
|
removeLastSegmentFromPath,
|
|
35
35
|
removeLayoutSegments,
|
|
36
|
+
removeLeadingUnderscores,
|
|
36
37
|
removeTrailingSlash,
|
|
37
38
|
removeUnderscores,
|
|
38
39
|
replaceBackslash,
|
|
@@ -398,7 +399,7 @@ export class Generator {
|
|
|
398
399
|
}
|
|
399
400
|
|
|
400
401
|
for (const node of routeFileResult) {
|
|
401
|
-
Generator.handleNode(node, acc)
|
|
402
|
+
Generator.handleNode(node, acc, this.config)
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
this.crawlingResult = { rootRouteNode, routeFileResult, acc }
|
|
@@ -677,14 +678,14 @@ export class Generator {
|
|
|
677
678
|
if (!config.disableTypes) {
|
|
678
679
|
fileRoutesByFullPath = [
|
|
679
680
|
`export interface FileRoutesByFullPath {
|
|
680
|
-
${[...createRouteNodesByFullPath(acc.routeNodes).entries()]
|
|
681
|
+
${[...createRouteNodesByFullPath(acc.routeNodes, config).entries()]
|
|
681
682
|
.filter(([fullPath]) => fullPath)
|
|
682
683
|
.map(([fullPath, routeNode]) => {
|
|
683
684
|
return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
|
|
684
685
|
})}
|
|
685
686
|
}`,
|
|
686
687
|
`export interface FileRoutesByTo {
|
|
687
|
-
${[...createRouteNodesByTo(acc.routeNodes).entries()]
|
|
688
|
+
${[...createRouteNodesByTo(acc.routeNodes, config).entries()]
|
|
688
689
|
.filter(([to]) => to)
|
|
689
690
|
.map(([to, routeNode]) => {
|
|
690
691
|
return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
|
|
@@ -700,7 +701,7 @@ ${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
|
|
|
700
701
|
fileRoutesByFullPath: FileRoutesByFullPath
|
|
701
702
|
fullPaths: ${
|
|
702
703
|
acc.routeNodes.length > 0
|
|
703
|
-
? [...createRouteNodesByFullPath(acc.routeNodes).keys()]
|
|
704
|
+
? [...createRouteNodesByFullPath(acc.routeNodes, config).keys()]
|
|
704
705
|
.filter((fullPath) => fullPath)
|
|
705
706
|
.map((fullPath) => `'${fullPath}'`)
|
|
706
707
|
.join('|')
|
|
@@ -709,7 +710,7 @@ fullPaths: ${
|
|
|
709
710
|
fileRoutesByTo: FileRoutesByTo
|
|
710
711
|
to: ${
|
|
711
712
|
acc.routeNodes.length > 0
|
|
712
|
-
? [...createRouteNodesByTo(acc.routeNodes).keys()]
|
|
713
|
+
? [...createRouteNodesByTo(acc.routeNodes, config).keys()]
|
|
713
714
|
.filter((to) => to)
|
|
714
715
|
.map((to) => `'${to}'`)
|
|
715
716
|
.join('|')
|
|
@@ -727,6 +728,7 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
|
|
|
727
728
|
module: this.targetTemplate.fullPkg,
|
|
728
729
|
interfaceName: 'FileRoutesByPath',
|
|
729
730
|
routeNodes: sortedRouteNodes,
|
|
731
|
+
config,
|
|
730
732
|
})
|
|
731
733
|
}
|
|
732
734
|
|
|
@@ -1186,13 +1188,25 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
|
|
|
1186
1188
|
return this.crawlingResult
|
|
1187
1189
|
}
|
|
1188
1190
|
|
|
1189
|
-
private static handleNode(
|
|
1191
|
+
private static handleNode(
|
|
1192
|
+
node: RouteNode,
|
|
1193
|
+
acc: HandleNodeAccumulator,
|
|
1194
|
+
config?: Config,
|
|
1195
|
+
) {
|
|
1190
1196
|
// Do not remove this as we need to set the lastIndex to 0 as it
|
|
1191
1197
|
// is necessary to reset the regex's index when using the global flag
|
|
1192
1198
|
// otherwise it might not match the next time it's used
|
|
1199
|
+
const useExperimentalNonNestedRoutes =
|
|
1200
|
+
config?.experimental?.nonNestedRoutes ?? false
|
|
1201
|
+
|
|
1193
1202
|
resetRegex(this.routeGroupPatternRegex)
|
|
1194
1203
|
|
|
1195
|
-
let parentRoute = hasParentRoute(
|
|
1204
|
+
let parentRoute = hasParentRoute(
|
|
1205
|
+
acc.routeNodes,
|
|
1206
|
+
node,
|
|
1207
|
+
node.routePath,
|
|
1208
|
+
node.originalRoutePath,
|
|
1209
|
+
)
|
|
1196
1210
|
|
|
1197
1211
|
// if the parent route is a virtual parent route, we need to find the real parent route
|
|
1198
1212
|
if (parentRoute?.isVirtualParentRoute && parentRoute.children?.length) {
|
|
@@ -1201,6 +1215,7 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
|
|
|
1201
1215
|
parentRoute.children,
|
|
1202
1216
|
node,
|
|
1203
1217
|
node.routePath,
|
|
1218
|
+
node.originalRoutePath,
|
|
1204
1219
|
)
|
|
1205
1220
|
if (possibleParentRoute) {
|
|
1206
1221
|
parentRoute = possibleParentRoute
|
|
@@ -1220,8 +1235,15 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
|
|
|
1220
1235
|
lastRouteSegment.startsWith('_') ||
|
|
1221
1236
|
split.every((part) => this.routeGroupPatternRegex.test(part))
|
|
1222
1237
|
|
|
1238
|
+
// with new nonNestedPaths feature we can be sure any remaining trailing underscores are escaped and should remain
|
|
1239
|
+
// TODO with new major we can remove check and only remove leading underscores
|
|
1223
1240
|
node.cleanedPath = removeGroups(
|
|
1224
|
-
|
|
1241
|
+
(useExperimentalNonNestedRoutes
|
|
1242
|
+
? removeLeadingUnderscores(
|
|
1243
|
+
removeLayoutSegments(node.path ?? ''),
|
|
1244
|
+
config?.routeToken ?? '',
|
|
1245
|
+
)
|
|
1246
|
+
: removeUnderscores(removeLayoutSegments(node.path))) ?? '',
|
|
1225
1247
|
)
|
|
1226
1248
|
|
|
1227
1249
|
if (
|
|
@@ -1270,6 +1292,7 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
|
|
|
1270
1292
|
_fsRouteType: 'static',
|
|
1271
1293
|
},
|
|
1272
1294
|
acc,
|
|
1295
|
+
config,
|
|
1273
1296
|
)
|
|
1274
1297
|
}
|
|
1275
1298
|
return
|
|
@@ -1316,7 +1339,7 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
|
|
|
1316
1339
|
node.path = determineNodePath(node)
|
|
1317
1340
|
}
|
|
1318
1341
|
|
|
1319
|
-
this.handleNode(parentNode, acc)
|
|
1342
|
+
this.handleNode(parentNode, acc, config)
|
|
1320
1343
|
} else {
|
|
1321
1344
|
anchorRoute.children = anchorRoute.children ?? []
|
|
1322
1345
|
anchorRoute.children.push(node)
|
package/src/types.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type RouteNode = {
|
|
|
4
4
|
variableName: string
|
|
5
5
|
_fsRouteType: FsRouteType
|
|
6
6
|
routePath?: string
|
|
7
|
+
originalRoutePath?: string
|
|
7
8
|
cleanedPath?: string
|
|
8
9
|
path?: string
|
|
9
10
|
isNonPath?: boolean
|
|
@@ -13,6 +14,7 @@ export type RouteNode = {
|
|
|
13
14
|
children?: Array<RouteNode>
|
|
14
15
|
parent?: RouteNode
|
|
15
16
|
createFileRouteProps?: Set<string>
|
|
17
|
+
_isExperimentalNonNestedRoute?: boolean
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export interface GetRouteNodesResult {
|