@tanstack/router-generator 1.121.0-alpha.26 → 1.121.0-alpha.28
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.cjs +5 -5
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.cts +3 -3
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs +18 -4
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +1 -0
- package/dist/cjs/filesystem/virtual/config.cjs.map +1 -1
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +18 -12
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/filesystem/virtual/getRouteNodes.d.cts +4 -1
- package/dist/cjs/filesystem/virtual/loadConfigFile.cjs.map +1 -1
- package/dist/cjs/generator.cjs +147 -98
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/generator.d.cts +11 -9
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/logger.cjs.map +1 -1
- package/dist/cjs/plugin/default-generator-plugin.cjs +16 -10
- package/dist/cjs/plugin/default-generator-plugin.cjs.map +1 -1
- package/dist/cjs/template.cjs.map +1 -1
- package/dist/cjs/transform/default-transform-plugin.cjs +6 -4
- package/dist/cjs/transform/default-transform-plugin.cjs.map +1 -1
- package/dist/cjs/transform/transform.cjs +48 -32
- package/dist/cjs/transform/transform.cjs.map +1 -1
- package/dist/cjs/transform/transform.d.cts +1 -1
- package/dist/cjs/transform/types.d.cts +1 -1
- package/dist/cjs/transform/utils.cjs.map +1 -1
- package/dist/cjs/transform/utils.d.cts +1 -1
- package/dist/cjs/types.d.cts +5 -0
- package/dist/cjs/utils.cjs +43 -24
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -0
- package/dist/esm/config.d.ts +3 -3
- package/dist/esm/config.js +6 -6
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.d.ts +1 -0
- package/dist/esm/filesystem/physical/getRouteNodes.js +19 -5
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
- package/dist/esm/filesystem/virtual/config.js.map +1 -1
- package/dist/esm/filesystem/virtual/getRouteNodes.d.ts +4 -1
- package/dist/esm/filesystem/virtual/getRouteNodes.js +19 -13
- package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
- package/dist/esm/filesystem/virtual/loadConfigFile.js.map +1 -1
- package/dist/esm/generator.d.ts +11 -9
- package/dist/esm/generator.js +150 -101
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/logger.js.map +1 -1
- package/dist/esm/plugin/default-generator-plugin.js +16 -10
- package/dist/esm/plugin/default-generator-plugin.js.map +1 -1
- package/dist/esm/template.js.map +1 -1
- package/dist/esm/transform/default-transform-plugin.js +6 -4
- package/dist/esm/transform/default-transform-plugin.js.map +1 -1
- package/dist/esm/transform/transform.d.ts +1 -1
- package/dist/esm/transform/transform.js +48 -32
- package/dist/esm/transform/transform.js.map +1 -1
- package/dist/esm/transform/types.d.ts +1 -1
- package/dist/esm/transform/utils.d.ts +1 -1
- package/dist/esm/transform/utils.js.map +1 -1
- package/dist/esm/types.d.ts +5 -0
- package/dist/esm/utils.d.ts +6 -0
- package/dist/esm/utils.js +43 -24
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/config.ts +6 -5
- package/src/filesystem/physical/getRouteNodes.ts +31 -11
- package/src/filesystem/virtual/getRouteNodes.ts +32 -23
- package/src/generator.ts +196 -72
- package/src/index.ts +2 -0
- package/src/plugin/default-generator-plugin.ts +16 -3
- package/src/transform/default-transform-plugin.ts +5 -2
- package/src/transform/transform.ts +68 -43
- package/src/transform/types.ts +1 -1
- package/src/transform/utils.ts +1 -1
- package/src/types.ts +7 -0
- package/src/utils.ts +79 -31
package/src/generator.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import * as fsp from 'node:fs/promises'
|
|
3
|
-
import {
|
|
3
|
+
import { mkdirSync } 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 {
|
|
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 {
|
|
14
|
+
buildFileRoutesByPathInterface,
|
|
11
15
|
buildImportString,
|
|
12
16
|
buildRouteTreeConfig,
|
|
13
17
|
checkFileExists,
|
|
@@ -19,8 +23,6 @@ import {
|
|
|
19
23
|
format,
|
|
20
24
|
getResolvedRouteNodeVariableName,
|
|
21
25
|
hasParentRoute,
|
|
22
|
-
inferFullPath,
|
|
23
|
-
inferPath,
|
|
24
26
|
isRouteNodeValidForAugmentation,
|
|
25
27
|
lowerCaseFirstChar,
|
|
26
28
|
mergeImportDeclarations,
|
|
@@ -46,6 +48,7 @@ import type { TargetTemplate } from './template'
|
|
|
46
48
|
import type {
|
|
47
49
|
FsRouteType,
|
|
48
50
|
GetRouteNodesResult,
|
|
51
|
+
GetRoutesByFileMapResult,
|
|
49
52
|
HandleNodeAccumulator,
|
|
50
53
|
ImportDeclaration,
|
|
51
54
|
RouteNode,
|
|
@@ -55,8 +58,9 @@ import type { Logger } from './logger'
|
|
|
55
58
|
import type { TransformPlugin } from './transform/types'
|
|
56
59
|
|
|
57
60
|
interface fs {
|
|
58
|
-
stat: (
|
|
59
|
-
|
|
61
|
+
stat: (
|
|
62
|
+
filePath: string,
|
|
63
|
+
) => Promise<{ mtimeMs: bigint; mode: number; uid: number; gid: number }>
|
|
60
64
|
rename: (oldPath: string, newPath: string) => Promise<void>
|
|
61
65
|
writeFile: (filePath: string, content: string) => Promise<void>
|
|
62
66
|
readFile: (
|
|
@@ -64,11 +68,20 @@ interface fs {
|
|
|
64
68
|
) => Promise<
|
|
65
69
|
{ stat: { mtimeMs: bigint }; fileContent: string } | 'file-not-existing'
|
|
66
70
|
>
|
|
71
|
+
chmod: (filePath: string, mode: number) => Promise<void>
|
|
72
|
+
chown: (filePath: string, uid: number, gid: number) => Promise<void>
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
const DefaultFileSystem: fs = {
|
|
70
|
-
stat: (filePath) =>
|
|
71
|
-
|
|
76
|
+
stat: async (filePath) => {
|
|
77
|
+
const res = await fsp.stat(filePath, { bigint: true })
|
|
78
|
+
return {
|
|
79
|
+
mtimeMs: res.mtimeMs,
|
|
80
|
+
mode: Number(res.mode),
|
|
81
|
+
uid: Number(res.uid),
|
|
82
|
+
gid: Number(res.gid),
|
|
83
|
+
}
|
|
84
|
+
},
|
|
72
85
|
rename: (oldPath, newPath) => fsp.rename(oldPath, newPath),
|
|
73
86
|
writeFile: (filePath, content) => fsp.writeFile(filePath, content),
|
|
74
87
|
readFile: async (filePath: string) => {
|
|
@@ -87,6 +100,8 @@ const DefaultFileSystem: fs = {
|
|
|
87
100
|
throw e
|
|
88
101
|
}
|
|
89
102
|
},
|
|
103
|
+
chmod: (filePath, mode) => fsp.chmod(filePath, mode),
|
|
104
|
+
chown: (filePath, uid, gid) => fsp.chown(filePath, uid, gid),
|
|
90
105
|
}
|
|
91
106
|
|
|
92
107
|
interface Rerun {
|
|
@@ -135,6 +150,7 @@ interface GeneratorCacheEntry {
|
|
|
135
150
|
|
|
136
151
|
interface RouteNodeCacheEntry extends GeneratorCacheEntry {
|
|
137
152
|
exports: Array<string>
|
|
153
|
+
routeId: string
|
|
138
154
|
}
|
|
139
155
|
|
|
140
156
|
type GeneratorRouteNodeCache = Map</** filePath **/ string, RouteNodeCacheEntry>
|
|
@@ -160,7 +176,7 @@ export class Generator {
|
|
|
160
176
|
|
|
161
177
|
private root: string
|
|
162
178
|
private routesDirectoryPath: string
|
|
163
|
-
private
|
|
179
|
+
private sessionId?: string
|
|
164
180
|
private fs: fs
|
|
165
181
|
private logger: Logger
|
|
166
182
|
private generatedRouteTreePath: string
|
|
@@ -171,15 +187,13 @@ export class Generator {
|
|
|
171
187
|
// this is just a cache for the transform plugins since we need them for each route file that is to be processed
|
|
172
188
|
private transformPlugins: Array<TransformPlugin> = []
|
|
173
189
|
private routeGroupPatternRegex = /\(.+\)/g
|
|
190
|
+
private physicalDirectories: Array<string> = []
|
|
174
191
|
|
|
175
192
|
constructor(opts: { config: Config; root: string; fs?: fs }) {
|
|
176
193
|
this.config = opts.config
|
|
177
194
|
this.logger = logging({ disabled: this.config.disableLogging })
|
|
178
195
|
this.root = opts.root
|
|
179
196
|
this.fs = opts.fs || DefaultFileSystem
|
|
180
|
-
this.tmpDir = this.fs.mkdtempSync(
|
|
181
|
-
path.join(this.config.tmpDir, 'tanstack-router-'),
|
|
182
|
-
)
|
|
183
197
|
this.generatedRouteTreePath = path.resolve(this.config.generatedRouteTree)
|
|
184
198
|
this.targetTemplate = getTargetTemplate(this.config)
|
|
185
199
|
|
|
@@ -204,17 +218,22 @@ export class Generator {
|
|
|
204
218
|
: path.resolve(this.root, this.config.routesDirectory)
|
|
205
219
|
}
|
|
206
220
|
|
|
221
|
+
public getRoutesByFileMap(): GetRoutesByFileMapResult {
|
|
222
|
+
return new Map(
|
|
223
|
+
[...this.routeNodeCache.entries()].map(([filePath, cacheEntry]) => [
|
|
224
|
+
filePath,
|
|
225
|
+
{ routePath: cacheEntry.routeId },
|
|
226
|
+
]),
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
207
230
|
public async run(event?: GeneratorEvent): Promise<void> {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
215
|
-
) {
|
|
216
|
-
return
|
|
217
|
-
}
|
|
231
|
+
if (
|
|
232
|
+
event &&
|
|
233
|
+
event.type !== 'rerun' &&
|
|
234
|
+
!this.isFileRelevantForRouteTreeGeneration(event.path)
|
|
235
|
+
) {
|
|
236
|
+
return
|
|
218
237
|
}
|
|
219
238
|
this.fileEventQueue.push(event ?? { type: 'rerun' })
|
|
220
239
|
// only allow a single run at a time
|
|
@@ -234,7 +253,7 @@ export class Generator {
|
|
|
234
253
|
await Promise.all(
|
|
235
254
|
tempQueue.map(async (e) => {
|
|
236
255
|
if (e.type === 'update') {
|
|
237
|
-
let cacheEntry
|
|
256
|
+
let cacheEntry: GeneratorCacheEntry | undefined
|
|
238
257
|
if (e.path === this.generatedRouteTreePath) {
|
|
239
258
|
cacheEntry = this.routeTreeFileCache
|
|
240
259
|
} else {
|
|
@@ -292,7 +311,7 @@ export class Generator {
|
|
|
292
311
|
}
|
|
293
312
|
|
|
294
313
|
private async generatorInternal() {
|
|
295
|
-
let writeRouteTreeFile = false
|
|
314
|
+
let writeRouteTreeFile: boolean | 'force' = false
|
|
296
315
|
|
|
297
316
|
let getRouteNodesResult: GetRouteNodesResult
|
|
298
317
|
|
|
@@ -302,7 +321,11 @@ export class Generator {
|
|
|
302
321
|
getRouteNodesResult = await physicalGetRouteNodes(this.config, this.root)
|
|
303
322
|
}
|
|
304
323
|
|
|
305
|
-
const {
|
|
324
|
+
const {
|
|
325
|
+
rootRouteNode,
|
|
326
|
+
routeNodes: beforeRouteNodes,
|
|
327
|
+
physicalDirectories,
|
|
328
|
+
} = getRouteNodesResult
|
|
306
329
|
if (rootRouteNode === undefined) {
|
|
307
330
|
let errorMessage = `rootRouteNode must not be undefined. Make sure you've added your root route into the route-tree.`
|
|
308
331
|
if (!this.config.virtualRouteConfig) {
|
|
@@ -310,6 +333,7 @@ export class Generator {
|
|
|
310
333
|
}
|
|
311
334
|
throw new Error(errorMessage)
|
|
312
335
|
}
|
|
336
|
+
this.physicalDirectories = physicalDirectories
|
|
313
337
|
|
|
314
338
|
writeRouteTreeFile = await this.handleRootNode(rootRouteNode)
|
|
315
339
|
|
|
@@ -376,9 +400,40 @@ export class Generator {
|
|
|
376
400
|
}
|
|
377
401
|
}
|
|
378
402
|
writeRouteTreeFile = true
|
|
403
|
+
} else {
|
|
404
|
+
const routeTreeFileChange = await this.didFileChangeComparedToCache(
|
|
405
|
+
{ path: this.generatedRouteTreePath },
|
|
406
|
+
this.routeTreeFileCache,
|
|
407
|
+
)
|
|
408
|
+
if (routeTreeFileChange.result !== false) {
|
|
409
|
+
writeRouteTreeFile = 'force'
|
|
410
|
+
if (routeTreeFileChange.result === true) {
|
|
411
|
+
const routeTreeFile = await this.fs.readFile(
|
|
412
|
+
this.generatedRouteTreePath,
|
|
413
|
+
)
|
|
414
|
+
if (routeTreeFile !== 'file-not-existing') {
|
|
415
|
+
this.routeTreeFileCache = {
|
|
416
|
+
fileContent: routeTreeFile.fileContent,
|
|
417
|
+
mtimeMs: routeTreeFile.stat.mtimeMs,
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (!writeRouteTreeFile) {
|
|
425
|
+
// only needs to be done if no other changes have been detected yet
|
|
426
|
+
// compare shadowCache and cache to identify deleted routes
|
|
427
|
+
for (const fullPath of this.routeNodeCache.keys()) {
|
|
428
|
+
if (!this.routeNodeShadowCache.has(fullPath)) {
|
|
429
|
+
writeRouteTreeFile = true
|
|
430
|
+
break
|
|
431
|
+
}
|
|
432
|
+
}
|
|
379
433
|
}
|
|
380
434
|
|
|
381
435
|
if (!writeRouteTreeFile) {
|
|
436
|
+
this.swapCaches()
|
|
382
437
|
return
|
|
383
438
|
}
|
|
384
439
|
|
|
@@ -393,7 +448,10 @@ export class Generator {
|
|
|
393
448
|
|
|
394
449
|
let newMtimeMs: bigint | undefined
|
|
395
450
|
if (this.routeTreeFileCache) {
|
|
396
|
-
if (
|
|
451
|
+
if (
|
|
452
|
+
writeRouteTreeFile !== 'force' &&
|
|
453
|
+
this.routeTreeFileCache.fileContent === routeTreeContent
|
|
454
|
+
) {
|
|
397
455
|
// existing route tree file is already up-to-date, don't write it
|
|
398
456
|
// we should only get here in the initial run when the route cache is not filled yet
|
|
399
457
|
} else {
|
|
@@ -425,7 +483,10 @@ export class Generator {
|
|
|
425
483
|
}
|
|
426
484
|
}
|
|
427
485
|
|
|
428
|
-
|
|
486
|
+
this.swapCaches()
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
private swapCaches() {
|
|
429
490
|
this.routeNodeCache = this.routeNodeShadowCache
|
|
430
491
|
this.routeNodeShadowCache = new Map()
|
|
431
492
|
}
|
|
@@ -602,16 +663,18 @@ export class Generator {
|
|
|
602
663
|
if (!this.config.disableTypes && hasMatchingRouteFiles) {
|
|
603
664
|
fileRoutesByFullPathPerPlugin = [
|
|
604
665
|
`export interface File${exportName}sByFullPath {
|
|
605
|
-
${[...createRouteNodesByFullPath(acc.routeNodes).entries()]
|
|
606
|
-
([fullPath
|
|
666
|
+
${[...createRouteNodesByFullPath(acc.routeNodes).entries()]
|
|
667
|
+
.filter(([fullPath]) => fullPath)
|
|
668
|
+
.map(([fullPath, routeNode]) => {
|
|
607
669
|
return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`
|
|
608
|
-
}
|
|
609
|
-
)}
|
|
670
|
+
})}
|
|
610
671
|
}`,
|
|
611
672
|
`export interface File${exportName}sByTo {
|
|
612
|
-
${[...createRouteNodesByTo(acc.routeNodes).entries()]
|
|
613
|
-
|
|
614
|
-
|
|
673
|
+
${[...createRouteNodesByTo(acc.routeNodes).entries()]
|
|
674
|
+
.filter(([to]) => to)
|
|
675
|
+
.map(([to, routeNode]) => {
|
|
676
|
+
return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode, exportName)}`
|
|
677
|
+
})}
|
|
615
678
|
}`,
|
|
616
679
|
`export interface File${exportName}sById {
|
|
617
680
|
'${rootRouteId}': typeof root${exportName}Import,
|
|
@@ -621,9 +684,23 @@ ${[...createRouteNodesById(acc.routeNodes).entries()].map(([id, routeNode]) => {
|
|
|
621
684
|
}`,
|
|
622
685
|
`export interface File${exportName}Types {
|
|
623
686
|
file${exportName}sByFullPath: File${exportName}sByFullPath
|
|
624
|
-
fullPaths: ${
|
|
687
|
+
fullPaths: ${
|
|
688
|
+
acc.routeNodes.length > 0
|
|
689
|
+
? [...createRouteNodesByFullPath(acc.routeNodes).keys()]
|
|
690
|
+
.filter((fullPath) => fullPath)
|
|
691
|
+
.map((fullPath) => `'${fullPath}'`)
|
|
692
|
+
.join('|')
|
|
693
|
+
: 'never'
|
|
694
|
+
}
|
|
625
695
|
file${exportName}sByTo: File${exportName}sByTo
|
|
626
|
-
to: ${
|
|
696
|
+
to: ${
|
|
697
|
+
acc.routeNodes.length > 0
|
|
698
|
+
? [...createRouteNodesByTo(acc.routeNodes).keys()]
|
|
699
|
+
.filter((to) => to)
|
|
700
|
+
.map((to) => `'${to}'`)
|
|
701
|
+
.join('|')
|
|
702
|
+
: 'never'
|
|
703
|
+
}
|
|
627
704
|
id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(acc.routeNodes).keys()].map((id) => `'${id}'`)].join('|')}
|
|
628
705
|
file${exportName}sById: File${exportName}sById
|
|
629
706
|
}`,
|
|
@@ -634,7 +711,13 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
634
711
|
|
|
635
712
|
fileRoutesByPathInterfacePerPlugin = buildFileRoutesByPathInterface({
|
|
636
713
|
...plugin.moduleAugmentation({ generator: this }),
|
|
637
|
-
routeNodes:
|
|
714
|
+
routeNodes:
|
|
715
|
+
this.config.verboseFileRoutes !== false
|
|
716
|
+
? sortedRouteNodes
|
|
717
|
+
: [
|
|
718
|
+
...routeFileResult.map(({ node }) => node),
|
|
719
|
+
...sortedRouteNodes.filter((d) => d.isVirtual),
|
|
720
|
+
],
|
|
638
721
|
exportName,
|
|
639
722
|
})
|
|
640
723
|
}
|
|
@@ -784,6 +867,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
784
867
|
fileContent: existingRouteFile.fileContent,
|
|
785
868
|
mtimeMs: existingRouteFile.stat.mtimeMs,
|
|
786
869
|
exports: [],
|
|
870
|
+
routeId: node.routePath ?? '$$TSR_NO_ROUTE_PATH_ASSIGNED$$',
|
|
787
871
|
}
|
|
788
872
|
|
|
789
873
|
const escapedRoutePath = node.routePath?.replaceAll('$', '$$') ?? ''
|
|
@@ -804,7 +888,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
804
888
|
tLazyRouteTemplate.template(),
|
|
805
889
|
{
|
|
806
890
|
tsrImports: tLazyRouteTemplate.imports.tsrImports(),
|
|
807
|
-
tsrPath: escapedRoutePath.replaceAll(/\{(
|
|
891
|
+
tsrPath: escapedRoutePath.replaceAll(/\{(.+?)\}/gm, '$1'),
|
|
808
892
|
tsrExportStart:
|
|
809
893
|
tLazyRouteTemplate.imports.tsrExportStart(escapedRoutePath),
|
|
810
894
|
tsrExportEnd: tLazyRouteTemplate.imports.tsrExportEnd(),
|
|
@@ -832,7 +916,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
832
916
|
tRouteTemplate.template(),
|
|
833
917
|
{
|
|
834
918
|
tsrImports: tRouteTemplate.imports.tsrImports(),
|
|
835
|
-
tsrPath: escapedRoutePath.replaceAll(/\{(
|
|
919
|
+
tsrPath: escapedRoutePath.replaceAll(/\{(.+?)\}/gm, '$1'),
|
|
836
920
|
tsrExportStart:
|
|
837
921
|
tRouteTemplate.imports.tsrExportStart(escapedRoutePath),
|
|
838
922
|
tsrExportEnd: tRouteTemplate.imports.tsrExportEnd(),
|
|
@@ -956,6 +1040,31 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
956
1040
|
event: { type: 'update', path: opts.filePath },
|
|
957
1041
|
})
|
|
958
1042
|
}
|
|
1043
|
+
const newFileState = await this.fs.stat(tmpPath)
|
|
1044
|
+
if (newFileState.mode !== beforeStat.mode) {
|
|
1045
|
+
await this.fs.chmod(tmpPath, beforeStat.mode)
|
|
1046
|
+
}
|
|
1047
|
+
if (
|
|
1048
|
+
newFileState.uid !== beforeStat.uid ||
|
|
1049
|
+
newFileState.gid !== beforeStat.gid
|
|
1050
|
+
) {
|
|
1051
|
+
try {
|
|
1052
|
+
await this.fs.chown(tmpPath, beforeStat.uid, beforeStat.gid)
|
|
1053
|
+
} catch (err) {
|
|
1054
|
+
if (
|
|
1055
|
+
typeof err === 'object' &&
|
|
1056
|
+
err !== null &&
|
|
1057
|
+
'code' in err &&
|
|
1058
|
+
(err as any).code === 'EPERM'
|
|
1059
|
+
) {
|
|
1060
|
+
console.warn(
|
|
1061
|
+
`[safeFileWrite] chown failed: ${(err as any).message}`,
|
|
1062
|
+
)
|
|
1063
|
+
} else {
|
|
1064
|
+
throw err
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
959
1068
|
} else {
|
|
960
1069
|
if (await checkFileExists(opts.filePath)) {
|
|
961
1070
|
throw rerun({
|
|
@@ -975,7 +1084,13 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
975
1084
|
private getTempFileName(filePath: string) {
|
|
976
1085
|
const absPath = path.resolve(filePath)
|
|
977
1086
|
const hash = crypto.createHash('md5').update(absPath).digest('hex')
|
|
978
|
-
|
|
1087
|
+
// lazy initialize sessionId to only create tmpDir when it is first needed
|
|
1088
|
+
if (!this.sessionId) {
|
|
1089
|
+
// ensure the directory exists
|
|
1090
|
+
mkdirSync(this.config.tmpDir, { recursive: true })
|
|
1091
|
+
this.sessionId = crypto.randomBytes(4).toString('hex')
|
|
1092
|
+
}
|
|
1093
|
+
return path.join(this.config.tmpDir, `${this.sessionId}-${hash}`)
|
|
979
1094
|
}
|
|
980
1095
|
|
|
981
1096
|
private async isRouteFileCacheFresh(node: RouteNode): Promise<
|
|
@@ -1050,7 +1165,9 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1050
1165
|
const result = await this.isRouteFileCacheFresh(node)
|
|
1051
1166
|
|
|
1052
1167
|
if (result.status === 'fresh') {
|
|
1053
|
-
|
|
1168
|
+
node.exports = result.cacheEntry.exports
|
|
1169
|
+
this.routeNodeShadowCache.set(node.fullPath, result.cacheEntry)
|
|
1170
|
+
return result.exportsChanged
|
|
1054
1171
|
}
|
|
1055
1172
|
const rootNodeFile = await this.fs.readFile(node.fullPath)
|
|
1056
1173
|
if (rootNodeFile === 'file-not-existing') {
|
|
@@ -1061,6 +1178,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1061
1178
|
fileContent: rootNodeFile.fileContent,
|
|
1062
1179
|
mtimeMs: rootNodeFile.stat.mtimeMs,
|
|
1063
1180
|
exports: [],
|
|
1181
|
+
routeId: node.routePath ?? '$$TSR_NO_ROOT_ROUTE_PATH_ASSIGNED$$',
|
|
1064
1182
|
}
|
|
1065
1183
|
|
|
1066
1184
|
// scaffold the root route
|
|
@@ -1093,6 +1211,8 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1093
1211
|
const rootRouteExports: Array<string> = []
|
|
1094
1212
|
for (const plugin of this.pluginsWithTransform) {
|
|
1095
1213
|
const exportName = plugin.transformPlugin.exportName
|
|
1214
|
+
// TODO we need to parse instead of just string match
|
|
1215
|
+
// otherwise a commented out export will still be detected
|
|
1096
1216
|
if (rootNodeFile.fileContent.includes(`export const ${exportName}`)) {
|
|
1097
1217
|
rootRouteExports.push(exportName)
|
|
1098
1218
|
}
|
|
@@ -1141,7 +1261,7 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1141
1261
|
|
|
1142
1262
|
node.isNonPath =
|
|
1143
1263
|
lastRouteSegment.startsWith('_') ||
|
|
1144
|
-
this.routeGroupPatternRegex.test(
|
|
1264
|
+
split.every((part) => this.routeGroupPatternRegex.test(part))
|
|
1145
1265
|
|
|
1146
1266
|
node.cleanedPath = removeGroups(
|
|
1147
1267
|
removeUnderscores(removeLayoutSegments(node.path)) ?? '',
|
|
@@ -1252,38 +1372,42 @@ ${acc.routeTree.map((child) => `${child.variableName}${exportName}: typeof ${get
|
|
|
1252
1372
|
|
|
1253
1373
|
acc.routeNodes.push(node)
|
|
1254
1374
|
}
|
|
1255
|
-
}
|
|
1256
1375
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1263
|
-
return `declare module '${opts.module}' {
|
|
1264
|
-
interface ${opts.interfaceName} {
|
|
1265
|
-
${opts.routeNodes
|
|
1266
|
-
.map((routeNode) => {
|
|
1267
|
-
const filePathId = routeNode.routePath
|
|
1268
|
-
let preloaderRoute = ''
|
|
1269
|
-
|
|
1270
|
-
if (routeNode.exports?.includes(opts.exportName)) {
|
|
1271
|
-
preloaderRoute = `typeof ${routeNode.variableName}${opts.exportName}Import`
|
|
1272
|
-
} else {
|
|
1273
|
-
preloaderRoute = 'unknown'
|
|
1274
|
-
}
|
|
1376
|
+
// only process files that are relevant for the route tree generation
|
|
1377
|
+
private isFileRelevantForRouteTreeGeneration(filePath: string): boolean {
|
|
1378
|
+
// the generated route tree file
|
|
1379
|
+
if (filePath === this.generatedRouteTreePath) {
|
|
1380
|
+
return true
|
|
1381
|
+
}
|
|
1275
1382
|
|
|
1276
|
-
|
|
1383
|
+
// files inside the routes folder
|
|
1384
|
+
if (filePath.startsWith(this.routesDirectoryPath)) {
|
|
1385
|
+
return true
|
|
1386
|
+
}
|
|
1277
1387
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1388
|
+
// the virtual route config file passed into `virtualRouteConfig`
|
|
1389
|
+
if (
|
|
1390
|
+
typeof this.config.virtualRouteConfig === 'string' &&
|
|
1391
|
+
filePath === this.config.virtualRouteConfig
|
|
1392
|
+
) {
|
|
1393
|
+
return true
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// this covers all files that are mounted via `virtualRouteConfig` or any `__virtual.ts` files
|
|
1397
|
+
if (this.routeNodeCache.has(filePath)) {
|
|
1398
|
+
return true
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// virtual config files such as`__virtual.ts`
|
|
1402
|
+
if (isVirtualConfigFile(path.basename(filePath))) {
|
|
1403
|
+
return true
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// route files inside directories mounted via `physical()` inside a virtual route config
|
|
1407
|
+
if (this.physicalDirectories.some((dir) => filePath.startsWith(dir))) {
|
|
1408
|
+
return true
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
return false
|
|
1287
1412
|
}
|
|
1288
|
-
}`
|
|
1289
1413
|
}
|
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 }) => {
|
|
@@ -4,9 +4,10 @@ import type { TransformImportsConfig, TransformPlugin } from './types'
|
|
|
4
4
|
|
|
5
5
|
const b = types.builders
|
|
6
6
|
|
|
7
|
+
const EXPORT_NAME = 'Route'
|
|
7
8
|
export const defaultTransformPlugin: TransformPlugin = {
|
|
8
9
|
name: 'default-transform',
|
|
9
|
-
exportName:
|
|
10
|
+
exportName: EXPORT_NAME,
|
|
10
11
|
imports: (ctx) => {
|
|
11
12
|
const imports: TransformImportsConfig = {}
|
|
12
13
|
const targetModule = `@tanstack/${ctx.target}-router`
|
|
@@ -87,7 +88,9 @@ export const defaultTransformPlugin: TransformPlugin = {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
if (identifier === undefined) {
|
|
90
|
-
throw new Error(
|
|
91
|
+
throw new Error(
|
|
92
|
+
`expected identifier to be present in ${ctx.routeId} for export ${EXPORT_NAME}`,
|
|
93
|
+
)
|
|
91
94
|
}
|
|
92
95
|
if (identifier.name === 'createFileRoute' && ctx.lazy) {
|
|
93
96
|
identifier.name = 'createLazyFileRoute'
|