@tanstack/router-generator 1.99.6 → 1.99.8

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.
@@ -113,9 +113,7 @@ async function getRouteNodes(config, root) {
113
113
  const isAPIRoute = routePath.startsWith(
114
114
  `${utils.removeTrailingSlash(config.apiBase)}/`
115
115
  );
116
- const segments = routePath.split("/");
117
- const lastRouteSegment = segments[segments.length - 1];
118
- const isLayout = lastRouteSegment !== config.indexToken && lastRouteSegment !== config.routeToken && (lastRouteSegment == null ? void 0 : lastRouteSegment.startsWith("_")) || false;
116
+ const isLayout = determineRouteIsLayout(routePath, config);
119
117
  [
120
118
  [isComponent, "component"],
121
119
  [isErrorComponent, "errorComponent"],
@@ -161,5 +159,20 @@ async function getRouteNodes(config, root) {
161
159
  const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId.rootPathId}`);
162
160
  return { rootRouteNode, routeNodes };
163
161
  }
162
+ function determineRouteIsLayout(normalizedRoutePath, config) {
163
+ const segments = normalizedRoutePath.split("/").filter(Boolean);
164
+ if (segments.length === 0) {
165
+ return false;
166
+ }
167
+ const lastRouteSegment = segments[segments.length - 1];
168
+ const secondToLastRouteSegment = segments[segments.length - 2];
169
+ if (lastRouteSegment === rootPathId.rootPathId) {
170
+ return false;
171
+ }
172
+ if (lastRouteSegment === config.routeToken && typeof secondToLastRouteSegment === "string") {
173
+ return secondToLastRouteSegment.startsWith("_");
174
+ }
175
+ return lastRouteSegment !== config.indexToken && lastRouteSegment !== config.routeToken && lastRouteSegment.startsWith("_");
176
+ }
164
177
  exports.getRouteNodes = getRouteNodes;
165
178
  //# sourceMappingURL=getRouteNodes.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"getRouteNodes.cjs","sources":["../../../../src/filesystem/physical/getRouteNodes.ts"],"sourcesContent":["import path from 'node:path'\nimport * as fsp from 'node:fs/promises'\nimport {\n determineInitialRoutePath,\n logging,\n removeExt,\n removeTrailingSlash,\n replaceBackslash,\n routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesVirtual } from '../virtual/getRouteNodes'\nimport { loadConfigFile } from '../virtual/loadConfigFile'\nimport { rootPathId } from './rootPathId'\nimport type {\n VirtualRootRoute,\n VirtualRouteSubtreeConfig,\n} from '@tanstack/virtual-file-routes'\nimport type { GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\n\nconst disallowedRouteGroupConfiguration = /\\(([^)]+)\\).(ts|js|tsx|jsx)/\n\nexport async function getRouteNodes(\n config: Config,\n root: string,\n): Promise<GetRouteNodesResult> {\n const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =\n config\n const logger = logging({ disabled: config.disableLogging })\n const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')\n\n const routeNodes: Array<RouteNode> = []\n\n async function recurse(dir: string) {\n const fullDir = path.resolve(config.routesDirectory, dir)\n let dirList = await fsp.readdir(fullDir, { withFileTypes: true })\n\n dirList = dirList.filter((d) => {\n if (\n d.name.startsWith('.') ||\n (routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))\n ) {\n return false\n }\n\n if (routeFilePrefix) {\n return d.name.startsWith(routeFilePrefix)\n }\n\n if (routeFileIgnorePattern) {\n return !d.name.match(routeFileIgnoreRegExp)\n }\n\n return true\n })\n\n const virtualConfigFile = dirList.find((dirent) => {\n return dirent.isFile() && dirent.name.match(/__virtual\\.[mc]?[jt]s$/)\n })\n\n if (virtualConfigFile !== undefined) {\n const virtualRouteConfigExport = await loadConfigFile(\n path.resolve(fullDir, virtualConfigFile.name),\n )\n let virtualRouteSubtreeConfig: VirtualRouteSubtreeConfig\n if (typeof virtualRouteConfigExport.default === 'function') {\n virtualRouteSubtreeConfig = await virtualRouteConfigExport.default()\n } else {\n virtualRouteSubtreeConfig = virtualRouteConfigExport.default\n }\n const dummyRoot: VirtualRootRoute = {\n type: 'root',\n file: '',\n children: virtualRouteSubtreeConfig,\n }\n const { routeNodes: virtualRouteNodes } = await getRouteNodesVirtual(\n {\n ...config,\n routesDirectory: fullDir,\n virtualRouteConfig: dummyRoot,\n },\n root,\n )\n virtualRouteNodes.forEach((node) => {\n const filePath = replaceBackslash(path.join(dir, node.filePath))\n const routePath = `/${dir}${node.routePath}`\n\n node.variableName = routePathToVariable(\n `${dir}/${removeExt(node.filePath)}`,\n )\n node.routePath = routePath\n node.filePath = filePath\n })\n\n routeNodes.push(...virtualRouteNodes)\n\n return\n }\n\n await Promise.all(\n dirList.map(async (dirent) => {\n const fullPath = path.posix.join(fullDir, dirent.name)\n const relativePath = path.posix.join(dir, dirent.name)\n\n if (dirent.isDirectory()) {\n await recurse(relativePath)\n } else if (fullPath.match(/\\.(tsx|ts|jsx|js)$/)) {\n const filePath = replaceBackslash(path.join(dir, dirent.name))\n const filePathNoExt = removeExt(filePath)\n let routePath = determineInitialRoutePath(filePathNoExt)\n\n if (routeFilePrefix) {\n routePath = routePath.replaceAll(routeFilePrefix, '')\n }\n\n if (disallowedRouteGroupConfiguration.test(dirent.name)) {\n const errorMessage = `A route configuration for a route group was found at \\`${filePath}\\`. This is not supported. Did you mean to use a layout/pathless route instead?`\n logger.error(`ERROR: ${errorMessage}`)\n throw new Error(errorMessage)\n }\n\n const variableName = routePathToVariable(routePath)\n\n const isLazy = routePath.endsWith('/lazy')\n\n if (isLazy) {\n routePath = routePath.replace(/\\/lazy$/, '')\n }\n\n const isRoute = routePath.endsWith(`/${config.routeToken}`)\n const isComponent = routePath.endsWith('/component')\n const isErrorComponent = routePath.endsWith('/errorComponent')\n const isPendingComponent = routePath.endsWith('/pendingComponent')\n const isLoader = routePath.endsWith('/loader')\n const isAPIRoute = routePath.startsWith(\n `${removeTrailingSlash(config.apiBase)}/`,\n )\n\n const segments = routePath.split('/')\n const lastRouteSegment = segments[segments.length - 1]\n const isLayout =\n (lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment?.startsWith('_')) ||\n false\n\n ;(\n [\n [isComponent, 'component'],\n [isErrorComponent, 'errorComponent'],\n [isPendingComponent, 'pendingComponent'],\n [isLoader, 'loader'],\n ] as const\n ).forEach(([isType, type]) => {\n if (isType) {\n logger.warn(\n `WARNING: The \\`.${type}.tsx\\` suffix used for the ${filePath} file is deprecated. Use the new \\`.lazy.tsx\\` suffix instead.`,\n )\n }\n })\n\n routePath = routePath.replace(\n new RegExp(\n `/(component|errorComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,\n ),\n '',\n )\n\n if (routePath === config.indexToken) {\n routePath = '/'\n }\n\n routePath =\n routePath.replace(new RegExp(`/${config.indexToken}$`), '/') || '/'\n\n routeNodes.push({\n filePath,\n fullPath,\n routePath,\n variableName,\n isRoute,\n isComponent,\n isErrorComponent,\n isPendingComponent,\n isLoader,\n isLazy,\n isLayout,\n isAPIRoute,\n })\n }\n }),\n )\n\n return routeNodes\n }\n\n await recurse('./')\n\n const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId}`)\n return { rootRouteNode, routeNodes }\n}\n"],"names":["logging","fsp","loadConfigFile","getRouteNodesVirtual","replaceBackslash","routePathToVariable","removeExt","determineInitialRoutePath","removeTrailingSlash","rootPathId"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,MAAM,oCAAoC;AAEpB,eAAA,cACpB,QACA,MAC8B;AAC9B,QAAM,EAAE,iBAAiB,uBAAuB,uBAC9C,IAAA;AACF,QAAM,SAASA,MAAAA,QAAQ,EAAE,UAAU,OAAO,gBAAgB;AAC1D,QAAM,wBAAwB,IAAI,OAAO,0BAA0B,IAAI,GAAG;AAE1E,QAAM,aAA+B,CAAC;AAEtC,iBAAe,QAAQ,KAAa;AAClC,UAAM,UAAU,KAAK,QAAQ,OAAO,iBAAiB,GAAG;AACpD,QAAA,UAAU,MAAMC,eAAI,QAAQ,SAAS,EAAE,eAAe,MAAM;AAEtD,cAAA,QAAQ,OAAO,CAAC,MAAM;AAE5B,UAAA,EAAE,KAAK,WAAW,GAAG,KACpB,yBAAyB,EAAE,KAAK,WAAW,qBAAqB,GACjE;AACO,eAAA;AAAA,MAAA;AAGT,UAAI,iBAAiB;AACZ,eAAA,EAAE,KAAK,WAAW,eAAe;AAAA,MAAA;AAG1C,UAAI,wBAAwB;AAC1B,eAAO,CAAC,EAAE,KAAK,MAAM,qBAAqB;AAAA,MAAA;AAGrC,aAAA;AAAA,IAAA,CACR;AAED,UAAM,oBAAoB,QAAQ,KAAK,CAAC,WAAW;AACjD,aAAO,OAAO,OAAO,KAAK,OAAO,KAAK,MAAM,wBAAwB;AAAA,IAAA,CACrE;AAED,QAAI,sBAAsB,QAAW;AACnC,YAAM,2BAA2B,MAAMC,eAAA;AAAA,QACrC,KAAK,QAAQ,SAAS,kBAAkB,IAAI;AAAA,MAC9C;AACI,UAAA;AACA,UAAA,OAAO,yBAAyB,YAAY,YAAY;AAC9B,oCAAA,MAAM,yBAAyB,QAAQ;AAAA,MAAA,OAC9D;AACL,oCAA4B,yBAAyB;AAAA,MAAA;AAEvD,YAAM,YAA8B;AAAA,QAClC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,UAAU;AAAA,MACZ;AACA,YAAM,EAAE,YAAY,kBAAkB,IAAI,MAAMC,gBAAA;AAAA,QAC9C;AAAA,UACE,GAAG;AAAA,UACH,iBAAiB;AAAA,UACjB,oBAAoB;AAAA,QACtB;AAAA,QACA;AAAA,MACF;AACkB,wBAAA,QAAQ,CAAC,SAAS;AAClC,cAAM,WAAWC,MAAAA,iBAAiB,KAAK,KAAK,KAAK,KAAK,QAAQ,CAAC;AAC/D,cAAM,YAAY,IAAI,GAAG,GAAG,KAAK,SAAS;AAE1C,aAAK,eAAeC,MAAA;AAAA,UAClB,GAAG,GAAG,IAAIC,MAAU,UAAA,KAAK,QAAQ,CAAC;AAAA,QACpC;AACA,aAAK,YAAY;AACjB,aAAK,WAAW;AAAA,MAAA,CACjB;AAEU,iBAAA,KAAK,GAAG,iBAAiB;AAEpC;AAAA,IAAA;AAGF,UAAM,QAAQ;AAAA,MACZ,QAAQ,IAAI,OAAO,WAAW;AAC5B,cAAM,WAAW,KAAK,MAAM,KAAK,SAAS,OAAO,IAAI;AACrD,cAAM,eAAe,KAAK,MAAM,KAAK,KAAK,OAAO,IAAI;AAEjD,YAAA,OAAO,eAAe;AACxB,gBAAM,QAAQ,YAAY;AAAA,QACjB,WAAA,SAAS,MAAM,oBAAoB,GAAG;AAC/C,gBAAM,WAAWF,MAAAA,iBAAiB,KAAK,KAAK,KAAK,OAAO,IAAI,CAAC;AACvD,gBAAA,gBAAgBE,gBAAU,QAAQ;AACpC,cAAA,YAAYC,gCAA0B,aAAa;AAEvD,cAAI,iBAAiB;AACP,wBAAA,UAAU,WAAW,iBAAiB,EAAE;AAAA,UAAA;AAGtD,cAAI,kCAAkC,KAAK,OAAO,IAAI,GAAG;AACjD,kBAAA,eAAe,0DAA0D,QAAQ;AAChF,mBAAA,MAAM,UAAU,YAAY,EAAE;AAC/B,kBAAA,IAAI,MAAM,YAAY;AAAA,UAAA;AAGxB,gBAAA,eAAeF,0BAAoB,SAAS;AAE5C,gBAAA,SAAS,UAAU,SAAS,OAAO;AAEzC,cAAI,QAAQ;AACE,wBAAA,UAAU,QAAQ,WAAW,EAAE;AAAA,UAAA;AAG7C,gBAAM,UAAU,UAAU,SAAS,IAAI,OAAO,UAAU,EAAE;AACpD,gBAAA,cAAc,UAAU,SAAS,YAAY;AAC7C,gBAAA,mBAAmB,UAAU,SAAS,iBAAiB;AACvD,gBAAA,qBAAqB,UAAU,SAAS,mBAAmB;AAC3D,gBAAA,WAAW,UAAU,SAAS,SAAS;AAC7C,gBAAM,aAAa,UAAU;AAAA,YAC3B,GAAGG,MAAA,oBAAoB,OAAO,OAAO,CAAC;AAAA,UACxC;AAEM,gBAAA,WAAW,UAAU,MAAM,GAAG;AACpC,gBAAM,mBAAmB,SAAS,SAAS,SAAS,CAAC;AAC/C,gBAAA,WACH,qBAAqB,OAAO,cAC3B,qBAAqB,OAAO,eAC5B,qDAAkB,WAAW,SAC/B;AAGA;AAAA,YACE,CAAC,aAAa,WAAW;AAAA,YACzB,CAAC,kBAAkB,gBAAgB;AAAA,YACnC,CAAC,oBAAoB,kBAAkB;AAAA,YACvC,CAAC,UAAU,QAAQ;AAAA,YAErB,QAAQ,CAAC,CAAC,QAAQ,IAAI,MAAM;AAC5B,gBAAI,QAAQ;AACH,qBAAA;AAAA,gBACL,mBAAmB,IAAI,8BAA8B,QAAQ;AAAA,cAC/D;AAAA,YAAA;AAAA,UACF,CACD;AAED,sBAAY,UAAU;AAAA,YACpB,IAAI;AAAA,cACF,sDAAsD,OAAO,UAAU;AAAA,YACzE;AAAA,YACA;AAAA,UACF;AAEI,cAAA,cAAc,OAAO,YAAY;AACvB,wBAAA;AAAA,UAAA;AAIZ,sBAAA,UAAU,QAAQ,IAAI,OAAO,IAAI,OAAO,UAAU,GAAG,GAAG,GAAG,KAAK;AAElE,qBAAW,KAAK;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA,CACD;AAAA,QAAA;AAAA,MAEJ,CAAA;AAAA,IACH;AAEO,WAAA;AAAA,EAAA;AAGT,QAAM,QAAQ,IAAI;AAEZ,QAAA,gBAAgB,WAAW,KAAK,CAAC,MAAM,EAAE,cAAc,IAAIC,WAAU,UAAA,EAAE;AACtE,SAAA,EAAE,eAAe,WAAW;AACrC;;"}
1
+ {"version":3,"file":"getRouteNodes.cjs","sources":["../../../../src/filesystem/physical/getRouteNodes.ts"],"sourcesContent":["import path from 'node:path'\nimport * as fsp from 'node:fs/promises'\nimport {\n determineInitialRoutePath,\n logging,\n removeExt,\n removeTrailingSlash,\n replaceBackslash,\n routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesVirtual } from '../virtual/getRouteNodes'\nimport { loadConfigFile } from '../virtual/loadConfigFile'\nimport { rootPathId } from './rootPathId'\nimport type {\n VirtualRootRoute,\n VirtualRouteSubtreeConfig,\n} from '@tanstack/virtual-file-routes'\nimport type { GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\n\nconst disallowedRouteGroupConfiguration = /\\(([^)]+)\\).(ts|js|tsx|jsx)/\n\nexport async function getRouteNodes(\n config: Config,\n root: string,\n): Promise<GetRouteNodesResult> {\n const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =\n config\n const logger = logging({ disabled: config.disableLogging })\n const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')\n\n const routeNodes: Array<RouteNode> = []\n\n async function recurse(dir: string) {\n const fullDir = path.resolve(config.routesDirectory, dir)\n let dirList = await fsp.readdir(fullDir, { withFileTypes: true })\n\n dirList = dirList.filter((d) => {\n if (\n d.name.startsWith('.') ||\n (routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))\n ) {\n return false\n }\n\n if (routeFilePrefix) {\n return d.name.startsWith(routeFilePrefix)\n }\n\n if (routeFileIgnorePattern) {\n return !d.name.match(routeFileIgnoreRegExp)\n }\n\n return true\n })\n\n const virtualConfigFile = dirList.find((dirent) => {\n return dirent.isFile() && dirent.name.match(/__virtual\\.[mc]?[jt]s$/)\n })\n\n if (virtualConfigFile !== undefined) {\n const virtualRouteConfigExport = await loadConfigFile(\n path.resolve(fullDir, virtualConfigFile.name),\n )\n let virtualRouteSubtreeConfig: VirtualRouteSubtreeConfig\n if (typeof virtualRouteConfigExport.default === 'function') {\n virtualRouteSubtreeConfig = await virtualRouteConfigExport.default()\n } else {\n virtualRouteSubtreeConfig = virtualRouteConfigExport.default\n }\n const dummyRoot: VirtualRootRoute = {\n type: 'root',\n file: '',\n children: virtualRouteSubtreeConfig,\n }\n const { routeNodes: virtualRouteNodes } = await getRouteNodesVirtual(\n {\n ...config,\n routesDirectory: fullDir,\n virtualRouteConfig: dummyRoot,\n },\n root,\n )\n virtualRouteNodes.forEach((node) => {\n const filePath = replaceBackslash(path.join(dir, node.filePath))\n const routePath = `/${dir}${node.routePath}`\n\n node.variableName = routePathToVariable(\n `${dir}/${removeExt(node.filePath)}`,\n )\n node.routePath = routePath\n node.filePath = filePath\n })\n\n routeNodes.push(...virtualRouteNodes)\n\n return\n }\n\n await Promise.all(\n dirList.map(async (dirent) => {\n const fullPath = path.posix.join(fullDir, dirent.name)\n const relativePath = path.posix.join(dir, dirent.name)\n\n if (dirent.isDirectory()) {\n await recurse(relativePath)\n } else if (fullPath.match(/\\.(tsx|ts|jsx|js)$/)) {\n const filePath = replaceBackslash(path.join(dir, dirent.name))\n const filePathNoExt = removeExt(filePath)\n let routePath = determineInitialRoutePath(filePathNoExt)\n\n if (routeFilePrefix) {\n routePath = routePath.replaceAll(routeFilePrefix, '')\n }\n\n if (disallowedRouteGroupConfiguration.test(dirent.name)) {\n const errorMessage = `A route configuration for a route group was found at \\`${filePath}\\`. This is not supported. Did you mean to use a layout/pathless route instead?`\n logger.error(`ERROR: ${errorMessage}`)\n throw new Error(errorMessage)\n }\n\n const variableName = routePathToVariable(routePath)\n\n const isLazy = routePath.endsWith('/lazy')\n\n if (isLazy) {\n routePath = routePath.replace(/\\/lazy$/, '')\n }\n\n const isRoute = routePath.endsWith(`/${config.routeToken}`)\n const isComponent = routePath.endsWith('/component')\n const isErrorComponent = routePath.endsWith('/errorComponent')\n const isPendingComponent = routePath.endsWith('/pendingComponent')\n const isLoader = routePath.endsWith('/loader')\n const isAPIRoute = routePath.startsWith(\n `${removeTrailingSlash(config.apiBase)}/`,\n )\n const isLayout = determineRouteIsLayout(routePath, config)\n\n ;(\n [\n [isComponent, 'component'],\n [isErrorComponent, 'errorComponent'],\n [isPendingComponent, 'pendingComponent'],\n [isLoader, 'loader'],\n ] as const\n ).forEach(([isType, type]) => {\n if (isType) {\n logger.warn(\n `WARNING: The \\`.${type}.tsx\\` suffix used for the ${filePath} file is deprecated. Use the new \\`.lazy.tsx\\` suffix instead.`,\n )\n }\n })\n\n routePath = routePath.replace(\n new RegExp(\n `/(component|errorComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,\n ),\n '',\n )\n\n if (routePath === config.indexToken) {\n routePath = '/'\n }\n\n routePath =\n routePath.replace(new RegExp(`/${config.indexToken}$`), '/') || '/'\n\n routeNodes.push({\n filePath,\n fullPath,\n routePath,\n variableName,\n isRoute,\n isComponent,\n isErrorComponent,\n isPendingComponent,\n isLoader,\n isLazy,\n isLayout,\n isAPIRoute,\n })\n }\n }),\n )\n\n return routeNodes\n }\n\n await recurse('./')\n\n const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId}`)\n return { rootRouteNode, routeNodes }\n}\n\n/**\n * Used to determine if a route is a 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 layout route\n */\nfunction determineRouteIsLayout(normalizedRoutePath: string, config: Config) {\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 const secondToLastRouteSegment = segments[segments.length - 2]\n\n // If segment === __root, then exit as false\n if (lastRouteSegment === rootPathId) {\n return false\n }\n\n // If segment === config.routeToken and secondToLastSegment is a string that starts with _, then exit as true\n // Since the route is actually a configuration route for a layout/pathless route\n // i.e. /foo/_layout/route.tsx === /foo/_layout.tsx\n if (\n lastRouteSegment === config.routeToken &&\n typeof secondToLastRouteSegment === 'string'\n ) {\n return secondToLastRouteSegment.startsWith('_')\n }\n\n // Segment starts with _\n return (\n lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment.startsWith('_')\n )\n}\n"],"names":["logging","fsp","loadConfigFile","getRouteNodesVirtual","replaceBackslash","routePathToVariable","removeExt","determineInitialRoutePath","removeTrailingSlash","rootPathId"],"mappings":"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"}
@@ -94,9 +94,7 @@ async function getRouteNodes(config, root) {
94
94
  const isAPIRoute = routePath.startsWith(
95
95
  `${removeTrailingSlash(config.apiBase)}/`
96
96
  );
97
- const segments = routePath.split("/");
98
- const lastRouteSegment = segments[segments.length - 1];
99
- const isLayout = lastRouteSegment !== config.indexToken && lastRouteSegment !== config.routeToken && (lastRouteSegment == null ? void 0 : lastRouteSegment.startsWith("_")) || false;
97
+ const isLayout = determineRouteIsLayout(routePath, config);
100
98
  [
101
99
  [isComponent, "component"],
102
100
  [isErrorComponent, "errorComponent"],
@@ -142,6 +140,21 @@ async function getRouteNodes(config, root) {
142
140
  const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId}`);
