@tanstack/router-generator 1.121.2 → 1.121.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.
Files changed (30) hide show
  1. package/dist/cjs/filesystem/physical/getRouteNodes.cjs +14 -3
  2. package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
  3. package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +1 -0
  4. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +14 -8
  5. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
  6. package/dist/cjs/filesystem/virtual/getRouteNodes.d.cts +4 -1
  7. package/dist/cjs/generator.cjs +34 -5
  8. package/dist/cjs/generator.cjs.map +1 -1
  9. package/dist/cjs/generator.d.cts +3 -0
  10. package/dist/cjs/transform/transform.cjs +38 -15
  11. package/dist/cjs/transform/transform.cjs.map +1 -1
  12. package/dist/cjs/types.d.cts +1 -0
  13. package/dist/esm/filesystem/physical/getRouteNodes.d.ts +1 -0
  14. package/dist/esm/filesystem/physical/getRouteNodes.js +15 -4
  15. package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
  16. package/dist/esm/filesystem/virtual/getRouteNodes.d.ts +4 -1
  17. package/dist/esm/filesystem/virtual/getRouteNodes.js +14 -8
  18. package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
  19. package/dist/esm/generator.d.ts +3 -0
  20. package/dist/esm/generator.js +35 -6
  21. package/dist/esm/generator.js.map +1 -1
  22. package/dist/esm/transform/transform.js +38 -15
  23. package/dist/esm/transform/transform.js.map +1 -1
  24. package/dist/esm/types.d.ts +1 -0
  25. package/package.json +1 -1
  26. package/src/filesystem/physical/getRouteNodes.ts +22 -10
  27. package/src/filesystem/virtual/getRouteNodes.ts +29 -21
  28. package/src/generator.ts +60 -13
  29. package/src/transform/transform.ts +59 -25
  30. package/src/types.ts +1 -0
@@ -53,7 +53,6 @@ export async function getRouteNodes(
53
53
  throw new Error(`virtualRouteConfig is undefined`)
54
54
  }
55
55
  let virtualRouteConfig: VirtualRootRoute
