@tanstack/router-generator 1.121.0-alpha.14 → 1.121.0-alpha.26

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 (90) hide show
  1. package/dist/cjs/config.cjs +21 -5
  2. package/dist/cjs/config.cjs.map +1 -1
  3. package/dist/cjs/config.d.cts +7 -3
  4. package/dist/cjs/filesystem/physical/getRouteNodes.cjs +5 -3
  5. package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
  6. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +1 -1
  7. package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
  8. package/dist/cjs/generator.cjs +825 -664
  9. package/dist/cjs/generator.cjs.map +1 -1
  10. package/dist/cjs/generator.d.cts +78 -1
  11. package/dist/cjs/index.cjs +5 -2
  12. package/dist/cjs/index.cjs.map +1 -1
  13. package/dist/cjs/index.d.cts +7 -3
  14. package/dist/cjs/logger.cjs +37 -0
  15. package/dist/cjs/logger.cjs.map +1 -0
  16. package/dist/cjs/logger.d.cts +10 -0
  17. package/dist/cjs/plugin/default-generator-plugin.cjs +88 -0
  18. package/dist/cjs/plugin/default-generator-plugin.cjs.map +1 -0
  19. package/dist/cjs/plugin/default-generator-plugin.d.cts +2 -0
  20. package/dist/cjs/plugin/types.d.cts +46 -0
  21. package/dist/cjs/template.cjs +10 -10
  22. package/dist/cjs/template.cjs.map +1 -1
  23. package/dist/cjs/template.d.cts +2 -2
  24. package/dist/cjs/transform/default-transform-plugin.cjs +95 -0
  25. package/dist/cjs/transform/default-transform-plugin.cjs.map +1 -0
  26. package/dist/cjs/transform/default-transform-plugin.d.cts +2 -0
  27. package/dist/cjs/transform/transform.cjs +356 -0
  28. package/dist/cjs/transform/transform.cjs.map +1 -0
  29. package/dist/cjs/transform/transform.d.cts +4 -0
  30. package/dist/cjs/transform/types.d.cts +43 -0
  31. package/dist/cjs/transform/utils.cjs +36 -0
  32. package/dist/cjs/transform/utils.cjs.map +1 -0
  33. package/dist/cjs/transform/utils.d.cts +2 -0
  34. package/dist/cjs/types.d.cts +22 -0
  35. package/dist/cjs/utils.cjs +237 -40
  36. package/dist/cjs/utils.cjs.map +1 -1
  37. package/dist/cjs/utils.d.cts +76 -9
  38. package/dist/esm/config.d.ts +7 -3
  39. package/dist/esm/config.js +19 -3
  40. package/dist/esm/config.js.map +1 -1
  41. package/dist/esm/filesystem/physical/getRouteNodes.js +3 -1
  42. package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
  43. package/dist/esm/filesystem/virtual/getRouteNodes.js +1 -1
  44. package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
  45. package/dist/esm/generator.d.ts +78 -1
  46. package/dist/esm/generator.js +814 -652
  47. package/dist/esm/generator.js.map +1 -1
  48. package/dist/esm/index.d.ts +7 -3
  49. package/dist/esm/index.js +7 -4
  50. package/dist/esm/index.js.map +1 -1
  51. package/dist/esm/logger.d.ts +10 -0
  52. package/dist/esm/logger.js +37 -0
  53. package/dist/esm/logger.js.map +1 -0
  54. package/dist/esm/plugin/default-generator-plugin.d.ts +2 -0
  55. package/dist/esm/plugin/default-generator-plugin.js +88 -0
  56. package/dist/esm/plugin/default-generator-plugin.js.map +1 -0
  57. package/dist/esm/plugin/types.d.ts +46 -0
  58. package/dist/esm/template.d.ts +2 -2
  59. package/dist/esm/template.js +10 -10
  60. package/dist/esm/template.js.map +1 -1
  61. package/dist/esm/transform/default-transform-plugin.d.ts +2 -0
  62. package/dist/esm/transform/default-transform-plugin.js +95 -0
  63. package/dist/esm/transform/default-transform-plugin.js.map +1 -0
  64. package/dist/esm/transform/transform.d.ts +4 -0
  65. package/dist/esm/transform/transform.js +356 -0
  66. package/dist/esm/transform/transform.js.map +1 -0
  67. package/dist/esm/transform/types.d.ts +43 -0
  68. package/dist/esm/transform/utils.d.ts +2 -0
  69. package/dist/esm/transform/utils.js +36 -0
  70. package/dist/esm/transform/utils.js.map +1 -0
  71. package/dist/esm/types.d.ts +22 -0
  72. package/dist/esm/utils.d.ts +76 -9
  73. package/dist/esm/utils.js +237 -40
  74. package/dist/esm/utils.js.map +1 -1
  75. package/package.json +8 -9
  76. package/src/config.ts +21 -2
  77. package/src/filesystem/physical/getRouteNodes.ts +2 -1
  78. package/src/filesystem/virtual/getRouteNodes.ts +1 -1
  79. package/src/generator.ts +1106 -934
  80. package/src/index.ts +25 -3
  81. package/src/logger.ts +43 -0
  82. package/src/plugin/default-generator-plugin.ts +96 -0
  83. package/src/plugin/types.ts +51 -0
  84. package/src/template.ts +33 -12
  85. package/src/transform/default-transform-plugin.ts +103 -0
  86. package/src/transform/transform.ts +439 -0
  87. package/src/transform/types.ts +50 -0
  88. package/src/transform/utils.ts +42 -0
  89. package/src/types.ts +25 -0
  90. package/src/utils.ts +351 -36