143
141
  return { rootRouteNode, routeNodes };
144
142
  }
143
+ function determineRouteIsLayout(normalizedRoutePath, config) {
144
+ const segments = normalizedRoutePath.split("/").filter(Boolean);
145
+ if (segments.length === 0) {
146
+ return false;
147
+ }
148
+ const lastRouteSegment = segments[segments.length - 1];
149
+ const secondToLastRouteSegment = segments[segments.length - 2];
150
+ if (lastRouteSegment === rootPathId) {
151
+ return false;
152
+ }
153
+ if (lastRouteSegment === config.routeToken && typeof secondToLastRouteSegment === "string") {
154
+ return secondToLastRouteSegment.startsWith("_");
155
+ }
156
+ return lastRouteSegment !== config.indexToken && lastRouteSegment !== config.routeToken && lastRouteSegment.startsWith("_");
157
+ }
145
158
  export {
146
159
  getRouteNodes
147
160
  };
@@ -1 +1 @@
1
- {"version":3,"file":"getRouteNodes.js","sources":["../../../../src/filesystem/physical/getRouteNodes.ts"],"sourcesContent":["import path from 'node:path'\nimport * as fsp from 'node:fs/promises'\nimport {\n determineInitialRoutePath,\n logging,\n removeExt,\n removeTrailingSlash,\n replaceBackslash,\n routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesVirtual } from '../virtual/getRouteNodes'\nimport { loadConfigFile } from '../virtual/loadConfigFile'\nimport { rootPathId } from './rootPathId'\nimport type {\n VirtualRootRoute,\n VirtualRouteSubtreeConfig,\n} from '@tanstack/virtual-file-routes'\nimport type { GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\n\nconst disallowedRouteGroupConfiguration = /\\(([^)]+)\\).(ts|js|tsx|jsx)/\n\nexport async function getRouteNodes(\n config: Config,\n root: string,\n): Promise<GetRouteNodesResult> {\n const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =\n config\n const logger = logging({ disabled: config.disableLogging })\n const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')\n\n const routeNodes: Array<RouteNode> = []\n\n async function recurse(dir: string) {\n const fullDir = path.resolve(config.routesDirectory, dir)\n let dirList = await fsp.readdir(fullDir, { withFileTypes: true })\n\n dirList = dirList.filter((d) => {\n if (\n d.name.startsWith('.') ||\n (routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))\n ) {\n return false\n }\n\n if (routeFilePrefix) {\n return d.name.startsWith(routeFilePrefix)\n }\n\n if (routeFileIgnorePattern) {\n return !d.name.match(routeFileIgnoreRegExp)\n }\n\n return true\n })\n\n const virtualConfigFile = dirList.find((dirent) => {\n return dirent.isFile() && dirent.name.match(/__virtual\\.[mc]?[jt]s$/)\n })\n\n if (virtualConfigFile !== undefined) {\n const virtualRouteConfigExport = await loadConfigFile(\n path.resolve(fullDir, virtualConfigFile.name),\n )\n let virtualRouteSubtreeConfig: VirtualRouteSubtreeConfig\n if (typeof virtualRouteConfigExport.default === 'function') {\n virtualRouteSubtreeConfig = await virtualRouteConfigExport.default()\n } else {\n virtualRouteSubtreeConfig = virtualRouteConfigExport.default\n }\n const dummyRoot: VirtualRootRoute = {\n type: 'root',\n file: '',\n children: virtualRouteSubtreeConfig,\n }\n const { routeNodes: virtualRouteNodes } = await getRouteNodesVirtual(\n {\n ...config,\n routesDirectory: fullDir,\n virtualRouteConfig: dummyRoot,\n },\n root,\n )\n virtualRouteNodes.forEach((node) => {\n const filePath = replaceBackslash(path.join(dir, node.filePath))\n const routePath = `/${dir}${node.routePath}`\n\n node.variableName = routePathToVariable(\n `${dir}/${removeExt(node.filePath)}`,\n )\n node.routePath = routePath\n node.filePath = filePath\n })\n\n routeNodes.push(...virtualRouteNodes)\n\n return\n }\n\n await Promise.all(\n dirList.map(async (dirent) => {\n const fullPath = path.posix.join(fullDir, dirent.name)\n const relativePath = path.posix.join(dir, dirent.name)\n\n if (dirent.isDirectory()) {\n await recurse(relativePath)\n } else if (fullPath.match(/\\.(tsx|ts|jsx|js)$/)) {\n const filePath = replaceBackslash(path.join(dir, dirent.name))\n const filePathNoExt = removeExt(filePath)\n let routePath = determineInitialRoutePath(filePathNoExt)\n\n if (routeFilePrefix) {\n routePath = routePath.replaceAll(routeFilePrefix, '')\n }\n\n if (disallowedRouteGroupConfiguration.test(dirent.name)) {\n const errorMessage = `A route configuration for a route group was found at \\`${filePath}\\`. This is not supported. Did you mean to use a layout/pathless route instead?`\n logger.error(`ERROR: ${errorMessage}`)\n throw new Error(errorMessage)\n }\n\n const variableName = routePathToVariable(routePath)\n\n const isLazy = routePath.endsWith('/lazy')\n\n if (isLazy) {\n routePath = routePath.replace(/\\/lazy$/, '')\n }\n\n const isRoute = routePath.endsWith(`/${config.routeToken}`)\n const isComponent = routePath.endsWith('/component')\n const isErrorComponent = routePath.endsWith('/errorComponent')\n const isPendingComponent = routePath.endsWith('/pendingComponent')\n const isLoader = routePath.endsWith('/loader')\n const isAPIRoute = routePath.startsWith(\n `${removeTrailingSlash(config.apiBase)}/`,\n )\n\n const segments = routePath.split('/')\n const lastRouteSegment = segments[segments.length - 1]\n const isLayout =\n (lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment?.startsWith('_')) ||\n false\n\n ;(\n [\n [isComponent, 'component'],\n [isErrorComponent, 'errorComponent'],\n [isPendingComponent, 'pendingComponent'],\n [isLoader, 'loader'],\n ] as const\n ).forEach(([isType, type]) => {\n if (isType) {\n logger.warn(\n `WARNING: The \\`.${type}.tsx\\` suffix used for the ${filePath} file is deprecated. Use the new \\`.lazy.tsx\\` suffix instead.`,\n )\n }\n })\n\n routePath = routePath.replace(\n new RegExp(\n `/(component|errorComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,\n ),\n '',\n )\n\n if (routePath === config.indexToken) {\n routePath = '/'\n }\n\n routePath =\n routePath.replace(new RegExp(`/${config.indexToken}$`), '/') || '/'\n\n routeNodes.push({\n filePath,\n fullPath,\n routePath,\n variableName,\n isRoute,\n isComponent,\n isErrorComponent,\n isPendingComponent,\n isLoader,\n isLazy,\n isLayout,\n isAPIRoute,\n })\n }\n }),\n )\n\n return routeNodes\n }\n\n await recurse('./')\n\n const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId}`)\n return { rootRouteNode, routeNodes }\n}\n"],"names":["getRouteNodesVirtual"],"mappings":"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"}
1
+ {"version":3,"file":"getRouteNodes.js","sources":["../../../../src/filesystem/physical/getRouteNodes.ts"],"sourcesContent":["import path from 'node:path'\nimport * as fsp from 'node:fs/promises'\nimport {\n determineInitialRoutePath,\n logging,\n removeExt,\n removeTrailingSlash,\n replaceBackslash,\n routePathToVariable,\n} from '../../utils'\nimport { getRouteNodes as getRouteNodesVirtual } from '../virtual/getRouteNodes'\nimport { loadConfigFile } from '../virtual/loadConfigFile'\nimport { rootPathId } from './rootPathId'\nimport type {\n VirtualRootRoute,\n VirtualRouteSubtreeConfig,\n} from '@tanstack/virtual-file-routes'\nimport type { GetRouteNodesResult, RouteNode } from '../../types'\nimport type { Config } from '../../config'\n\nconst disallowedRouteGroupConfiguration = /\\(([^)]+)\\).(ts|js|tsx|jsx)/\n\nexport async function getRouteNodes(\n config: Config,\n root: string,\n): Promise<GetRouteNodesResult> {\n const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =\n config\n const logger = logging({ disabled: config.disableLogging })\n const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')\n\n const routeNodes: Array<RouteNode> = []\n\n async function recurse(dir: string) {\n const fullDir = path.resolve(config.routesDirectory, dir)\n let dirList = await fsp.readdir(fullDir, { withFileTypes: true })\n\n dirList = dirList.filter((d) => {\n if (\n d.name.startsWith('.') ||\n (routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))\n ) {\n return false\n }\n\n if (routeFilePrefix) {\n return d.name.startsWith(routeFilePrefix)\n }\n\n if (routeFileIgnorePattern) {\n return !d.name.match(routeFileIgnoreRegExp)\n }\n\n return true\n })\n\n const virtualConfigFile = dirList.find((dirent) => {\n return dirent.isFile() && dirent.name.match(/__virtual\\.[mc]?[jt]s$/)\n })\n\n if (virtualConfigFile !== undefined) {\n const virtualRouteConfigExport = await loadConfigFile(\n path.resolve(fullDir, virtualConfigFile.name),\n )\n let virtualRouteSubtreeConfig: VirtualRouteSubtreeConfig\n if (typeof virtualRouteConfigExport.default === 'function') {\n virtualRouteSubtreeConfig = await virtualRouteConfigExport.default()\n } else {\n virtualRouteSubtreeConfig = virtualRouteConfigExport.default\n }\n const dummyRoot: VirtualRootRoute = {\n type: 'root',\n file: '',\n children: virtualRouteSubtreeConfig,\n }\n const { routeNodes: virtualRouteNodes } = await getRouteNodesVirtual(\n {\n ...config,\n routesDirectory: fullDir,\n virtualRouteConfig: dummyRoot,\n },\n root,\n )\n virtualRouteNodes.forEach((node) => {\n const filePath = replaceBackslash(path.join(dir, node.filePath))\n const routePath = `/${dir}${node.routePath}`\n\n node.variableName = routePathToVariable(\n `${dir}/${removeExt(node.filePath)}`,\n )\n node.routePath = routePath\n node.filePath = filePath\n })\n\n routeNodes.push(...virtualRouteNodes)\n\n return\n }\n\n await Promise.all(\n dirList.map(async (dirent) => {\n const fullPath = path.posix.join(fullDir, dirent.name)\n const relativePath = path.posix.join(dir, dirent.name)\n\n if (dirent.isDirectory()) {\n await recurse(relativePath)\n } else if (fullPath.match(/\\.(tsx|ts|jsx|js)$/)) {\n const filePath = replaceBackslash(path.join(dir, dirent.name))\n const filePathNoExt = removeExt(filePath)\n let routePath = determineInitialRoutePath(filePathNoExt)\n\n if (routeFilePrefix) {\n routePath = routePath.replaceAll(routeFilePrefix, '')\n }\n\n if (disallowedRouteGroupConfiguration.test(dirent.name)) {\n const errorMessage = `A route configuration for a route group was found at \\`${filePath}\\`. This is not supported. Did you mean to use a layout/pathless route instead?`\n logger.error(`ERROR: ${errorMessage}`)\n throw new Error(errorMessage)\n }\n\n const variableName = routePathToVariable(routePath)\n\n const isLazy = routePath.endsWith('/lazy')\n\n if (isLazy) {\n routePath = routePath.replace(/\\/lazy$/, '')\n }\n\n const isRoute = routePath.endsWith(`/${config.routeToken}`)\n const isComponent = routePath.endsWith('/component')\n const isErrorComponent = routePath.endsWith('/errorComponent')\n const isPendingComponent = routePath.endsWith('/pendingComponent')\n const isLoader = routePath.endsWith('/loader')\n const isAPIRoute = routePath.startsWith(\n `${removeTrailingSlash(config.apiBase)}/`,\n )\n const isLayout = determineRouteIsLayout(routePath, config)\n\n ;(\n [\n [isComponent, 'component'],\n [isErrorComponent, 'errorComponent'],\n [isPendingComponent, 'pendingComponent'],\n [isLoader, 'loader'],\n ] as const\n ).forEach(([isType, type]) => {\n if (isType) {\n logger.warn(\n `WARNING: The \\`.${type}.tsx\\` suffix used for the ${filePath} file is deprecated. Use the new \\`.lazy.tsx\\` suffix instead.`,\n )\n }\n })\n\n routePath = routePath.replace(\n new RegExp(\n `/(component|errorComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,\n ),\n '',\n )\n\n if (routePath === config.indexToken) {\n routePath = '/'\n }\n\n routePath =\n routePath.replace(new RegExp(`/${config.indexToken}$`), '/') || '/'\n\n routeNodes.push({\n filePath,\n fullPath,\n routePath,\n variableName,\n isRoute,\n isComponent,\n isErrorComponent,\n isPendingComponent,\n isLoader,\n isLazy,\n isLayout,\n isAPIRoute,\n })\n }\n }),\n )\n\n return routeNodes\n }\n\n await recurse('./')\n\n const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId}`)\n return { rootRouteNode, routeNodes }\n}\n\n/**\n * Used to determine if a route is a 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 layout route\n */\nfunction determineRouteIsLayout(normalizedRoutePath: string, config: Config) {\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 const secondToLastRouteSegment = segments[segments.length - 2]\n\n // If segment === __root, then exit as false\n if (lastRouteSegment === rootPathId) {\n return false\n }\n\n // If segment === config.routeToken and secondToLastSegment is a string that starts with _, then exit as true\n // Since the route is actually a configuration route for a layout/pathless route\n // i.e. /foo/_layout/route.tsx === /foo/_layout.tsx\n if (\n lastRouteSegment === config.routeToken &&\n typeof secondToLastRouteSegment === 'string'\n ) {\n return secondToLastRouteSegment.startsWith('_')\n }\n\n // Segment starts with _\n return (\n lastRouteSegment !== config.indexToken &&\n lastRouteSegment !== config.routeToken &&\n lastRouteSegment.startsWith('_')\n )\n}\n"],"names":["getRouteNodesVirtual"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-generator",
3
- "version": "1.99.6",
3
+ "version": "1.99.8",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  "@tanstack/virtual-file-routes": "^1.99.0"
56
56
  },
57
57
  "peerDependencies": {
58
- "@tanstack/react-router": "^1.99.6"
58
+ "@tanstack/react-router": "^1.99.8"
59
59
  },
60
60
  "peerDependenciesMeta": {
61
61
  "@tanstack/react-router": {
@@ -135,14 +135,7 @@ export async function getRouteNodes(
135
135
  const isAPIRoute = routePath.startsWith(
136
136
  `${removeTrailingSlash(config.apiBase)}/`,
137
137
  )
138
-
139
- const segments = routePath.split('/')
140
- const lastRouteSegment = segments[segments.length - 1]
141
- const isLayout =
142
- (lastRouteSegment !== config.indexToken &&
143
- lastRouteSegment !== config.routeToken &&
144
- lastRouteSegment?.startsWith('_')) ||
145
- false
138
+ const isLayout = determineRouteIsLayout(routePath, config)
146
139
 
147
140
  ;(
148
141
  [
@@ -199,3 +192,42 @@ export async function getRouteNodes(
199
192
  const rootRouteNode = routeNodes.find((d) => d.routePath === `/${rootPathId}`)
200
193
  return { rootRouteNode, routeNodes }
201
194
  }
195
+
196
+ /**
197
+ * Used to determine if a route is a layout route
198
+ * @param normalizedRoutePath Normalized route path, i.e `/foo/_layout/route.tsx` and `/foo._layout.route.tsx` to `/foo/_layout/route`
199
+ * @param config The `router-generator` configuration object
200
+ * @returns Boolean indicating if the route is a layout route
201
+ */
202
+ function determineRouteIsLayout(normalizedRoutePath: string, config: Config) {
203
+ const segments = normalizedRoutePath.split('/').filter(Boolean)
204
+
205
+ if (segments.length === 0) {
206
+ return false
207
+ }
208
+
209
+ const lastRouteSegment = segments[segments.length - 1]!
210
+ const secondToLastRouteSegment = segments[segments.length - 2]
211
+
212
+ // If segment === __root, then exit as false
213
+ if (lastRouteSegment === rootPathId) {
214
+ return false
215
+ }
216
+
217
+ // If segment === config.routeToken and secondToLastSegment is a string that starts with _, then exit as true
218
+ // Since the route is actually a configuration route for a layout/pathless route
219
+ // i.e. /foo/_layout/route.tsx === /foo/_layout.tsx
220
+ if (
221
+ lastRouteSegment === config.routeToken &&
222
+ typeof secondToLastRouteSegment === 'string'
223
+ ) {
224
+ return secondToLastRouteSegment.startsWith('_')
225
+ }
226
+
227
+ // Segment starts with _
228
+ return (
229
+ lastRouteSegment !== config.indexToken &&
230
+ lastRouteSegment !== config.routeToken &&
231
+ lastRouteSegment.startsWith('_')
232
+ )
233
+ }