56
- let children: Array<RouteNode> = []
57
56
  if (typeof tsrConfig.virtualRouteConfig === 'string') {
58
57
  virtualRouteConfig = await getVirtualRouteConfigFromFileExport(
59
58
  tsrConfig,
@@ -62,7 +61,7 @@ export async function getRouteNodes(
62
61
  } else {
63
62
  virtualRouteConfig = tsrConfig.virtualRouteConfig
64
63
  }
65
- children = await getRouteNodesRecursive(
64
+ const { children, physicalDirectories } = await getRouteNodesRecursive(
66
65
  tsrConfig,
67
66
  root,
68
67
  fullDir,
@@ -80,7 +79,7 @@ export async function getRouteNodes(
80
79
  const rootRouteNode = allNodes[0]
81
80
  const routeNodes = allNodes.slice(1)
82
81
 
83
- return { rootRouteNode, routeNodes }
82
+ return { rootRouteNode, routeNodes, physicalDirectories }
84
83
  }
85
84
 
86
85
  /**
@@ -135,20 +134,22 @@ export async function getRouteNodesRecursive(
135
134
  fullDir: string,
136
135
  nodes?: Array<VirtualRouteNode>,
137
136
  parent?: RouteNode,
138
- ): Promise<Array<RouteNode>> {
137
+ ): Promise<{ children: Array<RouteNode>; physicalDirectories: Array<string> }> {
139
138
  if (nodes === undefined) {
140
- return []
139
+ return { children: [], physicalDirectories: [] }
141
140
  }
141
+ const allPhysicalDirectories: Array<string> = []
142
142
  const children = await Promise.all(
143
143
  nodes.map(async (node) => {
144
144
  if (node.type === 'physical') {
145
- const { routeNodes } = await getRouteNodesPhysical(
145
+ const { routeNodes, physicalDirectories } = await getRouteNodesPhysical(
146
146
  {
147
147
  ...tsrConfig,
148
148
  routesDirectory: resolve(fullDir, node.directory),
149
149
  },
150
150
  root,
151
151
  )
152
+ allPhysicalDirectories.push(node.directory)
152
153
  routeNodes.forEach((subtreeNode) => {
153
154
  subtreeNode.variableName = routePathToVariable(
154
155
  `${node.pathPrefix}/${removeExt(subtreeNode.filePath)}`,
@@ -206,14 +207,16 @@ export async function getRouteNodesRecursive(
206
207
  }
207
208
 
208
209
  if (node.children !== undefined) {
209
- const children = await getRouteNodesRecursive(
210
- tsrConfig,
211
- root,
212
- fullDir,
213
- node.children,
214
- routeNode,
215
- )
210
+ const { children, physicalDirectories } =
211
+ await getRouteNodesRecursive(
212
+ tsrConfig,
213
+ root,
214
+ fullDir,
215
+ node.children,
216
+ routeNode,
217
+ )
216
218
  routeNode.children = children
219
+ allPhysicalDirectories.push(...physicalDirectories)
217
220
 
218
221
  // If the route has children, it should be a layout
219
222
  routeNode._fsRouteType = 'layout'
@@ -242,19 +245,24 @@ export async function getRouteNodesRecursive(
242
245
  }
243
246
 
244
247
  if (node.children !== undefined) {
245
- const children = await getRouteNodesRecursive(
246
- tsrConfig,
247
- root,
248
- fullDir,
249
- node.children,
250
- routeNode,
251
- )
248
+ const { children, physicalDirectories } =
249
+ await getRouteNodesRecursive(
250
+ tsrConfig,
251
+ root,
252
+ fullDir,
253
+ node.children,
254
+ routeNode,
255
+ )
252
256
  routeNode.children = children
257
+ allPhysicalDirectories.push(...physicalDirectories)
253
258
  }
254
259
  return routeNode
255
260
  }
256
261
  }
257
262
  }),
258
263
  )
259
- return children.flat()
264
+ return {
265
+ children: children.flat(),
266
+ physicalDirectories: allPhysicalDirectories,
267
+ }
260
268
  }
package/src/generator.ts CHANGED
@@ -4,7 +4,10 @@ import { mkdtempSync } from 'node:fs'
4
4
  import crypto from 'node:crypto'
5
5
  import { deepEqual, rootRouteId } from '@tanstack/router-core'
6
6
  import { logging } from './logger'
7
- import { getRouteNodes as physicalGetRouteNodes } from './filesystem/physical/getRouteNodes'
7
+ import {
8
+ isVirtualConfigFile,
9
+ getRouteNodes as physicalGetRouteNodes,
10
+ } from './filesystem/physical/getRouteNodes'
8
11
  import { getRouteNodes as virtualGetRouteNodes } from './filesystem/virtual/getRouteNodes'
9
12
  import { rootPathId } from './filesystem/physical/rootPathId'
10
13
  import {
@@ -170,6 +173,7 @@ export class Generator {
170
173
  // this is just a cache for the transform plugins since we need them for each route file that is to be processed
171
174
  private transformPlugins: Array<TransformPlugin> = []
172
175
  private routeGroupPatternRegex = /\(.+\)/g
176
+ private physicalDirectories: Array<string> = []
173
177
 
174
178
  constructor(opts: { config: Config; root: string; fs?: fs }) {
175
179
  this.config = opts.config
@@ -203,17 +207,17 @@ export class Generator {
203
207
  : path.resolve(this.root, this.config.routesDirectory)
204
208
  }
205
209
 
210
+ public getRouteFileList(): Set<string> {
211
+ return new Set(this.routeNodeCache.keys())
212
+ }
213
+
206
214
  public async run(event?: GeneratorEvent): Promise<void> {
207
- // we are only interested in FileEvents that affect either the generated route tree or files inside the routes folder
208
- if (event && event.type !== 'rerun') {
209
- if (
210
- !(
211
- event.path === this.generatedRouteTreePath ||
212
- event.path.startsWith(this.routesDirectoryPath)
213
- )
214
- ) {
215
- return
216
- }
215
+ if (
216
+ event &&
217
+ event.type !== 'rerun' &&
218
+ !this.isFileRelevantForRouteTreeGeneration(event.path)
219
+ ) {
220
+ return
217
221
  }
218
222
  this.fileEventQueue.push(event ?? { type: 'rerun' })
219
223
  // only allow a single run at a time
@@ -233,7 +237,7 @@ export class Generator {
233
237
  await Promise.all(
234
238
  tempQueue.map(async (e) => {
235
239
  if (e.type === 'update') {
236
- let cacheEntry
240
+ let cacheEntry: GeneratorCacheEntry | undefined
237
241
  if (e.path === this.generatedRouteTreePath) {
238
242
  cacheEntry = this.routeTreeFileCache
239
243
  } else {
@@ -301,7 +305,11 @@ export class Generator {
301
305
  getRouteNodesResult = await physicalGetRouteNodes(this.config, this.root)
302
306
  }
303
307
 
304
- const { rootRouteNode, routeNodes: beforeRouteNodes } = getRouteNodesResult
308
+ const {
309
+ rootRouteNode,
310
+ routeNodes: beforeRouteNodes,
311
+ physicalDirectories,
312
+ } = getRouteNodesResult
305
313
  if (rootRouteNode === undefined) {
306
314
  let errorMessage = `rootRouteNode must not be undefined. Make sure you've added your root route into the route-tree.`
307
315
  if (!this.config.virtualRouteConfig) {
@@ -309,6 +317,7 @@ export class Generator {
309
317
  }
310
318
  throw new Error(errorMessage)
311
319
  }
320
+ this.physicalDirectories = physicalDirectories
312
321
 
313
322
  writeRouteTreeFile = await this.handleRootNode(rootRouteNode)
314
323
 
@@ -1292,4 +1301,42 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
1292
1301
 
1293
1302
  acc.routeNodes.push(node)
1294
1303
  }
1304
+
1305
+ // only process files that are relevant for the route tree generation
1306
+ private isFileRelevantForRouteTreeGeneration(filePath: string): boolean {
1307
+ // the generated route tree file
1308
+ if (filePath === this.generatedRouteTreePath) {
1309
+ return true
1310
+ }
1311
+
1312
+ // files inside the routes folder
1313
+ if (filePath.startsWith(this.routesDirectoryPath)) {
1314
+ return true
1315
+ }
1316
+
1317
+ // the virtual route config file passed into `virtualRouteConfig`
1318
+ if (
1319
+ typeof this.config.virtualRouteConfig === 'string' &&
1320
+ filePath === this.config.virtualRouteConfig
1321
+ ) {
1322
+ return true
1323
+ }
1324
+
1325
+ // this covers all files that are mounted via `virtualRouteConfig` or any `__virtual.ts` files
1326
+ if (this.routeNodeCache.has(filePath)) {
1327
+ return true
1328
+ }
1329
+
1330
+ // virtual config files such as`__virtual.ts`
1331
+ if (isVirtualConfigFile(path.basename(filePath))) {
1332
+ return true
1333
+ }
1334
+
1335
+ // route files inside directories mounted via `physical()` inside a virtual route config
1336
+ if (this.physicalDirectories.some((dir) => filePath.startsWith(dir))) {
1337
+ return true
1338
+ }
1339
+
1340
+ return false
1341
+ }
1295
1342
  }
@@ -56,35 +56,69 @@ export async function transform({
56
56
  registeredExports.set(exportName, plugin)
57
57
  }
58
58
 
59
+ function onExportFound(
60
+ decl: types.namedTypes.VariableDeclarator,
61
+ exportName: string,
62
+ plugin: TransformPlugin,
63
+ ) {
64
+ const pluginAppliedChanges = plugin.onExportFound({
65
+ decl,
66
+ ctx: { ...ctx, preferredQuote },
67
+ })
68
+ if (pluginAppliedChanges) {
69
+ appliedChanges = true
70
+ }
71
+
72
+ // export is handled, remove it from the registered exports
73
+ registeredExports.delete(exportName)
74
+ // store the export so we can later return it once the file is transformed
75
+ foundExports.push(exportName)
76
+ }
77
+
59
78
  const program: types.namedTypes.Program = ast.program
60
79
  // first pass: find registered exports
61
80
  for (const n of program.body) {
62
- if (
63
- registeredExports.size > 0 &&
64
- n.type === 'ExportNamedDeclaration' &&
65
- n.declaration?.type === 'VariableDeclaration'
66
- ) {
67
- const decl = n.declaration.declarations[0]
68
- if (
69
- decl &&
70
- decl.type === 'VariableDeclarator' &&
71
- decl.id.type === 'Identifier'
72
- ) {
73
- const plugin = registeredExports.get(decl.id.name)
74
- if (plugin) {
75
- const pluginAppliedChanges = plugin.onExportFound({
76
- decl,
77
- ctx: { ...ctx, preferredQuote },
78
- })
79
-
80
- if (pluginAppliedChanges) {
81
- appliedChanges = true
81
+ if (registeredExports.size > 0 && n.type === 'ExportNamedDeclaration') {
82
+ // direct export of a variable declaration, e.g. `export const Route = createFileRoute('/path')`
83
+ if (n.declaration?.type === 'VariableDeclaration') {
84
+ const decl = n.declaration.declarations[0]
85
+ if (
86
+ decl &&
87
+ decl.type === 'VariableDeclarator' &&
88
+ decl.id.type === 'Identifier'
89
+ ) {
90
+ const plugin = registeredExports.get(decl.id.name)
91
+ if (plugin) {
92
+ onExportFound(decl, decl.id.name, plugin)
93
+ }
94
+ }
95
+ }
96
+ // this is an export without a declaration, e.g. `export { Route }`
97
+ else if (n.declaration === null && n.specifiers) {
98
+ for (const spec of n.specifiers) {
99
+ if (typeof spec.exported.name === 'string') {
100
+ const plugin = registeredExports.get(spec.exported.name)
101
+ if (plugin) {
102
+ const variableName = spec.local?.name || spec.exported.name
103
+ // find the matching variable declaration by iterating over the top-level declarations
104
+ for (const decl of program.body) {
105
+ if (
106
+ decl.type === 'VariableDeclaration' &&
107
+ decl.declarations[0]
108
+ ) {
109
+ const variable = decl.declarations[0]
110
+ if (
111
+ variable.type === 'VariableDeclarator' &&
112
+ variable.id.type === 'Identifier' &&
113
+ variable.id.name === variableName
114
+ ) {
115
+ onExportFound(variable, spec.exported.name, plugin)
116
+ break
117
+ }
118
+ }
119
+ }
120
+ }
82
121
  }
83
-
84
- // export is handled, remove it from the registered exports
85
- registeredExports.delete(decl.id.name)
86
- // store the export so we can later return it once the file is transformed
87
- foundExports.push(decl.id.name)
88
122
  }
89
123
  }
90
124
  }
package/src/types.ts CHANGED
@@ -18,6 +18,7 @@ export type RouteNode = {
18
18
  export interface GetRouteNodesResult {
19
19
  rootRouteNode?: RouteNode
20
20
  routeNodes: Array<RouteNode>
21
+ physicalDirectories: Array<string>
21
22
  }
22
23
 
23
24
  export type FsRouteType =