@tanstack/router-generator 1.141.2 → 1.141.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-generator",
3
- "version": "1.141.2",
3
+ "version": "1.141.5",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -54,12 +54,12 @@
54
54
  "source-map": "^0.7.4",
55
55
  "tsx": "^4.19.2",
56
56
  "zod": "^3.24.2",
57
- "@tanstack/router-core": "1.141.2",
58
- "@tanstack/virtual-file-routes": "1.141.0",
59
- "@tanstack/router-utils": "1.141.0"
57
+ "@tanstack/router-core": "1.141.4",
58
+ "@tanstack/router-utils": "1.141.0",
59
+ "@tanstack/virtual-file-routes": "1.141.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@tanstack/react-router": "1.141.2"
62
+ "@tanstack/react-router": "1.141.4"
63
63
  },
64
64
  "scripts": {
65
65
  "clean": "rimraf ./dist && rimraf ./coverage",
package/src/generator.ts CHANGED
@@ -38,7 +38,6 @@ import {
38
38
  removeUnderscores,
39
39
  replaceBackslash,
40
40
  resetRegex,
41
- routePathToVariable,
42
41
  trimPathLeft,
43
42
  } from './utils'
44
43
  import { fillTemplate, getTargetTemplate } from './template'
@@ -409,6 +408,7 @@ export class Generator {
409
408
  routeTree: [],
410
409
  routeNodes: [],
411
410
  routePiecesByPath: {},
411
+ routeNodesByPath: new Map(),
412
412
  }
413
413
 
414
414
  for (const node of routeFileResult) {
@@ -651,7 +651,10 @@ export class Generator {
651
651
  `const ${node.variableName}Route = ${node.variableName}RouteImport.update({
652
652
  ${[
653
653
  `id: '${node.path}'`,
654
- !node.isNonPath ? `path: '${node.cleanedPath}'` : undefined,
654
+ !node.isNonPath ||
655
+ (node._fsRouteType === 'pathless_layout' && node.cleanedPath)
656
+ ? `path: '${node.cleanedPath}'`
657
+ : undefined,
655
658
  `getParentRoute: () => ${findParent(node)}`,
656
659
  ]
657
660
  .filter(Boolean)
@@ -1351,27 +1354,13 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
1351
1354
 
1352
1355
  resetRegex(this.routeGroupPatternRegex)
1353
1356
 
1354
- let parentRoute = hasParentRoute(
1357
+ const parentRoute = hasParentRoute(
1355
1358
  acc.routeNodes,
1356
1359
  node,
1357
1360
  node.routePath,
1358
1361
  node.originalRoutePath,
1359
1362
  )
1360
1363
 
1361
- // if the parent route is a virtual parent route, we need to find the real parent route
1362
- if (parentRoute?.isVirtualParentRoute && parentRoute.children?.length) {
1363
- // only if this sub-parent route returns a valid parent route, we use it, if not leave it as it
1364
- const possibleParentRoute = hasParentRoute(
1365
- parentRoute.children,
1366
- node,
1367
- node.routePath,
1368
- node.originalRoutePath,
1369
- )
1370
- if (possibleParentRoute) {
1371
- parentRoute = possibleParentRoute
1372
- }
1373
- }
1374
-
1375
1364
  if (parentRoute) node.parent = parentRoute
1376
1365
 
1377
1366
  node.path = determineNodePath(node)
@@ -1419,23 +1408,13 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
1419
1408
  acc.routePiecesByPath[node.routePath!] =
1420
1409
  acc.routePiecesByPath[node.routePath!] || {}
1421
1410
 
1422
- acc.routePiecesByPath[node.routePath!]![
1411
+ const pieceKey =
1423
1412
  node._fsRouteType === 'lazy'
1424
1413
  ? 'lazy'
1425
- : node._fsRouteType === 'loader'
1426
- ? 'loader'
1427
- : node._fsRouteType === 'errorComponent'
1428
- ? 'errorComponent'
1429
- : node._fsRouteType === 'notFoundComponent'
1430
- ? 'notFoundComponent'
1431
- : node._fsRouteType === 'pendingComponent'
1432
- ? 'pendingComponent'
1433
- : 'component'
1434
- ] = node
1435
-
1436
- const anchorRoute = acc.routeNodes.find(
1437
- (d) => d.routePath === node.routePath,
1438
- )
1414
+ : (node._fsRouteType as keyof (typeof acc.routePiecesByPath)[string])
1415
+ acc.routePiecesByPath[node.routePath!]![pieceKey] = node
1416
+
1417
+ const anchorRoute = acc.routeNodesByPath.get(node.routePath!)
1439
1418
 
1440
1419
  // Don't create virtual routes for root route component pieces - the root route is handled separately
1441
1420
  if (!anchorRoute && node.routePath !== `/${rootPathId}`) {
@@ -1452,66 +1431,45 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
1452
1431
  return
1453
1432
  }
1454
1433
 
1455
- const cleanedPathIsEmpty = (node.cleanedPath || '').length === 0
1456
- const nonPathRoute =
1457
- node._fsRouteType === 'pathless_layout' && node.isNonPath
1458
-
1459
- node.isVirtualParentRequired =
1460
- node._fsRouteType === 'pathless_layout' || nonPathRoute
1461
- ? !cleanedPathIsEmpty
1462
- : false
1463
-
1464
- if (!node.isVirtual && node.isVirtualParentRequired) {
1465
- const parentRoutePath = removeLastSegmentFromPath(node.routePath) || '/'
1466
- const parentVariableName = routePathToVariable(parentRoutePath)
1467
-
1468
- const anchorRoute = acc.routeNodes.find(
1469
- (d) => d.routePath === parentRoutePath,
1470
- )
1471
-
1472
- if (!anchorRoute) {
1473
- const parentNode: RouteNode = {
1474
- ...node,
1475
- path: removeLastSegmentFromPath(node.path) || '/',
1476
- filePath: removeLastSegmentFromPath(node.filePath) || '/',
1477
- fullPath: removeLastSegmentFromPath(node.fullPath) || '/',
1478
- routePath: parentRoutePath,
1479
- variableName: parentVariableName,
1480
- isVirtual: true,
1481
- _fsRouteType: 'layout', // layout since this route will wrap other routes
1482
- isVirtualParentRoute: true,
1483
- isVirtualParentRequired: false,
1484
- }
1485
-
1486
- parentNode.children = parentNode.children ?? []
1487
- parentNode.children.push(node)
1488
-
1489
- node.parent = parentNode
1490
-
1491
- if (node._fsRouteType === 'pathless_layout') {
1492
- // since `node.path` is used as the `id` on the route definition, we need to update it
1493
- node.path = determineNodePath(node)
1434
+ const isPathlessLayoutWithPath =
1435
+ node._fsRouteType === 'pathless_layout' &&
1436
+ node.cleanedPath &&
1437
+ node.cleanedPath.length > 0
1438
+
1439
+ // Special handling: pathless layouts with path need to find real ancestor
1440
+ if (!node.isVirtual && isPathlessLayoutWithPath) {
1441
+ const immediateParentPath =
1442
+ removeLastSegmentFromPath(node.routePath) || '/'
1443
+ let searchPath = immediateParentPath
1444
+
1445
+ // Find nearest real (non-virtual, non-index) parent
1446
+ while (searchPath) {
1447
+ const candidate = acc.routeNodesByPath.get(searchPath)
1448
+ if (candidate && !candidate.isVirtual && candidate.path !== '/') {
1449
+ node.parent = candidate
1450
+ node.path = node.routePath
1451
+ node.cleanedPath = removeGroups(
1452
+ removeUnderscores(removeLayoutSegments(immediateParentPath)) ?? '',
1453
+ )
1454
+ break
1494
1455
  }
1495
-
1496
- this.handleNode(parentNode, acc, config)
1497
- } else {
1498
- anchorRoute.children = anchorRoute.children ?? []
1499
- anchorRoute.children.push(node)
1500
-
1501
- node.parent = anchorRoute
1456
+ if (searchPath === '/') break
1457
+ searchPath = removeLastSegmentFromPath(searchPath) || '/'
1502
1458
  }
1503
1459
  }
1504
1460
 
1461
+ // Add to parent's children or to root
1505
1462
  if (node.parent) {
1506
- if (!node.isVirtualParentRequired) {
1507
- node.parent.children = node.parent.children ?? []
1508
- node.parent.children.push(node)
1509
- }
1463
+ node.parent.children = node.parent.children ?? []
1464
+ node.parent.children.push(node)
1510
1465
  } else {
1511
1466
  acc.routeTree.push(node)
1512
1467
  }
1513
1468
 
1514
1469
  acc.routeNodes.push(node)
1470
+ if (node.routePath && !node.isVirtual) {
1471
+ acc.routeNodesByPath.set(node.routePath, node)
1472
+ }
1515
1473
  }
1516
1474
 
1517
1475
  // only process files that are relevant for the route tree generation
package/src/types.ts CHANGED
@@ -8,7 +8,6 @@ export type RouteNode = {
8
8
  cleanedPath?: string
9
9
  path?: string
10
10
  isNonPath?: boolean
11
- isVirtualParentRequired?: boolean
12
11
  isVirtualParentRoute?: boolean
13
12
  isVirtual?: boolean
14
13
  children?: Array<RouteNode>
@@ -58,6 +57,8 @@ export type HandleNodeAccumulator = {
58
57
  routeTree: Array<RouteNode>
59
58
  routePiecesByPath: Record<string, RouteSubNode>
60
59
  routeNodes: Array<RouteNode>
60
+ /** O(1) lookup by routePath - avoids O(n) .find() on every node */
61
+ routeNodesByPath: Map<string, RouteNode>
61
62
  }
62
63
 
63
64
  export type GetRoutesByFileMapResultValue = { routePath: string }
package/src/utils.ts CHANGED
@@ -711,11 +711,7 @@ export const findParent = (node: RouteNode | undefined): string => {
711
711
  return `rootRouteImport`
712
712
  }
713
713
  if (node.parent) {
714
- if (node.isVirtualParentRequired) {
715
- return `${node.parent.variableName}Route`
716
- } else {
717
- return `${node.parent.variableName}Route`
718
- }
714
+ return `${node.parent.variableName}Route`
719
715
  }
720
716
  return findParent(node.parent)
721
717
  }