@tanstack/router-generator 1.141.6 → 1.141.7

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/src/generator.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  import { getRouteNodes as virtualGetRouteNodes } from './filesystem/virtual/getRouteNodes'
12
12
  import { rootPathId } from './filesystem/physical/rootPathId'
13
13
  import {
14
+ RoutePrefixMap,
14
15
  buildFileRoutesByPathInterface,
15
16
  buildImportString,
16
17
  buildRouteTreeConfig,
@@ -37,7 +38,6 @@ import {
37
38
  removeTrailingSlash,
38
39
  removeUnderscores,
39
40
  replaceBackslash,
40
- resetRegex,
41
41
  trimPathLeft,
42
42
  } from './utils'
43
43
  import { fillTemplate, getTargetTemplate } from './template'
@@ -188,9 +188,14 @@ export class Generator {
188
188
  private runPromise: Promise<void> | undefined
189
189
  private fileEventQueue: Array<GeneratorEvent> = []
190
190
  private plugins: Array<GeneratorPlugin> = []
191
- private static routeGroupPatternRegex = /\(.+\)/g
191
+ private static routeGroupPatternRegex = /\(.+\)/
192
192
  private physicalDirectories: Array<string> = []
193
193
 
194
+ private indexTokenRegex: RegExp
195
+ private routeTokenRegex: RegExp
196
+ private static componentPieceRegex =
197
+ /[./](component|errorComponent|notFoundComponent|pendingComponent|loader|lazy)[.]/
198
+
194
199
  constructor(opts: { config: Config; root: string; fs?: fs }) {
195
200
  this.config = opts.config
196
201
  this.logger = logging({ disabled: this.config.disableLogging })
@@ -202,6 +207,9 @@ export class Generator {
202
207
  this.routesDirectoryPath = this.getRoutesDirectoryPath()
203
208
  this.plugins.push(...(opts.config.plugins || []))
204
209
 
210
+ this.indexTokenRegex = new RegExp(`[./]${this.config.indexToken}[.]`)
211
+ this.routeTokenRegex = new RegExp(`[./]${this.config.routeToken}[.]`)
212
+
205
213
  for (const plugin of this.plugins) {
206
214
  plugin.init?.({ generator: this })
207
215
  }
@@ -346,20 +354,9 @@ export class Generator {
346
354
  const preRouteNodes = multiSortBy(beforeRouteNodes, [
347
355
  (d) => (d.routePath === '/' ? -1 : 1),
348
356
  (d) => d.routePath?.split('/').length,
349
- (d) =>
350
- d.filePath.match(new RegExp(`[./]${this.config.indexToken}[.]`))
351
- ? 1
352
- : -1,
353
- (d) =>
354
- d.filePath.match(
355
- /[./](component|errorComponent|notFoundComponent|pendingComponent|loader|lazy)[.]/,
356
- )
357
- ? 1
358
- : -1,
359
- (d) =>
360
- d.filePath.match(new RegExp(`[./]${this.config.routeToken}[.]`))
361
- ? -1
362
- : 1,
357
+ (d) => (d.filePath.match(this.indexTokenRegex) ? 1 : -1),
358
+ (d) => (d.filePath.match(Generator.componentPieceRegex) ? 1 : -1),
359
+ (d) => (d.filePath.match(this.routeTokenRegex) ? -1 : 1),
363
360
  (d) => (d.routePath?.endsWith('/') ? -1 : 1),
364
361
  (d) => d.routePath,
365
362
  ]).filter((d) => {
@@ -411,8 +408,10 @@ export class Generator {
411
408
  routeNodesByPath: new Map(),
412
409
  }
413
410
 
411
+ const prefixMap = new RoutePrefixMap(routeFileResult)
412
+
414
413
  for (const node of routeFileResult) {
415
- Generator.handleNode(node, acc, this.config)
414
+ Generator.handleNode(node, acc, prefixMap, this.config)
416
415
  }
417
416
 
418
417
  this.crawlingResult = { rootRouteNode, routeFileResult, acc }
@@ -549,44 +548,53 @@ export class Generator {
549
548
  (d) => d,
550
549
  ])
551
550
 
552
- const routeImports = sortedRouteNodes
553
- .filter((d) => !d.isVirtual)
554
- .flatMap((node) =>
555
- getImportForRouteNode(
556
- node,
557
- config,
558
- this.generatedRouteTreePath,
559
- this.root,
560
- ),
561
- )
551
+ const routeImports: Array<ImportDeclaration> = []
552
+ const virtualRouteNodes: Array<string> = []
562
553
 
563
- const virtualRouteNodes = sortedRouteNodes
564
- .filter((d) => d.isVirtual)
565
- .map((node) => {
566
- return `const ${
567
- node.variableName
568
- }RouteImport = createFileRoute('${node.routePath}')()`
569
- })
554
+ for (const node of sortedRouteNodes) {
555
+ if (node.isVirtual) {
556
+ virtualRouteNodes.push(
557
+ `const ${node.variableName}RouteImport = createFileRoute('${node.routePath}')()`,
558
+ )
559
+ } else {
560
+ routeImports.push(
561
+ getImportForRouteNode(
562
+ node,
563
+ config,
564
+ this.generatedRouteTreePath,
565
+ this.root,
566
+ ),
567
+ )
568
+ }
569
+ }
570
570
 
571
571
  const imports: Array<ImportDeclaration> = []
572
- if (acc.routeNodes.some((n) => n.isVirtual)) {
572
+ if (virtualRouteNodes.length > 0) {
573
573
  imports.push({
574
574
  specifiers: [{ imported: 'createFileRoute' }],
575
575
  source: this.targetTemplate.fullPkg,
576
576
  })
577
577
  }
578
578
  // Add lazyRouteComponent import if there are component pieces
579
- const hasComponentPieces = sortedRouteNodes.some(
580
- (node) =>
581
- acc.routePiecesByPath[node.routePath!]?.component ||
582
- acc.routePiecesByPath[node.routePath!]?.errorComponent ||
583
- acc.routePiecesByPath[node.routePath!]?.notFoundComponent ||
584
- acc.routePiecesByPath[node.routePath!]?.pendingComponent,
585
- )
586
- // Add lazyFn import if there are loader pieces
587
- const hasLoaderPieces = sortedRouteNodes.some(
588
- (node) => acc.routePiecesByPath[node.routePath!]?.loader,
589
- )
579
+ let hasComponentPieces = false
580
+ let hasLoaderPieces = false
581
+ for (const node of sortedRouteNodes) {
582
+ const pieces = acc.routePiecesByPath[node.routePath!]
583
+ if (pieces) {
584
+ if (
585
+ pieces.component ||
586
+ pieces.errorComponent ||
587
+ pieces.notFoundComponent ||
588
+ pieces.pendingComponent
589
+ ) {
590
+ hasComponentPieces = true
591
+ }
592
+ if (pieces.loader) {
593
+ hasLoaderPieces = true
594
+ }
595
+ if (hasComponentPieces && hasLoaderPieces) break
596
+ }
597
+ }
590
598
  if (hasComponentPieces || hasLoaderPieces) {
591
599
  const runtimeImport: ImportDeclaration = {
592
600
  specifiers: [],
@@ -606,21 +614,23 @@ export class Generator {
606
614
  source: this.targetTemplate.fullPkg,
607
615
  importKind: 'type',
608
616
  }
609
- if (
610
- sortedRouteNodes.some(
611
- (d) =>
612
- isRouteNodeValidForAugmentation(d) && d._fsRouteType !== 'lazy',
613
- )
614
- ) {
617
+ let needsCreateFileRoute = false
618
+ let needsCreateLazyFileRoute = false
619
+ for (const node of sortedRouteNodes) {
620
+ if (isRouteNodeValidForAugmentation(node)) {
621
+ if (node._fsRouteType !== 'lazy') {
622
+ needsCreateFileRoute = true
623
+ }
624
+ if (acc.routePiecesByPath[node.routePath!]?.lazy) {
625
+ needsCreateLazyFileRoute = true
626
+ }
627
+ }
628
+ if (needsCreateFileRoute && needsCreateLazyFileRoute) break
629
+ }
630
+ if (needsCreateFileRoute) {
615
631
  typeImport.specifiers.push({ imported: 'CreateFileRoute' })
616
632
  }
617
- if (
618
- sortedRouteNodes.some(
619
- (node) =>
620
- acc.routePiecesByPath[node.routePath!]?.lazy &&
621
- isRouteNodeValidForAugmentation(node),
622
- )
623
- ) {
633
+ if (needsCreateLazyFileRoute) {
624
634
  typeImport.specifiers.push({ imported: 'CreateLazyFileRoute' })
625
635
  }
626
636
 
@@ -636,15 +646,13 @@ export class Generator {
636
646
  )
637
647
 
638
648
  const createUpdateRoutes = sortedRouteNodes.map((node) => {
639
- const loaderNode = acc.routePiecesByPath[node.routePath!]?.loader
640
- const componentNode = acc.routePiecesByPath[node.routePath!]?.component
641
- const errorComponentNode =
642
- acc.routePiecesByPath[node.routePath!]?.errorComponent
643
- const notFoundComponentNode =
644
- acc.routePiecesByPath[node.routePath!]?.notFoundComponent
645
- const pendingComponentNode =
646
- acc.routePiecesByPath[node.routePath!]?.pendingComponent
647
- const lazyComponentNode = acc.routePiecesByPath[node.routePath!]?.lazy
649
+ const pieces = acc.routePiecesByPath[node.routePath!]
650
+ const loaderNode = pieces?.loader
651
+ const componentNode = pieces?.component
652
+ const errorComponentNode = pieces?.errorComponent
653
+ const notFoundComponentNode = pieces?.notFoundComponent
654
+ const pendingComponentNode = pieces?.pendingComponent
655
+ const lazyComponentNode = pieces?.lazy
648
656
 
649
657
  return [
650
658
  [
@@ -752,13 +760,11 @@ export class Generator {
752
760
 
753
761
  // Generate update for root route if it has component pieces
754
762
  const rootRoutePath = `/${rootPathId}`
755
- const rootComponentNode = acc.routePiecesByPath[rootRoutePath]?.component
756
- const rootErrorComponentNode =
757
- acc.routePiecesByPath[rootRoutePath]?.errorComponent
758
- const rootNotFoundComponentNode =
759
- acc.routePiecesByPath[rootRoutePath]?.notFoundComponent
760
- const rootPendingComponentNode =
761
- acc.routePiecesByPath[rootRoutePath]?.pendingComponent
763
+ const rootPieces = acc.routePiecesByPath[rootRoutePath]
764
+ const rootComponentNode = rootPieces?.component
765
+ const rootErrorComponentNode = rootPieces?.errorComponent
766
+ const rootNotFoundComponentNode = rootPieces?.notFoundComponent
767
+ const rootPendingComponentNode = rootPieces?.pendingComponent
762
768
 
763
769
  let rootRouteUpdate = ''
764
770
  if (
@@ -816,16 +822,23 @@ export class Generator {
816
822
  let fileRoutesByFullPath = ''
817
823
 
818
824
  if (!config.disableTypes) {
825
+ const routeNodesByFullPath = createRouteNodesByFullPath(
826
+ acc.routeNodes,
827
+ config,
828
+ )
829
+ const routeNodesByTo = createRouteNodesByTo(acc.routeNodes, config)
830
+ const routeNodesById = createRouteNodesById(acc.routeNodes)
831
+
819
832
  fileRoutesByFullPath = [
820
833
  `export interface FileRoutesByFullPath {
821
- ${[...createRouteNodesByFullPath(acc.routeNodes, config).entries()]
834
+ ${[...routeNodesByFullPath.entries()]
822
835
  .filter(([fullPath]) => fullPath)
823
836
  .map(([fullPath, routeNode]) => {
824
837
  return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
825
838
  })}
826
839
  }`,
827
840
  `export interface FileRoutesByTo {
828
- ${[...createRouteNodesByTo(acc.routeNodes, config).entries()]
841
+ ${[...routeNodesByTo.entries()]
829
842
  .filter(([to]) => to)
830
843
  .map(([to, routeNode]) => {
831
844
  return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
@@ -833,7 +846,7 @@ ${[...createRouteNodesByTo(acc.routeNodes, config).entries()]
833
846
  }`,
834
847
  `export interface FileRoutesById {
835
848
  '${rootRouteId}': typeof rootRouteImport,
836
- ${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
849
+ ${[...routeNodesById.entries()].map(([id, routeNode]) => {
837
850
  return `'${id}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
838
851
  })}
839
852
  }`,
@@ -841,7 +854,7 @@ ${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
841
854
  fileRoutesByFullPath: FileRoutesByFullPath
842
855
  fullPaths: ${
843
856
  acc.routeNodes.length > 0
844
- ? [...createRouteNodesByFullPath(acc.routeNodes, config).keys()]
857
+ ? [...routeNodesByFullPath.keys()]
845
858
  .filter((fullPath) => fullPath)
846
859
  .map((fullPath) => `'${fullPath}'`)
847
860
  .join('|')
@@ -850,13 +863,13 @@ fullPaths: ${
850
863
  fileRoutesByTo: FileRoutesByTo
851
864
  to: ${
852
865
  acc.routeNodes.length > 0
853
- ? [...createRouteNodesByTo(acc.routeNodes, config).keys()]
866
+ ? [...routeNodesByTo.keys()]
854
867
  .filter((to) => to)
855
868
  .map((to) => `'${to}'`)
856
869
  .join('|')
857
870
  : 'never'
858
871
  }
859
- id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(acc.routeNodes).keys()].map((id) => `'${id}'`)].join('|')}
872
+ id: ${[`'${rootRouteId}'`, ...[...routeNodesById.keys()].map((id) => `'${id}'`)].join('|')}
860
873
  fileRoutesById: FileRoutesById
861
874
  }`,
862
875
  `export interface RootRouteChildren {
@@ -1344,18 +1357,14 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
1344
1357
  private static handleNode(
1345
1358
  node: RouteNode,
1346
1359
  acc: HandleNodeAccumulator,
1360
+ prefixMap: RoutePrefixMap,
1347
1361
  config?: Config,
1348
1362
  ) {
1349
- // Do not remove this as we need to set the lastIndex to 0 as it
1350
- // is necessary to reset the regex's index when using the global flag
1351
- // otherwise it might not match the next time it's used
1352
1363
  const useExperimentalNonNestedRoutes =
1353
1364
  config?.experimental?.nonNestedRoutes ?? false
1354
1365
 
1355
- resetRegex(this.routeGroupPatternRegex)
1356
-
1357
1366
  const parentRoute = hasParentRoute(
1358
- acc.routeNodes,
1367
+ prefixMap,
1359
1368
  node,
1360
1369
  node.routePath,
1361
1370
  node.originalRoutePath,
@@ -1425,6 +1434,7 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
1425
1434
  _fsRouteType: 'static',
1426
1435
  },
1427
1436
  acc,
1437
+ prefixMap,
1428
1438
  config,
1429
1439
  )
1430
1440
  }
@@ -1447,9 +1457,12 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
1447
1457
  const candidate = acc.routeNodesByPath.get(searchPath)
1448
1458
  if (candidate && !candidate.isVirtual && candidate.path !== '/') {
1449
1459
  node.parent = candidate
1450
- node.path = node.routePath
1460
+ node.path =
1461
+ node.routePath?.replace(candidate.routePath ?? '', '') || '/'
1462
+ const pathRelativeToParent =
1463
+ immediateParentPath.replace(candidate.routePath ?? '', '') || '/'
1451
1464
  node.cleanedPath = removeGroups(
1452
- removeUnderscores(removeLayoutSegments(immediateParentPath)) ?? '',
1465
+ removeUnderscores(removeLayoutSegments(pathRelativeToParent)) ?? '',
1453
1466
  )
1454
1467
  break
1455
1468
  }