@tanstack/router-generator 1.121.7 → 1.121.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.
- package/dist/cjs/config.d.cts +3 -3
- package/dist/cjs/generator.cjs +17 -12
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/generator.d.cts +2 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/plugin/default-generator-plugin.cjs +18 -5
- package/dist/cjs/plugin/default-generator-plugin.cjs.map +1 -1
- package/dist/cjs/types.d.cts +4 -0
- package/dist/cjs/utils.cjs +16 -3
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/config.d.ts +3 -3
- package/dist/esm/generator.d.ts +2 -1
- package/dist/esm/generator.js +17 -12
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/plugin/default-generator-plugin.js +18 -5
- package/dist/esm/plugin/default-generator-plugin.js.map +1 -1
- package/dist/esm/types.d.ts +4 -0
- package/dist/esm/utils.js +16 -3
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/generator.ts +38 -11
- package/src/index.ts +2 -0
- package/src/plugin/default-generator-plugin.ts +16 -3
- package/src/types.ts +6 -0
- package/src/utils.ts +45 -31
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { defaultTransformPlugin } from "../transform/default-transform-plugin.js";
|
|
2
2
|
import { checkRouteFullPathUniqueness, isRouteNodeValidForAugmentation } from "../utils.js";
|
|
3
|
+
const EXPORT_NAME = "Route";
|
|
3
4
|
function defaultGeneratorPlugin() {
|
|
4
5
|
return {
|
|
5
6
|
name: "default",
|
|
6
7
|
transformPlugin: defaultTransformPlugin,
|
|
7
8
|
imports: (opts) => {
|
|
9
|
+
var _a;
|
|
8
10
|
const imports = [];
|
|
9
11
|
if (opts.acc.routeNodes.some((n) => n.isVirtual)) {
|
|
10
12
|
imports.push({
|
|
@@ -25,8 +27,8 @@ function defaultGeneratorPlugin() {
|
|
|
25
27
|
}
|
|
26
28
|
if (opts.sortedRouteNodes.some(
|
|
27
29
|
(node) => {
|
|
28
|
-
var
|
|
29
|
-
return ((
|
|
30
|
+
var _a2;
|
|
31
|
+
return ((_a2 = opts.acc.routePiecesByPath[node.routePath]) == null ? void 0 : _a2.lazy) && isRouteNodeValidForAugmentation(node);
|
|
30
32
|
}
|
|
31
33
|
)) {
|
|
32
34
|
typeImport.specifiers.push({ imported: "CreateLazyFileRoute" });
|
|
@@ -36,6 +38,15 @@ function defaultGeneratorPlugin() {
|
|
|
36
38
|
imports.push(typeImport);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
41
|
+
const hasMatchingRouteFiles = opts.acc.routeNodes.length > 0;
|
|
42
|
+
if (hasMatchingRouteFiles) {
|
|
43
|
+
if (!((_a = opts.rootRouteNode.exports) == null ? void 0 : _a.includes(EXPORT_NAME))) {
|
|
44
|
+
imports.push({
|
|
45
|
+
specifiers: [{ imported: "createRootRoute" }],
|
|
46
|
+
source: opts.generator.targetTemplate.fullPkg
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
39
50
|
return imports;
|
|
40
51
|
},
|
|
41
52
|
moduleAugmentation: ({ generator }) => ({
|
|
@@ -43,7 +54,9 @@ function defaultGeneratorPlugin() {
|
|
|
43
54
|
interfaceName: "FileRoutesByPath"
|
|
44
55
|
}),
|
|
45
56
|
onRouteTreesChanged: ({ routeTrees, generator }) => {
|
|
46
|
-
const routeTree = routeTrees.find(
|
|
57
|
+
const routeTree = routeTrees.find(
|
|
58
|
+
(tree) => tree.exportName === EXPORT_NAME
|
|
59
|
+
);
|
|
47
60
|
if (!routeTree) {
|
|
48
61
|
throw new Error(
|
|
49
62
|
'No route tree found with export name "Route". Please ensure your routes are correctly defined.'
|
|
@@ -53,7 +66,7 @@ function defaultGeneratorPlugin() {
|
|
|
53
66
|
routeTree.sortedRouteNodes.filter(
|
|
54
67
|
(d) => {
|
|
55
68
|
var _a;
|
|
56
|
-
return d.children === void 0 && "lazy" !== d._fsRouteType && ((_a = d.exports) == null ? void 0 : _a.includes(
|
|
69
|
+
return d.children === void 0 && "lazy" !== d._fsRouteType && ((_a = d.exports) == null ? void 0 : _a.includes(EXPORT_NAME));
|
|
57
70
|
}
|
|
58
71
|
),
|
|
59
72
|
generator.config
|
|
@@ -72,7 +85,7 @@ function defaultGeneratorPlugin() {
|
|
|
72
85
|
`;
|
|
73
86
|
}
|
|
74
87
|
},
|
|
75
|
-
createRootRouteCode: () => `
|
|
88
|
+
createRootRouteCode: () => `createRootRoute()`,
|
|
76
89
|
createVirtualRouteCode: ({ node }) => `createFileRoute('${node.routePath}')()`,
|
|
77
90
|
config: ({ sortedRouteNodes }) => {
|
|
78
91
|
const hasMatchingRouteFiles = sortedRouteNodes.length > 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-generator-plugin.js","sources":["../../../src/plugin/default-generator-plugin.ts"],"sourcesContent":["import { defaultTransformPlugin } from '../transform/default-transform-plugin'\nimport {\n checkRouteFullPathUniqueness,\n isRouteNodeValidForAugmentation,\n} from '../utils'\nimport type { ImportDeclaration } from '../types'\nimport type { GeneratorPluginWithTransform } from './types'\n\nexport function defaultGeneratorPlugin(): GeneratorPluginWithTransform {\n return {\n name: 'default',\n transformPlugin: defaultTransformPlugin,\n imports: (opts) => {\n const imports: Array<ImportDeclaration> = []\n if (opts.acc.routeNodes.some((n) => n.isVirtual)) {\n imports.push({\n specifiers: [{ imported: 'createFileRoute' }],\n source: opts.generator.targetTemplate.fullPkg,\n })\n }\n if (opts.generator.config.verboseFileRoutes === false) {\n const typeImport: ImportDeclaration = {\n specifiers: [],\n source: opts.generator.targetTemplate.fullPkg,\n importKind: 'type',\n }\n if (\n opts.sortedRouteNodes.some(\n (d) =>\n isRouteNodeValidForAugmentation(d) && d._fsRouteType !== 'lazy',\n )\n ) {\n typeImport.specifiers.push({ imported: 'CreateFileRoute' })\n }\n if (\n opts.sortedRouteNodes.some(\n (node) =>\n opts.acc.routePiecesByPath[node.routePath!]?.lazy &&\n isRouteNodeValidForAugmentation(node),\n )\n ) {\n typeImport.specifiers.push({ imported: 'CreateLazyFileRoute' })\n }\n\n if (typeImport.specifiers.length > 0) {\n typeImport.specifiers.push({ imported: 'FileRoutesByPath' })\n imports.push(typeImport)\n }\n }\n return imports\n },\n moduleAugmentation: ({ generator }) => ({\n module: generator.targetTemplate.fullPkg,\n interfaceName: 'FileRoutesByPath',\n }),\n onRouteTreesChanged: ({ routeTrees, generator }) => {\n const routeTree = routeTrees.find((tree) => tree.exportName ===
|
|
1
|
+
{"version":3,"file":"default-generator-plugin.js","sources":["../../../src/plugin/default-generator-plugin.ts"],"sourcesContent":["import { defaultTransformPlugin } from '../transform/default-transform-plugin'\nimport {\n checkRouteFullPathUniqueness,\n isRouteNodeValidForAugmentation,\n} from '../utils'\nimport type { ImportDeclaration } from '../types'\nimport type { GeneratorPluginWithTransform } from './types'\n\nconst EXPORT_NAME = 'Route'\nexport function defaultGeneratorPlugin(): GeneratorPluginWithTransform {\n return {\n name: 'default',\n transformPlugin: defaultTransformPlugin,\n imports: (opts) => {\n const imports: Array<ImportDeclaration> = []\n if (opts.acc.routeNodes.some((n) => n.isVirtual)) {\n imports.push({\n specifiers: [{ imported: 'createFileRoute' }],\n source: opts.generator.targetTemplate.fullPkg,\n })\n }\n if (opts.generator.config.verboseFileRoutes === false) {\n const typeImport: ImportDeclaration = {\n specifiers: [],\n source: opts.generator.targetTemplate.fullPkg,\n importKind: 'type',\n }\n if (\n opts.sortedRouteNodes.some(\n (d) =>\n isRouteNodeValidForAugmentation(d) && d._fsRouteType !== 'lazy',\n )\n ) {\n typeImport.specifiers.push({ imported: 'CreateFileRoute' })\n }\n if (\n opts.sortedRouteNodes.some(\n (node) =>\n opts.acc.routePiecesByPath[node.routePath!]?.lazy &&\n isRouteNodeValidForAugmentation(node),\n )\n ) {\n typeImport.specifiers.push({ imported: 'CreateLazyFileRoute' })\n }\n\n if (typeImport.specifiers.length > 0) {\n typeImport.specifiers.push({ imported: 'FileRoutesByPath' })\n imports.push(typeImport)\n }\n }\n const hasMatchingRouteFiles = opts.acc.routeNodes.length > 0\n if (hasMatchingRouteFiles) {\n // needs a virtual root route\n if (!opts.rootRouteNode.exports?.includes(EXPORT_NAME)) {\n imports.push({\n specifiers: [{ imported: 'createRootRoute' }],\n source: opts.generator.targetTemplate.fullPkg,\n })\n }\n }\n return imports\n },\n moduleAugmentation: ({ generator }) => ({\n module: generator.targetTemplate.fullPkg,\n interfaceName: 'FileRoutesByPath',\n }),\n onRouteTreesChanged: ({ routeTrees, generator }) => {\n const routeTree = routeTrees.find(\n (tree) => tree.exportName === EXPORT_NAME,\n )\n if (!routeTree) {\n throw new Error(\n 'No route tree found with export name \"Route\". Please ensure your routes are correctly defined.',\n )\n }\n checkRouteFullPathUniqueness(\n routeTree.sortedRouteNodes.filter(\n (d) =>\n d.children === undefined &&\n 'lazy' !== d._fsRouteType &&\n d.exports?.includes(EXPORT_NAME),\n ),\n generator.config,\n )\n },\n routeModuleAugmentation: ({ routeNode }) => {\n if (routeNode._fsRouteType === 'lazy') {\n return `const createLazyFileRoute: CreateLazyFileRoute<FileRoutesByPath['${routeNode.routePath}']['preLoaderRoute']>`\n } else {\n return `const createFileRoute: CreateFileRoute<'${routeNode.routePath}',\n FileRoutesByPath['${routeNode.routePath}']['parentRoute'],\n FileRoutesByPath['${routeNode.routePath}']['id'],\n FileRoutesByPath['${routeNode.routePath}']['path'],\n FileRoutesByPath['${routeNode.routePath}']['fullPath']\n >\n `\n }\n },\n createRootRouteCode: () => `createRootRoute()`,\n createVirtualRouteCode: ({ node }) =>\n `createFileRoute('${node.routePath}')()`,\n config: ({ sortedRouteNodes }) => {\n const hasMatchingRouteFiles = sortedRouteNodes.length > 0\n return {\n virtualRootRoute: hasMatchingRouteFiles,\n }\n },\n }\n}\n"],"names":["_a"],"mappings":";;AAQA,MAAM,cAAc;AACb,SAAS,yBAAuD;AAC9D,SAAA;AAAA,IACL,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,SAAS,CAAC,SAAS;;AACjB,YAAM,UAAoC,CAAC;AACvC,UAAA,KAAK,IAAI,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG;AAChD,gBAAQ,KAAK;AAAA,UACX,YAAY,CAAC,EAAE,UAAU,mBAAmB;AAAA,UAC5C,QAAQ,KAAK,UAAU,eAAe;AAAA,QAAA,CACvC;AAAA,MAAA;AAEH,UAAI,KAAK,UAAU,OAAO,sBAAsB,OAAO;AACrD,cAAM,aAAgC;AAAA,UACpC,YAAY,CAAC;AAAA,UACb,QAAQ,KAAK,UAAU,eAAe;AAAA,UACtC,YAAY;AAAA,QACd;AACA,YACE,KAAK,iBAAiB;AAAA,UACpB,CAAC,MACC,gCAAgC,CAAC,KAAK,EAAE,iBAAiB;AAAA,QAAA,GAE7D;AACA,qBAAW,WAAW,KAAK,EAAE,UAAU,mBAAmB;AAAA,QAAA;AAE5D,YACE,KAAK,iBAAiB;AAAA,UACpB,CAAC,SACC;;AAAA,qBAAAA,MAAA,KAAK,IAAI,kBAAkB,KAAK,SAAU,MAA1C,gBAAAA,IAA6C,SAC7C,gCAAgC,IAAI;AAAA;AAAA,QAAA,GAExC;AACA,qBAAW,WAAW,KAAK,EAAE,UAAU,uBAAuB;AAAA,QAAA;AAG5D,YAAA,WAAW,WAAW,SAAS,GAAG;AACpC,qBAAW,WAAW,KAAK,EAAE,UAAU,oBAAoB;AAC3D,kBAAQ,KAAK,UAAU;AAAA,QAAA;AAAA,MACzB;AAEF,YAAM,wBAAwB,KAAK,IAAI,WAAW,SAAS;AAC3D,UAAI,uBAAuB;AAEzB,YAAI,GAAC,UAAK,cAAc,YAAnB,mBAA4B,SAAS,eAAc;AACtD,kBAAQ,KAAK;AAAA,YACX,YAAY,CAAC,EAAE,UAAU,mBAAmB;AAAA,YAC5C,QAAQ,KAAK,UAAU,eAAe;AAAA,UAAA,CACvC;AAAA,QAAA;AAAA,MACH;AAEK,aAAA;AAAA,IACT;AAAA,IACA,oBAAoB,CAAC,EAAE,iBAAiB;AAAA,MACtC,QAAQ,UAAU,eAAe;AAAA,MACjC,eAAe;AAAA,IAAA;AAAA,IAEjB,qBAAqB,CAAC,EAAE,YAAY,gBAAgB;AAClD,YAAM,YAAY,WAAW;AAAA,QAC3B,CAAC,SAAS,KAAK,eAAe;AAAA,MAChC;AACA,UAAI,CAAC,WAAW;AACd,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MAAA;AAEF;AAAA,QACE,UAAU,iBAAiB;AAAA,UACzB,CAAC,MACC;;AAAA,qBAAE,aAAa,UACf,WAAW,EAAE,kBACb,OAAE,YAAF,mBAAW,SAAS;AAAA;AAAA,QACxB;AAAA,QACA,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,yBAAyB,CAAC,EAAE,gBAAgB;AACtC,UAAA,UAAU,iBAAiB,QAAQ;AAC9B,eAAA,oEAAoE,UAAU,SAAS;AAAA,MAAA,OACzF;AACE,eAAA,2CAA2C,UAAU,SAAS;AAAA,gCAC7C,UAAU,SAAS;AAAA,gCACnB,UAAU,SAAS;AAAA,gCACnB,UAAU,SAAS;AAAA,gCACnB,UAAU,SAAS;AAAA;AAAA;AAAA,MAAA;AAAA,IAI/C;AAAA,IACA,qBAAqB,MAAM;AAAA,IAC3B,wBAAwB,CAAC,EAAE,KACzB,MAAA,oBAAoB,KAAK,SAAS;AAAA,IACpC,QAAQ,CAAC,EAAE,uBAAuB;AAC1B,YAAA,wBAAwB,iBAAiB,SAAS;AACjD,aAAA;AAAA,QACL,kBAAkB;AAAA,MACpB;AAAA,IAAA;AAAA,EAEJ;AACF;"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -41,3 +41,7 @@ export type HandleNodeAccumulator = {
|
|
|
41
41
|
routePiecesByPath: Record<string, RouteSubNode>;
|
|
42
42
|
routeNodes: Array<RouteNode>;
|
|
43
43
|
};
|
|
44
|
+
export type GetRoutesByFileMapResultValue = {
|
|
45
|
+
routePath: string;
|
|
46
|
+
};
|
|
47
|
+
export type GetRoutesByFileMapResult = Map<string, GetRoutesByFileMapResultValue>;
|
package/dist/esm/utils.js
CHANGED
|
@@ -260,7 +260,10 @@ Conflicting files:
|
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
function buildRouteTreeConfig(nodes, exportName, disableTypes, depth = 1) {
|
|
263
|
-
const children = nodes.
|
|
263
|
+
const children = nodes.filter((n) => {
|
|
264
|
+
var _a;
|
|
265
|
+
return (_a = n.exports) == null ? void 0 : _a.includes(exportName);
|
|
266
|
+
}).map((node) => {
|
|
264
267
|
var _a, _b;
|
|
265
268
|
if (node._fsRouteType === "__root") {
|
|
266
269
|
return;
|
|
@@ -277,10 +280,20 @@ function buildRouteTreeConfig(nodes, exportName, disableTypes, depth = 1) {
|
|
|
277
280
|
depth + 1
|
|
278
281
|
);
|
|
279
282
|
const childrenDeclaration = disableTypes ? "" : `interface ${route}${exportName}Children {
|
|
280
|
-
${node.children.
|
|
283
|
+
${node.children.filter((n) => {
|
|
284
|
+
var _a2;
|
|
285
|
+
return (_a2 = n.exports) == null ? void 0 : _a2.includes(exportName);
|
|
286
|
+
}).map(
|
|
287
|
+
(child) => `${child.variableName}${exportName}: typeof ${getResolvedRouteNodeVariableName(child, exportName)}`
|
|
288
|
+
).join(",")}
|
|
281
289
|
}`;
|
|
282
290
|
const children2 = `const ${route}${exportName}Children${disableTypes ? "" : `: ${route}${exportName}Children`} = {
|
|
283
|
-
${node.children.
|
|
291
|
+
${node.children.filter((n) => {
|
|
292
|
+
var _a2;
|
|
293
|
+
return (_a2 = n.exports) == null ? void 0 : _a2.includes(exportName);
|
|
294
|
+
}).map(
|
|
295
|
+
(child) => `${child.variableName}${exportName}: ${getResolvedRouteNodeVariableName(child, exportName)}`
|
|
296
|
+
).join(",")}
|
|
284
297
|
}`;
|
|
285
298
|
const routeWithChildren = `const ${route}${exportName}WithChildren = ${route}${exportName}._addFileChildren(${route}${exportName}Children)`;
|
|
286
299
|
return [
|
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\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 const BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\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(/\\[(.)\\]/g, '$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 variableNameSuffix: string,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}${variableNameSuffix}WithChildren`\n : `${routeNode.variableName}${variableNameSuffix}`\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 exportName: string,\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 exportName,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}${exportName}Children {\n ${node.children.map((child) => `${child.variableName}${exportName}: typeof ${getResolvedRouteNodeVariableName(child, exportName)}`).join(',')}\n}`\n\n const children = `const ${route}${exportName}Children${disableTypes ? '' : `: ${route}${exportName}Children`} = {\n ${node.children.map((child) => `${child.variableName}${exportName}: ${getResolvedRouteNodeVariableName(child, exportName)}`).join(',')}\n}`\n\n const routeWithChildren = `const ${route}${exportName}WithChildren = ${route}${exportName}._addFileChildren(${route}${exportName}Children)`\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 function hasChildWithExport(\n node: RouteNode,\n exportName: string,\n): boolean {\n return (\n node.children?.some((child) => hasChildWithExport(child, exportName)) ??\n false\n )\n}\n\nexport const findParent = (\n node: RouteNode | undefined,\n exportName: string,\n): string => {\n if (!node) {\n return `root${exportName}Import`\n }\n if (node.parent) {\n if (node.parent.exports?.includes(exportName)) {\n if (node.isVirtualParentRequired) {\n return `${node.parent.variableName}${exportName}`\n } else {\n return `${node.parent.variableName}${exportName}`\n }\n }\n }\n return findParent(node.parent, exportName)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n exportName: 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 let preloaderRoute = ''\n\n if (routeNode.exports?.includes(opts.exportName)) {\n preloaderRoute = `typeof ${routeNode.variableName}${opts.exportName}Import`\n } else {\n preloaderRoute = 'unknown'\n }\n\n const parent = findParent(routeNode, opts.exportName)\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"],"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\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 const BRACKET_CONTENT_RE = /\\[(.*?)\\]/g\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(/\\[(.)\\]/g, '$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 variableNameSuffix: string,\n): string => {\n return routeNode.children?.length\n ? `${routeNode.variableName}${variableNameSuffix}WithChildren`\n : `${routeNode.variableName}${variableNameSuffix}`\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 exportName: string,\n disableTypes: boolean,\n depth = 1,\n): Array<string> {\n const children = nodes\n .filter((n) => n.exports?.includes(exportName))\n .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 exportName,\n disableTypes,\n depth + 1,\n )\n\n const childrenDeclaration = disableTypes\n ? ''\n : `interface ${route}${exportName}Children {\n ${node.children\n .filter((n) => n.exports?.includes(exportName))\n .map(\n (child) =>\n `${child.variableName}${exportName}: typeof ${getResolvedRouteNodeVariableName(child, exportName)}`,\n )\n .join(',')}\n}`\n\n const children = `const ${route}${exportName}Children${disableTypes ? '' : `: ${route}${exportName}Children`} = {\n ${node.children\n .filter((n) => n.exports?.includes(exportName))\n .map(\n (child) =>\n `${child.variableName}${exportName}: ${getResolvedRouteNodeVariableName(child, exportName)}`,\n )\n .join(',')}\n}`\n\n const routeWithChildren = `const ${route}${exportName}WithChildren = ${route}${exportName}._addFileChildren(${route}${exportName}Children)`\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 function hasChildWithExport(\n node: RouteNode,\n exportName: string,\n): boolean {\n return (\n node.children?.some((child) => hasChildWithExport(child, exportName)) ??\n false\n )\n}\n\nexport const findParent = (\n node: RouteNode | undefined,\n exportName: string,\n): string => {\n if (!node) {\n return `root${exportName}Import`\n }\n if (node.parent) {\n if (node.parent.exports?.includes(exportName)) {\n if (node.isVirtualParentRequired) {\n return `${node.parent.variableName}${exportName}`\n } else {\n return `${node.parent.variableName}${exportName}`\n }\n }\n }\n return findParent(node.parent, exportName)\n}\n\nexport function buildFileRoutesByPathInterface(opts: {\n routeNodes: Array<RouteNode>\n module: string\n interfaceName: string\n exportName: 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 let preloaderRoute = ''\n\n if (routeNode.exports?.includes(opts.exportName)) {\n preloaderRoute = `typeof ${routeNode.variableName}${opts.exportName}Import`\n } else {\n preloaderRoute = 'unknown'\n }\n\n const parent = findParent(routeNode, opts.exportName)\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"],"names":["path","_a","children"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/generator.ts
CHANGED
|
@@ -48,6 +48,7 @@ import type { TargetTemplate } from './template'
|
|
|
48
48
|
import type {
|
|
49
49
|
FsRouteType,
|
|
50
50
|
GetRouteNodesResult,
|
|
51
|
+
GetRoutesByFileMapResult,
|
|
51
52
|
HandleNodeAccumulator,
|
|
52
53
|
ImportDeclaration,
|
|
53
54
|
RouteNode,
|
|
@@ -137,6 +138,7 @@ interface GeneratorCacheEntry {
|
|
|
137
138
|
|
|
138
139
|
interface RouteNodeCacheEntry extends GeneratorCacheEntry {
|
|
139
140
|
exports: Array<string>
|
|
141
|
+
routeId: string
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
type GeneratorRouteNodeCache = Map</** filePath **/ string, RouteNodeCacheEntry>
|
|
@@ -207,8 +209,13 @@ export class Generator {
|
|
|
207
209
|
: path.resolve(this.root, this.config.routesDirectory)
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
public
|
|
211
|
-
return new
|
|
212
|
+
public getRoutesByFileMap(): GetRoutesByFileMapResult {
|
|
213
|
+
return new Map(
|
|
214
|
+
[...this.routeNodeCache.entries()].map(([filePath, cacheEntry]) => [
|
|
215
|
+
filePath,
|
|
216
|
+
{ routePath: cacheEntry.routeId },
|
|
217
|
+
]),
|
|
218
|
+
)
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
public async run(event?: GeneratorEvent): Promise<void> {
|
|
@@ -643,16 +650,18 @@ export class Generator {
|
|
|
643
650
|
if (!this.config.disableTypes && hasMatchingRouteFiles) {
|
|
644
651
|
fileRoutesByFullPathPerPlugin = [
|
|
645
652
|
`export interface File${exportName}sByFullPath {
|
|
646
|
-
${[...createRouteNodesByFullPath(acc.routeNodes).entries()]
|
|
647
|
-
([fullPath
|
|
653
|
+
${[...createRouteNodesByFullPath(acc.routeNodes).entries()]
|
|
654
|
+
.filter(([fullPath]) => fullPath)
|
|
655
|
+
.map(([fullPath, routeNode]) => {
|
|
648
656
|
return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`
|
|
649
|
-
}
|
|
650
|
-
)}
|
|
657
|
+
})}
|
|
651
658
|
}`,
|
|
652
659
|
`export interface File${exportName}sByTo {
|
|
653
|
-
${[...createRouteNodesByTo(acc.routeNodes).entries()]
|
|
654
|
-
|
|
655
|
-
|
|
660
|
+
${[...createRouteNodesByTo(acc.routeNodes).entries()]
|
|
661
|
+
.filter(([to]) => to)
|
|
662
|
+
.map(([to, routeNode]) => {
|
|
663
|
+
return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`
|
|
664
|
+
})}
|
|
656
665
|
}`,
|
|
657
666
|
`export interface File${exportName}sById {
|
|
658
667
|
'${rootRouteId}': typeof root${exportName}Import,
|
|
@@ -662,9 +671,23 @@ ${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
|
|
|
662
671
|
}`,
|
|
663
672
|
`export interface File${exportName}Types {
|
|
664
673
|
file${exportName}sByFullPath: File${exportName}sByFullPath
|
|
665
|
-
fullPaths: ${
|
|
674
|
+
fullPaths: ${
|
|
675
|
+
acc.routeNodes.length > 0
|
|
676
|
+
? [...createRouteNodesByFullPath(acc.routeNodes).keys()]
|
|
677
|
+
.filter((fullPath) => fullPath)
|
|
678
|
+
.map((fullPath) => `'${fullPath}'`)
|
|
679
|
+
.join('|')
|
|
680
|
+
: 'never'
|
|
681
|
+
}
|
|
666
682
|
file${exportName}sByTo: File${exportName}sByTo
|
|
667
|
-
to: ${
|
|
683
|
+
to: ${
|
|
684
|
+
acc.routeNodes.length > 0
|
|
685
|
+
? [...createRouteNodesByTo(acc.routeNodes).keys()]
|
|
686
|
+
.filter((to) => to)
|
|
687
|
+
.map((to) => `'${to}'`)
|
|
688
|
+
.join('|')
|
|
689
|
+
: 'never'
|
|
690
|
+
}
|
|
668
691
|
id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(acc.routeNodes).keys()].map((id) => `'${id}'`)].join('|')}
|
|
669
692
|
file${exportName}sById: File${exportName}sById
|
|
670
693
|
}`,
|
|
@@ -831,6 +854,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
831
854
|
fileContent: existingRouteFile.fileContent,
|
|
832
855
|
mtimeMs: existingRouteFile.stat.mtimeMs,
|
|
833
856
|
exports: [],
|
|
857
|
+
routeId: node.routePath ?? '$$TSR_NO_ROUTE_PATH_ASSIGNED$$',
|
|
834
858
|
}
|
|
835
859
|
|
|
836
860
|
const escapedRoutePath = node.routePath?.replaceAll('$', '$$') ?? ''
|
|
@@ -1110,6 +1134,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1110
1134
|
fileContent: rootNodeFile.fileContent,
|
|
1111
1135
|
mtimeMs: rootNodeFile.stat.mtimeMs,
|
|
1112
1136
|
exports: [],
|
|
1137
|
+
routeId: node.routePath ?? '$$TSR_NO_ROOT_ROUTE_PATH_ASSIGNED$$',
|
|
1113
1138
|
}
|
|
1114
1139
|
|
|
1115
1140
|
// scaffold the root route
|
|
@@ -1142,6 +1167,8 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1142
1167
|
const rootRouteExports: Array<string> = []
|
|
1143
1168
|
for (const plugin of this.pluginsWithTransform) {
|
|
1144
1169
|
const exportName = plugin.transformPlugin.exportName
|
|
1170
|
+
// TODO we need to parse instead of just string match
|
|
1171
|
+
// otherwise a commented out export will still be detected
|
|
1145
1172
|
if (rootNodeFile.fileContent.includes(`export const ${exportName}`)) {
|
|
1146
1173
|
rootRouteExports.push(exportName)
|
|
1147
1174
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import type { ImportDeclaration } from '../types'
|
|
7
7
|
import type { GeneratorPluginWithTransform } from './types'
|
|
8
8
|
|
|
9
|
+
const EXPORT_NAME = 'Route'
|
|
9
10
|
export function defaultGeneratorPlugin(): GeneratorPluginWithTransform {
|
|
10
11
|
return {
|
|
11
12
|
name: 'default',
|
|
@@ -47,6 +48,16 @@ export function defaultGeneratorPlugin(): GeneratorPluginWithTransform {
|
|
|
47
48
|
imports.push(typeImport)
|
|
48
49
|
}
|
|
49
50
|
}
|
|
51
|
+
const hasMatchingRouteFiles = opts.acc.routeNodes.length > 0
|
|
52
|
+
if (hasMatchingRouteFiles) {
|
|
53
|
+
// needs a virtual root route
|
|
54
|
+
if (!opts.rootRouteNode.exports?.includes(EXPORT_NAME)) {
|
|
55
|
+
imports.push({
|
|
56
|
+
specifiers: [{ imported: 'createRootRoute' }],
|
|
57
|
+
source: opts.generator.targetTemplate.fullPkg,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
50
61
|
return imports
|
|
51
62
|
},
|
|
52
63
|
moduleAugmentation: ({ generator }) => ({
|
|
@@ -54,7 +65,9 @@ export function defaultGeneratorPlugin(): GeneratorPluginWithTransform {
|
|
|
54
65
|
interfaceName: 'FileRoutesByPath',
|
|
55
66
|
}),
|
|
56
67
|
onRouteTreesChanged: ({ routeTrees, generator }) => {
|
|
57
|
-
const routeTree = routeTrees.find(
|
|
68
|
+
const routeTree = routeTrees.find(
|
|
69
|
+
(tree) => tree.exportName === EXPORT_NAME,
|
|
70
|
+
)
|
|
58
71
|
if (!routeTree) {
|
|
59
72
|
throw new Error(
|
|
60
73
|
'No route tree found with export name "Route". Please ensure your routes are correctly defined.',
|
|
@@ -65,7 +78,7 @@ export function defaultGeneratorPlugin(): GeneratorPluginWithTransform {
|
|
|
65
78
|
(d) =>
|
|
66
79
|
d.children === undefined &&
|
|
67
80
|
'lazy' !== d._fsRouteType &&
|
|
68
|
-
d.exports?.includes(
|
|
81
|
+
d.exports?.includes(EXPORT_NAME),
|
|
69
82
|
),
|
|
70
83
|
generator.config,
|
|
71
84
|
)
|
|
@@ -83,7 +96,7 @@ export function defaultGeneratorPlugin(): GeneratorPluginWithTransform {
|
|
|
83
96
|
`
|
|
84
97
|
}
|
|
85
98
|
},
|
|
86
|
-
createRootRouteCode: () => `
|
|
99
|
+
createRootRouteCode: () => `createRootRoute()`,
|
|
87
100
|
createVirtualRouteCode: ({ node }) =>
|
|
88
101
|
`createFileRoute('${node.routePath}')()`,
|
|
89
102
|
config: ({ sortedRouteNodes }) => {
|
package/src/types.ts
CHANGED
|
@@ -55,3 +55,9 @@ export type HandleNodeAccumulator = {
|
|
|
55
55
|
routePiecesByPath: Record<string, RouteSubNode>
|
|
56
56
|
routeNodes: Array<RouteNode>
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
export type GetRoutesByFileMapResultValue = { routePath: string }
|
|
60
|
+
export type GetRoutesByFileMapResult = Map<
|
|
61
|
+
string,
|
|
62
|
+
GetRoutesByFileMapResultValue
|
|
63
|
+
>
|
package/src/utils.ts
CHANGED
|
@@ -466,47 +466,61 @@ export function buildRouteTreeConfig(
|
|
|
466
466
|
disableTypes: boolean,
|
|
467
467
|
depth = 1,
|
|
468
468
|
): Array<string> {
|
|
469
|
-
const children = nodes
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
469
|
+
const children = nodes
|
|
470
|
+
.filter((n) => n.exports?.includes(exportName))
|
|
471
|
+
.map((node) => {
|
|
472
|
+
if (node._fsRouteType === '__root') {
|
|
473
|
+
return
|
|
474
|
+
}
|
|
473
475
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
476
|
+
if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {
|
|
477
|
+
return
|
|
478
|
+
}
|
|
477
479
|
|
|
478
|
-
|
|
480
|
+
const route = `${node.variableName}`
|
|
479
481
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
482
|
+
if (node.children?.length) {
|
|
483
|
+
const childConfigs = buildRouteTreeConfig(
|
|
484
|
+
node.children,
|
|
485
|
+
exportName,
|
|
486
|
+
disableTypes,
|
|
487
|
+
depth + 1,
|
|
488
|
+
)
|
|
487
489
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
${node.children
|
|
490
|
+
const childrenDeclaration = disableTypes
|
|
491
|
+
? ''
|
|
492
|
+
: `interface ${route}${exportName}Children {
|
|
493
|
+
${node.children
|
|
494
|
+
.filter((n) => n.exports?.includes(exportName))
|
|
495
|
+
.map(
|
|
496
|
+
(child) =>
|
|
497
|
+
`${child.variableName}${exportName}: typeof ${getResolvedRouteNodeVariableName(child, exportName)}`,
|
|
498
|
+
)
|
|
499
|
+
.join(',')}
|
|
492
500
|
}`
|
|
493
501
|
|
|
494
|
-
|
|
495
|
-
${node.children
|
|
502
|
+
const children = `const ${route}${exportName}Children${disableTypes ? '' : `: ${route}${exportName}Children`} = {
|
|
503
|
+
${node.children
|
|
504
|
+
.filter((n) => n.exports?.includes(exportName))
|
|
505
|
+
.map(
|
|
506
|
+
(child) =>
|
|
507
|
+
`${child.variableName}${exportName}: ${getResolvedRouteNodeVariableName(child, exportName)}`,
|
|
508
|
+
)
|
|
509
|
+
.join(',')}
|
|
496
510
|
}`
|
|
497
511
|
|
|
498
|
-
|
|
512
|
+
const routeWithChildren = `const ${route}${exportName}WithChildren = ${route}${exportName}._addFileChildren(${route}${exportName}Children)`
|
|
499
513
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
514
|
+
return [
|
|
515
|
+
childConfigs.join('\n'),
|
|
516
|
+
childrenDeclaration,
|
|
517
|
+
children,
|
|
518
|
+
routeWithChildren,
|
|
519
|
+
].join('\n\n')
|
|
520
|
+
}
|
|
507
521
|
|
|
508
|
-
|
|
509
|
-
|
|
522
|
+
return undefined
|
|
523
|
+
})
|
|
510
524
|
|
|
511
525
|
return children.filter((x) => x !== undefined)
|
|
512
526
|
}
|