@@ -0,0 +1,439 @@
1
+ import { parseAst } from '@tanstack/router-utils'
2
+ import { parse, print, types, visit } from 'recast'
3
+ import { SourceMapConsumer } from 'source-map'
4
+ import { mergeImportDeclarations } from '../utils'
5
+ import type { ImportDeclaration } from '../types'
6
+ import type { RawSourceMap } from 'source-map'
7
+ import type {
8
+ TransformOptions,
9
+ TransformPlugin,
10
+ TransformResult,
11
+ } from './types'
12
+
13
+ const b = types.builders
14
+
15
+ export async function transform({
16
+ ctx,
17
+ source,
18
+ plugins,
19
+ }: TransformOptions): Promise<TransformResult> {
20
+ let appliedChanges = false as boolean
21
+ let ast: types.namedTypes.File
22
+ const foundExports: Array<string> = []
23
+ try {
24
+ ast = parse(source, {
25
+ sourceFileName: 'output.ts',
26
+ parser: {
27
+ parse(code: string) {
28
+ return parseAst({
29
+ code,
30
+ // we need to instruct babel to produce tokens,
31
+ // otherwise recast will try to generate the tokens via its own parser and will fail
32
+ tokens: true,
33
+ })
34
+ },
35
+ },
36
+ })
37
+ } catch (e) {
38
+ console.error('Error parsing code', ctx.routeId, source, e)
39
+ return {
40
+ result: 'error',
41
+ error: e,
42
+ }
43
+ }
44
+
45
+ const preferredQuote = detectPreferredQuoteStyle(ast)
46
+
47
+ const registeredExports = new Map</* export name */ string, TransformPlugin>()
48
+
49
+ for (const plugin of plugins ?? []) {
50
+ const exportName = plugin.exportName
51
+ if (registeredExports.has(exportName)) {
52
+ throw new Error(
53
+ `Export ${exportName} is already registered by plugin ${registeredExports.get(exportName)?.name}`,
54
+ )
55
+ }
56
+ registeredExports.set(exportName, plugin)
57
+ }
58
+
59
+ const program: types.namedTypes.Program = ast.program
60
+ // first pass: find registered exports
61
+ 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
82
+ }
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
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ const imports: {
94
+ required: Array<ImportDeclaration>
95
+ banned: Array<ImportDeclaration>
96
+ } = {
97
+ required: [],
98
+ banned: [],
99
+ }
100
+
101
+ for (const plugin of plugins ?? []) {
102
+ const exportName = plugin.exportName
103
+ if (foundExports.includes(exportName)) {
104
+ const pluginImports = plugin.imports(ctx)
105
+ if (pluginImports.required) {
106
+ imports.required.push(...pluginImports.required)
107
+ }
108
+ if (pluginImports.banned) {
109
+ imports.banned.push(...pluginImports.banned)
110
+ }
111
+ }
112
+ }
113
+
114
+ imports.required = mergeImportDeclarations(imports.required)
115
+ imports.banned = mergeImportDeclarations(imports.banned)
116
+
117
+ const importStatementCandidates: Array<types.namedTypes.ImportDeclaration> =
118
+ []
119
+ const importDeclarationsToRemove: Array<types.namedTypes.ImportDeclaration> =
120
+ []
121
+
122
+ // second pass: apply import rules, but only if a matching export for the plugin was found
123
+ for (const n of program.body) {
124
+ const findImport =
125
+ (opts: { source: string; importKind?: 'type' | 'value' | 'typeof' }) =>
126
+ (i: ImportDeclaration) => {
127
+ if (i.source === opts.source) {
128
+ const importKind = i.importKind || 'value'
129
+ const expectedImportKind = opts.importKind || 'value'
130
+ return expectedImportKind === importKind
131
+ }
132
+ return false
133
+ }
134
+ if (n.type === 'ImportDeclaration' && typeof n.source.value === 'string') {
135
+ const filterImport = findImport({
136
+ source: n.source.value,
137
+ importKind: n.importKind,
138
+ })
139
+ let requiredImports = imports.required.filter(filterImport)[0]
140
+
141
+ const bannedImports = imports.banned.filter(filterImport)[0]
142
+ if (!requiredImports && !bannedImports) {
143
+ continue
144
+ }
145
+ const importSpecifiersToRemove: types.namedTypes.ImportDeclaration['specifiers'] =
146
+ []
147
+ if (n.specifiers) {
148
+ for (const spec of n.specifiers) {
149
+ if (!requiredImports && !bannedImports) {
150
+ break
151
+ }
152
+ if (
153
+ spec.type === 'ImportSpecifier' &&
154
+ typeof spec.imported.name === 'string'
155
+ ) {
156
+ if (requiredImports) {
157
+ const requiredImportIndex = requiredImports.specifiers.findIndex(
158
+ (imp) => imp.imported === spec.imported.name,
159
+ )
160
+ if (requiredImportIndex !== -1) {
161
+ // import is already present, remove it from requiredImports
162
+ requiredImports.specifiers.splice(requiredImportIndex, 1)
163
+ if (requiredImports.specifiers.length === 0) {
164
+ imports.required = imports.required.splice(
165
+ imports.required.indexOf(requiredImports),
166
+ 1,
167
+ )
168
+ requiredImports = undefined
169
+ }
170
+ } else {
171
+ // add the import statement to the candidates
172
+ importStatementCandidates.push(n)
173
+ }
174
+ }
175
+ if (bannedImports) {
176
+ const bannedImportIndex = bannedImports.specifiers.findIndex(
177
+ (imp) => imp.imported === spec.imported.name,
178
+ )
179
+ if (bannedImportIndex !== -1) {
180
+ importSpecifiersToRemove.push(spec)
181
+ }
182
+ }
183
+ }
184
+ }
185
+ if (importSpecifiersToRemove.length > 0) {
186
+ appliedChanges = true
187
+ n.specifiers = n.specifiers.filter(
188
+ (spec) => !importSpecifiersToRemove.includes(spec),
189
+ )
190
+
191
+ // mark the import statement as to be deleted if it is now empty
192
+ if (n.specifiers.length === 0) {
193
+ importDeclarationsToRemove.push(n)
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+ imports.required.forEach((requiredImport) => {
200
+ if (requiredImport.specifiers.length > 0) {
201
+ appliedChanges = true
202
+ if (importStatementCandidates.length > 0) {
203
+ // find the first import statement that matches both the module and the import kind
204
+ const importStatement = importStatementCandidates.find(
205
+ (importStatement) => {
206
+ if (importStatement.source.value === requiredImport.source) {
207
+ const importKind = importStatement.importKind || 'value'
208
+ const requiredImportKind = requiredImport.importKind || 'value'
209
+ return importKind === requiredImportKind
210
+ }
211
+ return false
212
+ },
213
+ )
214
+ if (importStatement) {
215
+ if (importStatement.specifiers === undefined) {
216
+ importStatement.specifiers = []
217
+ }
218
+ const importSpecifiersToAdd = requiredImport.specifiers.map((spec) =>
219
+ b.importSpecifier(
220
+ b.identifier(spec.imported),
221
+ b.identifier(spec.imported),
222
+ ),
223
+ )
224
+ importStatement.specifiers = [
225
+ ...importStatement.specifiers,
226
+ ...importSpecifiersToAdd,
227
+ ]
228
+ return
229
+ }
230
+ }
231
+ const importStatement = b.importDeclaration(
232
+ requiredImport.specifiers.map((spec) =>
233
+ b.importSpecifier(
234
+ b.identifier(spec.imported),
235
+ spec.local ? b.identifier(spec.local) : null,
236
+ ),
237
+ ),
238
+ b.stringLiteral(requiredImport.source),
239
+ )
240
+ program.body.unshift(importStatement)
241
+ }
242
+ })
243
+ if (importDeclarationsToRemove.length > 0) {
244
+ appliedChanges = true
245
+ for (const importDeclaration of importDeclarationsToRemove) {
246
+ // check if the import declaration is still empty
247
+ if (importDeclaration.specifiers?.length === 0) {
248
+ const index = program.body.indexOf(importDeclaration)
249
+ if (index !== -1) {
250
+ program.body.splice(index, 1)
251
+ }
252
+ }
253
+ }
254
+ }
255
+
256
+ if (!appliedChanges) {
257
+ return {
258
+ exports: foundExports,
259
+ result: 'not-modified',
260
+ }
261
+ }
262
+
263
+ const printResult = print(ast, {
264
+ reuseWhitespace: true,
265
+ sourceMapName: 'output.map',
266
+ })
267
+ let transformedCode = printResult.code
268
+ if (printResult.map) {
269
+ const fixedOutput = await fixTransformedOutputText({
270
+ originalCode: source,
271
+ transformedCode,
272
+ sourceMap: printResult.map as RawSourceMap,
273
+ preferredQuote,
274
+ })
275
+ transformedCode = fixedOutput
276
+ }
277
+ return {
278
+ result: 'modified',
279
+ exports: foundExports,
280
+ output: transformedCode,
281
+ }
282
+ }
283
+
284
+ async function fixTransformedOutputText({
285
+ originalCode,
286
+ transformedCode,
287
+ sourceMap,
288
+ preferredQuote,
289
+ }: {
290
+ originalCode: string
291
+ transformedCode: string
292
+ sourceMap: RawSourceMap
293
+ preferredQuote: '"' | "'" | '`'
294
+ }) {
295
+ const originalLines = originalCode.split('\n')
296
+ const transformedLines = transformedCode.split('\n')
297
+
298
+ const defaultUsesSemicolons = detectSemicolonUsage(originalCode)
299
+
300
+ const consumer = await new SourceMapConsumer(sourceMap)
301
+
302
+ const fixedLines = transformedLines.map((line, i) => {
303
+ const transformedLineNum = i + 1
304
+
305
+ let mapped = null
306
+ let origLineText = null
307
+
308
+ for (let col = 0; col < line.length; col++) {
309
+ mapped = consumer.originalPositionFor({
310
+ line: transformedLineNum,
311
+ column: col,
312
+ })
313
+ if (mapped.line != null && mapped.line > 0) {
314
+ origLineText = originalLines[mapped.line - 1]
315
+ break
316
+ }
317
+ }
318
+
319
+ if (origLineText != null) {
320
+ if (origLineText === line) {
321
+ return origLineText
322
+ }
323
+ return fixLine(line, {
324
+ originalLine: origLineText,
325
+ useOriginalSemicolon: true,
326
+ useOriginalQuotes: true,
327
+ fallbackQuote: preferredQuote,
328
+ })
329
+ } else {
330
+ return fixLine(line, {
331
+ originalLine: null,
332
+ useOriginalSemicolon: false,
333
+ useOriginalQuotes: false,
334
+ fallbackQuote: preferredQuote,
335
+ fallbackSemicolon: defaultUsesSemicolons,
336
+ })
337
+ }
338
+ })
339
+
340
+ return fixedLines.join('\n')
341
+ }
342
+
343
+ function fixLine(
344
+ line: string,
345
+ {
346
+ originalLine,
347
+ useOriginalSemicolon,
348
+ useOriginalQuotes,
349
+ fallbackQuote,
350
+ fallbackSemicolon = true,
351
+ }: {
352
+ originalLine: string | null
353
+ useOriginalSemicolon: boolean
354
+ useOriginalQuotes: boolean
355
+ fallbackQuote: string
356
+ fallbackSemicolon?: boolean
357
+ },
358
+ ) {
359
+ let result = line
360
+
361
+ if (useOriginalQuotes && originalLine) {
362
+ result = fixQuotes(result, originalLine, fallbackQuote)
363
+ } else if (!useOriginalQuotes && fallbackQuote) {
364
+ result = fixQuotesToPreferred(result, fallbackQuote)
365
+ }
366
+
367
+ if (useOriginalSemicolon && originalLine) {
368
+ const hadSemicolon = originalLine.trimEnd().endsWith(';')
369
+ const hasSemicolon = result.trimEnd().endsWith(';')
370
+ if (hadSemicolon && !hasSemicolon) result += ';'
371
+ if (!hadSemicolon && hasSemicolon) result = result.replace(/;\s*$/, '')
372
+ } else if (!useOriginalSemicolon) {
373
+ const hasSemicolon = result.trimEnd().endsWith(';')
374
+ if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\s*$/, '')
375
+ if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ';'
376
+ }
377
+
378
+ return result
379
+ }
380
+
381
+ function fixQuotes(line: string, originalLine: string, fallbackQuote: string) {
382
+ let originalQuote = detectQuoteFromLine(originalLine)
383
+ if (!originalQuote) {
384
+ originalQuote = fallbackQuote
385
+ }
386
+ return fixQuotesToPreferred(line, originalQuote)
387
+ }
388
+
389
+ function fixQuotesToPreferred(line: string, quote: string) {
390
+ // Replace existing quotes with preferred quote
391
+ return line.replace(
392
+ /(['"`])([^'"`\\]*(?:\\.[^'"`\\]*)*)\1/g,
393
+ (_, q, content) => {
394
+ const escaped = content.replaceAll(quote, `\\${quote}`)
395
+ return `${quote}${escaped}${quote}`
396
+ },
397
+ )
398
+ }
399
+
400
+ function detectQuoteFromLine(line: string) {
401
+ const match = line.match(/(['"`])(?:\\.|[^\\])*?\1/)
402
+ return match ? match[1] : null
403
+ }
404
+
405
+ function detectSemicolonUsage(code: string) {
406
+ const lines = code.split('\n').map((l) => l.trim())
407
+ const total = lines.length
408
+ const withSemis = lines.filter((l) => l.endsWith(';')).length
409
+ return withSemis > total / 2
410
+ }
411
+
412
+ export function detectPreferredQuoteStyle(ast: types.ASTNode): "'" | '"' | '`' {
413
+ let single = 0
414
+ let double = 0
415
+ let backtick = 0
416
+
417
+ visit(ast, {
418
+ visitStringLiteral(path) {
419
+ if (path.parent.node.type !== 'JSXAttribute') {
420
+ const raw = path.node.extra?.raw
421
+ if (raw?.startsWith("'")) single++
422
+ else if (raw?.startsWith('"')) double++
423
+ }
424
+ return false
425
+ },
426
+ visitTemplateLiteral(path) {
427
+ if (path.parent.type !== 'JSXAttribute') {
428
+ backtick++
429
+ }
430
+ return false
431
+ },
432
+ })
433
+
434
+ return single >= double && single >= backtick
435
+ ? "'"
436
+ : double >= single && double >= backtick
437
+ ? '"'
438
+ : '`'
439
+ }
@@ -0,0 +1,50 @@
1
+ import type { ImportDeclaration } from '../types'
2
+ import type { types } from 'recast'
3
+ import type { Config } from '../config'
4
+
5
+ export interface TransformOptions {
6
+ source: string
7
+ ctx: TransformContext
8
+ plugins?: Array<TransformPlugin>
9
+ }
10
+
11
+ export type TransformResult =
12
+ | {
13
+ result: 'not-modified'
14
+ exports: Array<string>
15
+ }
16
+ | {
17
+ result: 'modified'
18
+ output: string
19
+ exports: Array<string>
20
+ }
21
+ | {
22
+ result: 'error'
23
+ error?: any
24
+ }
25
+
26
+ export interface TransformImportsConfig {
27
+ banned?: Array<ImportDeclaration>
28
+ required?: Array<ImportDeclaration>
29
+ }
30
+ export interface TransformPlugin {
31
+ name: string
32
+ exportName: string
33
+ imports: (ctx: TransformContext) => TransformImportsConfig
34
+ /**
35
+ * Called after the export is found in the AST.
36
+ * @returns true if the plugin modified the AST, false otherwise
37
+ */
38
+ onExportFound: (opts: {
39
+ decl: types.namedTypes.VariableDeclarator
40
+ ctx: TransformContext
41
+ }) => boolean
42
+ }
43
+
44
+ export interface TransformContext {
45
+ target: Config['target']
46
+ routeId: string
47
+ lazy: boolean
48
+ verboseFileRoutes: boolean
49
+ preferredQuote?: '"' | "'" | '`'
50
+ }
@@ -0,0 +1,42 @@
1
+ import { types } from 'recast'
2
+
3
+ const b = types.builders
4
+
5
+ export function ensureStringArgument(
6
+ callExpression: types.namedTypes.CallExpression,
7
+ value: string,
8
+ preferredQuote?: "'" | '"' | '`',
9
+ ) {
10
+ const argument = callExpression.arguments[0]
11
+ if (!argument) {
12
+ let stringLiteral: types.namedTypes.StringLiteral
13
+ if (!preferredQuote) {
14
+ stringLiteral = b.stringLiteral.from({ value })
15
+ } else {
16
+ stringLiteral = b.stringLiteral.from({
17
+ value,
18
+ extra: {
19
+ rawValue: value,
20
+ raw: `${preferredQuote}${value}${preferredQuote}`,
21
+ },
22
+ })
23
+ }
24
+ callExpression.arguments.push(stringLiteral)
25
+ return true
26
+ } else if (argument.type === 'StringLiteral') {
27
+ if (argument.value !== value) {
28
+ argument.value = value
29
+ return true
30
+ }
31
+ } else if (argument.type === 'TemplateLiteral') {
32
+ if (
33
+ argument.quasis.length === 1 &&
34
+ argument.quasis[0] &&
35
+ argument.quasis[0].value.raw !== value
36
+ ) {
37
+ argument.quasis[0].value.raw = value
38
+ return true
39
+ }
40
+ }
41
+ return false
42
+ }
package/src/types.ts CHANGED
@@ -12,6 +12,7 @@ export type RouteNode = {
12
12
  isVirtual?: boolean
13
13
  children?: Array<RouteNode>
14
14
  parent?: RouteNode
15
+ exports?: Array<string>
15
16
  }
16
17
 
17
18
  export interface GetRouteNodesResult {
@@ -29,3 +30,27 @@ export type FsRouteType =
29
30
  | 'component' // @deprecated
30
31
  | 'pendingComponent' // @deprecated
31
32
  | 'errorComponent' // @deprecated
33
+
34
+ export type RouteSubNode = {
35
+ component?: RouteNode
36
+ errorComponent?: RouteNode
37
+ pendingComponent?: RouteNode
38
+ loader?: RouteNode
39
+ lazy?: RouteNode
40
+ }
41
+
42
+ export type ImportSpecifier = {
43
+ imported: string
44
+ local?: string
45
+ }
46
+ export type ImportDeclaration = {
47
+ source: string
48
+ specifiers: Array<ImportSpecifier>
49
+ importKind?: 'type' | 'value'
50
+ }
51
+
52
+ export type HandleNodeAccumulator = {
53
+ routeTree: Array<RouteNode>
54
+ routePiecesByPath: Record<string, RouteSubNode>
55
+ routeNodes: Array<RouteNode>
56
+ }