@tanstack/start-plugin-core 1.121.0-alpha.8 → 1.121.0

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 (187) hide show
  1. package/dist/cjs/compilers.cjs +24 -10
  2. package/dist/cjs/compilers.cjs.map +1 -1
  3. package/dist/cjs/constants.cjs +10 -0
  4. package/dist/cjs/constants.cjs.map +1 -1
  5. package/dist/cjs/constants.d.cts +2 -0
  6. package/dist/cjs/debug.cjs +5 -0
  7. package/dist/cjs/debug.cjs.map +1 -0
  8. package/dist/cjs/debug.d.cts +1 -0
  9. package/dist/cjs/{extractHtmlScripts.cjs → dev-server-plugin/extract-html-scripts.cjs} +1 -1
  10. package/dist/cjs/dev-server-plugin/extract-html-scripts.cjs.map +1 -0
  11. package/dist/cjs/{nitro/dev-server-plugin.cjs → dev-server-plugin/plugin.cjs} +2 -2
  12. package/dist/cjs/dev-server-plugin/plugin.cjs.map +1 -0
  13. package/dist/cjs/load-env-plugin/plugin.cjs +34 -0
  14. package/dist/cjs/load-env-plugin/plugin.cjs.map +1 -0
  15. package/dist/cjs/load-env-plugin/plugin.d.cts +3 -0
  16. package/dist/cjs/{build-sitemap.cjs → nitro-plugin/build-sitemap.cjs} +12 -10
  17. package/dist/cjs/nitro-plugin/build-sitemap.cjs.map +1 -0
  18. package/dist/cjs/{build-sitemap.d.cts → nitro-plugin/build-sitemap.d.cts} +2 -2
  19. package/dist/cjs/{nitro/nitro-plugin.cjs → nitro-plugin/plugin.cjs} +79 -65
  20. package/dist/cjs/nitro-plugin/plugin.cjs.map +1 -0
  21. package/dist/cjs/{prerender.cjs → nitro-plugin/prerender.cjs} +18 -14
  22. package/dist/cjs/nitro-plugin/prerender.cjs.map +1 -0
  23. package/dist/cjs/{prerender.d.cts → nitro-plugin/prerender.d.cts} +1 -1
  24. package/dist/cjs/nitro-plugin/queue.cjs.map +1 -0
  25. package/dist/cjs/plugin.cjs +50 -38
  26. package/dist/cjs/plugin.cjs.map +1 -1
  27. package/dist/cjs/plugin.d.cts +14 -2657
  28. package/dist/cjs/resolve-virtual-entries-plugin/plugin.cjs +66 -0
  29. package/dist/cjs/resolve-virtual-entries-plugin/plugin.cjs.map +1 -0
  30. package/dist/cjs/resolve-virtual-entries-plugin/plugin.d.cts +3 -0
  31. package/dist/cjs/schema.cjs +5 -5
  32. package/dist/cjs/schema.cjs.map +1 -1
  33. package/dist/cjs/schema.d.cts +32 -16
  34. package/dist/cjs/start-compiler-plugin.cjs +21 -19
  35. package/dist/cjs/start-compiler-plugin.cjs.map +1 -1
  36. package/dist/cjs/start-compiler-plugin.d.cts +1 -1
  37. package/dist/cjs/start-manifest-plugin/plugin.cjs +182 -0
  38. package/dist/cjs/start-manifest-plugin/plugin.cjs.map +1 -0
  39. package/dist/{esm/routesManifestPlugin.d.ts → cjs/start-manifest-plugin/plugin.d.cts} +1 -1
  40. package/dist/cjs/start-router-plugin/generator-plugins/routes-manifest-plugin.cjs +39 -0
  41. package/dist/cjs/start-router-plugin/generator-plugins/routes-manifest-plugin.cjs.map +1 -0
  42. package/dist/cjs/start-router-plugin/generator-plugins/routes-manifest-plugin.d.cts +6 -0
  43. package/dist/cjs/start-router-plugin/generator-plugins/server-routes-plugin.cjs +121 -0
  44. package/dist/cjs/start-router-plugin/generator-plugins/server-routes-plugin.cjs.map +1 -0
  45. package/dist/cjs/start-router-plugin/generator-plugins/server-routes-plugin.d.cts +2 -0
  46. package/dist/cjs/start-router-plugin/plugin.cjs +21 -0
  47. package/dist/cjs/start-router-plugin/plugin.cjs.map +1 -0
  48. package/dist/cjs/start-router-plugin/plugin.d.cts +3 -0
  49. package/dist/cjs/start-router-plugin/route-tree-client-plugin.cjs +72 -0
  50. package/dist/cjs/start-router-plugin/route-tree-client-plugin.cjs.map +1 -0
  51. package/dist/cjs/start-router-plugin/route-tree-client-plugin.d.cts +6 -0
  52. package/dist/cjs/start-router-plugin/virtual-route-tree-plugin.cjs +30 -0
  53. package/dist/cjs/start-router-plugin/virtual-route-tree-plugin.cjs.map +1 -0
  54. package/dist/cjs/start-router-plugin/virtual-route-tree-plugin.d.cts +4 -0
  55. package/dist/cjs/utils.cjs +11 -0
  56. package/dist/cjs/utils.cjs.map +1 -1
  57. package/dist/cjs/utils.d.cts +7 -0
  58. package/dist/esm/compilers.js +24 -10
  59. package/dist/esm/compilers.js.map +1 -1
  60. package/dist/esm/constants.d.ts +2 -0
  61. package/dist/esm/constants.js +10 -0
  62. package/dist/esm/constants.js.map +1 -1
  63. package/dist/esm/debug.d.ts +1 -0
  64. package/dist/esm/debug.js +5 -0
  65. package/dist/esm/debug.js.map +1 -0
  66. package/dist/esm/{extractHtmlScripts.js → dev-server-plugin/extract-html-scripts.js} +1 -1
  67. package/dist/esm/dev-server-plugin/extract-html-scripts.js.map +1 -0
  68. package/dist/esm/{nitro/dev-server-plugin.js → dev-server-plugin/plugin.js} +2 -2
  69. package/dist/esm/dev-server-plugin/plugin.js.map +1 -0
  70. package/dist/esm/load-env-plugin/plugin.d.ts +3 -0
  71. package/dist/esm/load-env-plugin/plugin.js +17 -0
  72. package/dist/esm/load-env-plugin/plugin.js.map +1 -0
  73. package/dist/esm/{build-sitemap.d.ts → nitro-plugin/build-sitemap.d.ts} +2 -2
  74. package/dist/esm/{build-sitemap.js → nitro-plugin/build-sitemap.js} +9 -7
  75. package/dist/esm/nitro-plugin/build-sitemap.js.map +1 -0
  76. package/dist/esm/nitro-plugin/plugin.js +181 -0
  77. package/dist/esm/nitro-plugin/plugin.js.map +1 -0
  78. package/dist/esm/{prerender.d.ts → nitro-plugin/prerender.d.ts} +1 -1
  79. package/dist/esm/{prerender.js → nitro-plugin/prerender.js} +15 -11
  80. package/dist/esm/nitro-plugin/prerender.js.map +1 -0
  81. package/dist/esm/nitro-plugin/queue.js.map +1 -0
  82. package/dist/esm/plugin.d.ts +14 -2657
  83. package/dist/esm/plugin.js +51 -39
  84. package/dist/esm/plugin.js.map +1 -1
  85. package/dist/esm/resolve-virtual-entries-plugin/plugin.d.ts +3 -0
  86. package/dist/esm/resolve-virtual-entries-plugin/plugin.js +49 -0
  87. package/dist/esm/resolve-virtual-entries-plugin/plugin.js.map +1 -0
  88. package/dist/esm/schema.d.ts +32 -16
  89. package/dist/esm/start-compiler-plugin.d.ts +1 -1
  90. package/dist/esm/start-compiler-plugin.js +21 -19
  91. package/dist/esm/start-compiler-plugin.js.map +1 -1
  92. package/dist/{cjs/routesManifestPlugin.d.cts → esm/start-manifest-plugin/plugin.d.ts} +1 -1
  93. package/dist/esm/start-manifest-plugin/plugin.js +182 -0
  94. package/dist/esm/start-manifest-plugin/plugin.js.map +1 -0
  95. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.d.ts +6 -0
  96. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +39 -0
  97. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -0
  98. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.d.ts +2 -0
  99. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.js +121 -0
  100. package/dist/esm/start-router-plugin/generator-plugins/server-routes-plugin.js.map +1 -0
  101. package/dist/esm/start-router-plugin/plugin.d.ts +3 -0
  102. package/dist/esm/start-router-plugin/plugin.js +21 -0
  103. package/dist/esm/start-router-plugin/plugin.js.map +1 -0
  104. package/dist/esm/start-router-plugin/route-tree-client-plugin.d.ts +6 -0
  105. package/dist/esm/start-router-plugin/route-tree-client-plugin.js +55 -0
  106. package/dist/esm/start-router-plugin/route-tree-client-plugin.js.map +1 -0
  107. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.d.ts +4 -0
  108. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.js +30 -0
  109. package/dist/esm/start-router-plugin/virtual-route-tree-plugin.js.map +1 -0
  110. package/dist/esm/utils.d.ts +7 -0
  111. package/dist/esm/utils.js +11 -0
  112. package/dist/esm/utils.js.map +1 -1
  113. package/package.json +8 -7
  114. package/src/compilers.ts +31 -131
  115. package/src/constants.ts +10 -0
  116. package/src/debug.ts +3 -0
  117. package/src/{nitro/dev-server-plugin.ts → dev-server-plugin/plugin.ts} +11 -1
  118. package/src/global.d.ts +8 -0
  119. package/src/load-env-plugin/plugin.ts +17 -0
  120. package/src/{build-sitemap.ts → nitro-plugin/build-sitemap.ts} +11 -8
  121. package/src/nitro-plugin/plugin.ts +244 -0
  122. package/src/{prerender.ts → nitro-plugin/prerender.ts} +17 -13
  123. package/src/plugin.ts +63 -41
  124. package/src/resolve-virtual-entries-plugin/plugin.ts +63 -0
  125. package/src/start-compiler-plugin.ts +25 -25
  126. package/src/start-manifest-plugin/plugin.ts +249 -0
  127. package/src/start-router-plugin/generator-plugins/routes-manifest-plugin.ts +43 -0
  128. package/src/start-router-plugin/generator-plugins/server-routes-plugin.ts +138 -0
  129. package/src/start-router-plugin/plugin.ts +35 -0
  130. package/src/start-router-plugin/route-tree-client-plugin.ts +76 -0
  131. package/src/start-router-plugin/virtual-route-tree-plugin.ts +30 -0
  132. package/src/utils.ts +11 -0
  133. package/dist/cjs/build-sitemap.cjs.map +0 -1
  134. package/dist/cjs/extractHtmlScripts.cjs.map +0 -1
  135. package/dist/cjs/nitro/build-nitro.cjs +0 -18
  136. package/dist/cjs/nitro/build-nitro.cjs.map +0 -1
  137. package/dist/cjs/nitro/build-nitro.d.cts +0 -2
  138. package/dist/cjs/nitro/dev-server-plugin.cjs.map +0 -1
  139. package/dist/cjs/nitro/nitro-plugin.cjs.map +0 -1
  140. package/dist/cjs/prerender.cjs.map +0 -1
  141. package/dist/cjs/queue.cjs.map +0 -1
  142. package/dist/cjs/routesManifestPlugin.cjs +0 -168
  143. package/dist/cjs/routesManifestPlugin.cjs.map +0 -1
  144. package/dist/cjs/start-server-routes-plugin/config.d.cts +0 -49
  145. package/dist/cjs/start-server-routes-plugin/plugin.cjs +0 -613
  146. package/dist/cjs/start-server-routes-plugin/plugin.cjs.map +0 -1
  147. package/dist/cjs/start-server-routes-plugin/plugin.d.cts +0 -3
  148. package/dist/cjs/start-server-routes-plugin/template.cjs +0 -111
  149. package/dist/cjs/start-server-routes-plugin/template.cjs.map +0 -1
  150. package/dist/cjs/start-server-routes-plugin/template.d.cts +0 -34
  151. package/dist/esm/build-sitemap.js.map +0 -1
  152. package/dist/esm/extractHtmlScripts.js.map +0 -1
  153. package/dist/esm/nitro/build-nitro.d.ts +0 -2
  154. package/dist/esm/nitro/build-nitro.js +0 -18
  155. package/dist/esm/nitro/build-nitro.js.map +0 -1
  156. package/dist/esm/nitro/dev-server-plugin.js.map +0 -1
  157. package/dist/esm/nitro/nitro-plugin.js +0 -167
  158. package/dist/esm/nitro/nitro-plugin.js.map +0 -1
  159. package/dist/esm/prerender.js.map +0 -1
  160. package/dist/esm/queue.js.map +0 -1
  161. package/dist/esm/routesManifestPlugin.js +0 -168
  162. package/dist/esm/routesManifestPlugin.js.map +0 -1
  163. package/dist/esm/start-server-routes-plugin/config.d.ts +0 -49
  164. package/dist/esm/start-server-routes-plugin/plugin.d.ts +0 -3
  165. package/dist/esm/start-server-routes-plugin/plugin.js +0 -613
  166. package/dist/esm/start-server-routes-plugin/plugin.js.map +0 -1
  167. package/dist/esm/start-server-routes-plugin/template.d.ts +0 -34
  168. package/dist/esm/start-server-routes-plugin/template.js +0 -111
  169. package/dist/esm/start-server-routes-plugin/template.js.map +0 -1
  170. package/src/nitro/build-nitro.ts +0 -27
  171. package/src/nitro/nitro-plugin.ts +0 -195
  172. package/src/routesManifestPlugin.ts +0 -220
  173. package/src/start-server-routes-plugin/config.ts +0 -8
  174. package/src/start-server-routes-plugin/plugin.ts +0 -899
  175. package/src/start-server-routes-plugin/template.ts +0 -164
  176. /package/dist/cjs/{extractHtmlScripts.d.cts → dev-server-plugin/extract-html-scripts.d.cts} +0 -0
  177. /package/dist/cjs/{nitro/dev-server-plugin.d.cts → dev-server-plugin/plugin.d.cts} +0 -0
  178. /package/dist/cjs/{nitro/nitro-plugin.d.cts → nitro-plugin/plugin.d.cts} +0 -0
  179. /package/dist/cjs/{queue.cjs → nitro-plugin/queue.cjs} +0 -0
  180. /package/dist/cjs/{queue.d.cts → nitro-plugin/queue.d.cts} +0 -0
  181. /package/dist/esm/{extractHtmlScripts.d.ts → dev-server-plugin/extract-html-scripts.d.ts} +0 -0
  182. /package/dist/esm/{nitro/dev-server-plugin.d.ts → dev-server-plugin/plugin.d.ts} +0 -0
  183. /package/dist/esm/{nitro/nitro-plugin.d.ts → nitro-plugin/plugin.d.ts} +0 -0
  184. /package/dist/esm/{queue.d.ts → nitro-plugin/queue.d.ts} +0 -0
  185. /package/dist/esm/{queue.js → nitro-plugin/queue.js} +0 -0
  186. /package/src/{extractHtmlScripts.ts → dev-server-plugin/extract-html-scripts.ts} +0 -0
  187. /package/src/{queue.ts → nitro-plugin/queue.ts} +0 -0
@@ -1,899 +0,0 @@
1
- import path, { isAbsolute, join, normalize } from 'node:path'
2
- import fs from 'node:fs'
3
- import fsp from 'node:fs/promises'
4
- import {
5
- format,
6
- logging,
7
- multiSortBy,
8
- physicalGetRouteNodes,
9
- removeExt,
10
- removeUnderscores,
11
- replaceBackslash,
12
- resetRegex,
13
- rootPathId,
14
- routePathToVariable,
15
- trimPathLeft,
16
- virtualGetRouteNodes,
17
- writeIfDifferent,
18
- } from '@tanstack/router-generator'
19
- import { rootRouteId } from '@tanstack/router-core'
20
- import { fillTemplate, getTargetTemplate } from './template'
21
- import type { GetRouteNodesResult, RouteNode } from '@tanstack/router-generator'
22
- import type { Config } from './config'
23
- import type { Plugin } from 'vite'
24
-
25
- let lock = false
26
- const checkLock = () => lock
27
- const setLock = (bool: boolean) => {
28
- lock = bool
29
- }
30
-
31
- export function TanStackStartServerRoutesVite(config: Config): Plugin {
32
- let ROOT: string = process.cwd()
33
- const moduleId = 'tanstack-start-server-routes-manifest:v'
34
-
35
- const getRoutesDirectoryPath = () => {
36
- return isAbsolute(config.routesDirectory)
37
- ? config.routesDirectory
38
- : join(ROOT, config.routesDirectory)
39
- }
40
-
41
- const generate = async () => {
42
- if (checkLock()) {
43
- return
44
- }
45
-
46
- setLock(true)
47
-
48
- try {
49
- await generator(config, ROOT)
50
- } catch (err) {
51
- console.error(err)
52
- console.info()
53
- } finally {
54
- setLock(false)
55
- }
56
- }
57
-
58
- const handleFile = async (file: string) => {
59
- const filePath = normalize(file)
60
-
61
- const routesDirectoryPath = getRoutesDirectoryPath()
62
- if (filePath.startsWith(routesDirectoryPath)) {
63
- await generate()
64
- }
65
- }
66
-
67
- return {
68
- name: 'tanstack-start-server-routes-plugin',
69
- configureServer(server) {
70
- server.watcher.on('all', (event, path) => {
71
- handleFile(path)
72
- })
73
- },
74
- configResolved(config) {
75
- ROOT = config.root
76
- },
77
- async buildStart() {
78
- await generate()
79
- // if (this.environment.name === 'server') {
80
- // }
81
- },
82
- sharedDuringBuild: true,
83
- resolveId(id) {
84
- if (id === moduleId) {
85
- const generatedRouteTreePath = getGeneratedRouteTreePath(ROOT)
86
- return generatedRouteTreePath
87
- }
88
- return null
89
- },
90
- }
91
- }
92
-
93
- // Maybe import this from `@tanstack/router-core` in the future???
94
- let latestTask = 0
95
- const routeGroupPatternRegex = /\(.+\)/g
96
- const possiblyNestedRouteGroupPatternRegex = /\([^/]+\)\/?/g
97
-
98
- let isFirst = false
99
- let skipMessage = false
100
-
101
- function getGeneratedRouteTreePath(root: string) {
102
- return path.resolve(root, '.tanstack-start/server-routes/routeTree.gen.ts')
103
- }
104
-
105
- async function generator(config: Config, root: string) {
106
- const generatedServerRouteTreePath = getGeneratedRouteTreePath(root)
107
- const ROUTE_TEMPLATE = getTargetTemplate(config.target)
108
- const logger = logging({ disabled: config.disableLogging })
109
-
110
- if (!isFirst) {
111
- // logger.log('♻️ Generating server routes...')
112
- isFirst = true
113
- } else if (skipMessage) {
114
- skipMessage = false
115
- } else {
116
- // logger.log('♻️ Regenerating server routes...')
117
- }
118
-
119
- const taskId = latestTask + 1
120
- latestTask = taskId
121
-
122
- const checkLatest = () => {
123
- if (latestTask !== taskId) {
124
- skipMessage = true
125
- return false
126
- }
127
-
128
- return true
129
- }
130
-
131
- const start = Date.now()
132
-
133
- let getRouteNodesResult: GetRouteNodesResult
134
-
135
- if (config.virtualRouteConfig) {
136
- getRouteNodesResult = await virtualGetRouteNodes(config, root)
137
- } else {
138
- getRouteNodesResult = await physicalGetRouteNodes(config, root)
139
- }
140
-
141
- const { rootRouteNode, routeNodes: beforeRouteNodes } = getRouteNodesResult
142
- if (rootRouteNode === undefined) {
143
- let errorMessage = `rootRouteNode must not be undefined. Make sure you've added your root route into the route-tree.`
144
- if (!config.virtualRouteConfig) {
145
- errorMessage += `\nMake sure that you add a "${rootPathId}.tsx" file to your routes directory.\nAdd the file in: "${config.routesDirectory}/${rootPathId}.tsx"`
146
- }
147
- throw new Error(errorMessage)
148
- }
149
-
150
- const preRouteNodes = multiSortBy(beforeRouteNodes, [
151
- (d) => (d.routePath === '/' ? -1 : 1),
152
- (d) => d.routePath?.split('/').length,
153
- (d) =>
154
- d.filePath.match(new RegExp(`[./]${config.indexToken}[.]`)) ? 1 : -1,
155
- (d) =>
156
- d.filePath.match(
157
- /[./](component|errorComponent|pendingComponent|loader|lazy)[.]/,
158
- )
159
- ? 1
160
- : -1,
161
- (d) =>
162
- d.filePath.match(new RegExp(`[./]${config.routeToken}[.]`)) ? -1 : 1,
163
- (d) => (d.routePath?.endsWith('/') ? -1 : 1),
164
- (d) => d.routePath,
165
- ]).filter((d) => ![`/${rootPathId}`].includes(d.routePath || ''))
166
-
167
- const routeTree: Array<RouteNode> = []
168
-
169
- // Loop over the flat list of routeNodes and
170
- // build up a tree based on the routeNodes' routePath
171
- const routeNodes: Array<RouteNode> = []
172
-
173
- // the handleRootNode function is not being collapsed into the handleNode function
174
- // because it requires only a subset of the logic that the handleNode function requires
175
- // and it's easier to read and maintain this way
176
- const handleRootNode = async (node?: RouteNode) => {
177
- if (!node) {
178
- // currently this is not being handled, but it could be in the future
179
- // for example to handle a virtual root route
180
- return
181
- }
182
-
183
- // from here on, we are only handling the root node that's present in the file system
184
- const routeCode = fs.readFileSync(node.fullPath, 'utf-8')
185
-
186
- if (!routeCode) {
187
- const _rootTemplate = ROUTE_TEMPLATE.rootRoute
188
- const replaced = await fillTemplate(config, _rootTemplate.template(), {
189
- tsrImports: _rootTemplate.imports.tsrImports(),
190
- tsrPath: rootPathId,
191
- tsrExportStart: _rootTemplate.imports.tsrExportStart(),
192
- tsrExportEnd: _rootTemplate.imports.tsrExportEnd(),
193
- })
194
-
195
- await writeIfDifferent(
196
- node.fullPath,
197
- '', // Empty string because the file doesn't exist yet
198
- replaced,
199
- {
200
- beforeWrite: () => {
201
- // logger.log(`🟡 Creating ${node.fullPath}`)
202
- },
203
- },
204
- )
205
- }
206
- }
207
-
208
- await handleRootNode(rootRouteNode)
209
-
210
- const handleNode = async (node: RouteNode) => {
211
- // Do not remove this as we need to set the lastIndex to 0 as it
212
- // is necessary to reset the regex's index when using the global flag
213
- // otherwise it might not match the next time it's used
214
- resetRegex(routeGroupPatternRegex)
215
-
216
- let parentRoute = hasParentRoute(routeNodes, node, node.routePath)
217
-
218
- // if the parent route is a virtual parent route, we need to find the real parent route
219
- if (parentRoute?.isVirtualParentRoute && parentRoute.children?.length) {
220
- // only if this sub-parent route returns a valid parent route, we use it, if not leave it as it
221
- const possibleParentRoute = hasParentRoute(
222
- parentRoute.children,
223
- node,
224
- node.routePath,
225
- )
226
- if (possibleParentRoute) {
227
- parentRoute = possibleParentRoute
228
- }
229
- }
230
-
231
- if (parentRoute) node.parent = parentRoute
232
-
233
- node.path = determineNodePath(node)
234
-
235
- const trimmedPath = trimPathLeft(node.path ?? '')
236
-
237
- const split = trimmedPath.split('/')
238
- const lastRouteSegment = split[split.length - 1] ?? trimmedPath
239
-
240
- node.isNonPath =
241
- lastRouteSegment.startsWith('_') ||
242
- routeGroupPatternRegex.test(lastRouteSegment)
243
-
244
- node.cleanedPath = removeGroups(
245
- removeUnderscores(removeLayoutSegments(node.path)) ?? '',
246
- )
247
-
248
- const routeCode = node.fullPath
249
- ? fs.readFileSync(node.fullPath, 'utf-8')
250
- : ''
251
-
252
- // Ensure the boilerplate for the route exists, which can be skipped for virtual parent routes and virtual routes
253
- if (!node.isVirtualParentRoute && !node.isVirtual) {
254
- // const escapedRoutePath = node.routePath?.replaceAll('$', '$$') ?? ''
255
- // let replaced = routeCode
256
- // await writeIfDifferent(node.fullPath, routeCode, replaced, {
257
- // beforeWrite: () => {
258
- // // logger.log(`🟡 Updating ${node.fullPath}`)
259
- // },
260
- // })
261
- }
262
-
263
- const cleanedPathIsEmpty = (node.cleanedPath || '').length === 0
264
- const nonPathRoute =
265
- node._fsRouteType === 'pathless_layout' && node.isNonPath
266
-
267
- node.isVirtualParentRequired =
268
- node._fsRouteType === 'pathless_layout' || nonPathRoute
269
- ? !cleanedPathIsEmpty
270
- : false
271
-
272
- if (!node.isVirtual && node.isVirtualParentRequired) {
273
- const parentRoutePath = removeLastSegmentFromPath(node.routePath) || '/'
274
- const parentVariableName = routePathToVariable(parentRoutePath)
275
-
276
- const anchorRoute = routeNodes.find(
277
- (d) => d.routePath === parentRoutePath,
278
- )
279
-
280
- if (!anchorRoute) {
281
- const parentNode: RouteNode = {
282
- ...node,
283
- path: removeLastSegmentFromPath(node.path) || '/',
284
- filePath: removeLastSegmentFromPath(node.filePath) || '/',
285
- fullPath: removeLastSegmentFromPath(node.fullPath) || '/',
286
- routePath: parentRoutePath,
287
- variableName: parentVariableName,
288
- isVirtual: true,
289
- _fsRouteType: 'layout', // layout since this route will wrap other routes
290
- isVirtualParentRoute: true,
291
- isVirtualParentRequired: false,
292
- }
293
-
294
- parentNode.children = parentNode.children ?? []
295
- parentNode.children.push(node)
296
-
297
- node.parent = parentNode
298
-
299
- if (node._fsRouteType === 'pathless_layout') {
300
- // since `node.path` is used as the `id` on the route definition, we need to update it
301
- node.path = determineNodePath(node)
302
- }
303
-
304
- await handleNode(parentNode)
305
- } else {
306
- anchorRoute.children = anchorRoute.children ?? []
307
- anchorRoute.children.push(node)
308
-
309
- node.parent = anchorRoute
310
- }
311
- }
312
-
313
- if (
314
- !routeCode
315
- .split('\n')
316
- .some((line) => line.trim().startsWith('export const ServerRoute'))
317
- ) {
318
- return
319
- }
320
-
321
- if (node.parent) {
322
- if (!node.isVirtualParentRequired) {
323
- node.parent.children = node.parent.children ?? []
324
- node.parent.children.push(node)
325
- }
326
- } else {
327
- routeTree.push(node)
328
- }
329
-
330
- routeNodes.push(node)
331
- }
332
-
333
- for (const node of preRouteNodes) {
334
- await handleNode(node)
335
- }
336
-
337
- // This is run against the `routeNodes` array since it
338
- // has the accumulated (intended) Server Route nodes
339
- // Since TSR allows multiple way of defining a route,
340
- // we need to ensure that a user hasn't defined the
341
- // same route in multiple ways (i.e. `flat`, `nested`, `virtual`)
342
- checkRouteFullPathUniqueness(routeNodes, config)
343
-
344
- function buildRouteTreeConfig(nodes: Array<RouteNode>, depth = 1): string {
345
- const children = nodes.map((node) => {
346
- if (node._fsRouteType === '__root') {
347
- return
348
- }
349
-
350
- if (node._fsRouteType === 'pathless_layout' && !node.children?.length) {
351
- return
352
- }
353
-
354
- const route = `${node.variableName}Route`
355
-
356
- if (node.children?.length) {
357
- const childConfigs = buildRouteTreeConfig(node.children, depth + 1)
358
-
359
- const childrenDeclaration = `interface ${route}Children {
360
- ${node.children.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(',')}
361
- }`
362
-
363
- const children = `const ${route}Children: ${route}Children = {
364
- ${node.children.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(',')}
365
- }`
366
-
367
- const routeWithChildren = `const ${route}WithChildren = ${route}._addFileChildren(${route}Children)`
368
-
369
- return [
370
- childConfigs,
371
- childrenDeclaration,
372
- children,
373
- routeWithChildren,
374
- ].join('\n\n')
375
- }
376
-
377
- return undefined
378
- })
379
-
380
- return children.filter(Boolean).join('\n\n')
381
- }
382
-
383
- const routeConfigChildrenText = buildRouteTreeConfig(routeTree)
384
-
385
- const sortedRouteNodes = multiSortBy(routeNodes, [
386
- (d) => (d.routePath?.includes(`/${rootPathId}`) ? -1 : 1),
387
- (d) => d.routePath?.split('/').length,
388
- (d) => (d.routePath?.endsWith(config.indexToken) ? -1 : 1),
389
- (d) => d,
390
- ])
391
-
392
- const imports = Object.entries({
393
- createFileRoute: sortedRouteNodes.some((d) => d.isVirtual),
394
- })
395
- .filter((d) => d[1])
396
- .map((d) => d[0])
397
-
398
- const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual)
399
-
400
- function getImportPath(node: RouteNode) {
401
- return replaceBackslash(
402
- removeExt(
403
- path.relative(
404
- path.dirname(generatedServerRouteTreePath),
405
- path.resolve(config.routesDirectory, node.filePath),
406
- ),
407
- ),
408
- )
409
- }
410
-
411
- const rootRouteExists = fs.existsSync(rootRouteNode.fullPath)
412
- const rootRouteCode = rootRouteExists
413
- ? fs.readFileSync(rootRouteNode.fullPath, 'utf-8')
414
- : ''
415
- const hasServerRootRoute =
416
- rootRouteExists && rootRouteCode.includes('export const ServerRoute')
417
-
418
- const routeImports = [
419
- ...config.routeTreeFileHeader,
420
- `// This file was automatically generated by TanStack Router.
421
- // You should NOT make any changes in this file as it will be overwritten.
422
- // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.`,
423
- imports.length
424
- ? `import { ${imports.join(', ')} } from '${ROUTE_TEMPLATE.fullPkg}'\n`
425
- : '',
426
- '// Import Routes',
427
- [
428
- `import type { FileRoutesByPath, CreateServerFileRoute } from '${ROUTE_TEMPLATE.fullPkg}'`,
429
- `import { createServerRoute, createServerFileRoute } from '${ROUTE_TEMPLATE.fullPkg}'`,
430
- hasServerRootRoute
431
- ? `import { ServerRoute as rootRouteImport } from './${getImportPath(rootRouteNode)}'`
432
- : '',
433
- ...sortedRouteNodes
434
- .filter((d) => !d.isVirtual)
435
- .map((node) => {
436
- return `import { ServerRoute as ${
437
- node.variableName
438
- }RouteImport } from './${getImportPath(node)}'`
439
- }),
440
- ].join('\n'),
441
- virtualRouteNodes.length ? '// Create Virtual Routes' : '',
442
- virtualRouteNodes
443
- .map((node) => {
444
- return `const ${
445
- node.variableName
446
- }RouteImport = createFileRoute('${node.routePath}')()`
447
- })
448
- .join('\n'),
449
- '// Create/Update Routes',
450
- !hasServerRootRoute
451
- ? `
452
- const rootRoute = createServerRoute()
453
- `
454
- : '',
455
- sortedRouteNodes
456
- .map((node) => {
457
- return [
458
- [
459
- `const ${node.variableName}Route = ${node.variableName}RouteImport.update({
460
- ${[
461
- `id: '${node.path}'`,
462
- !node.isNonPath ? `path: '${node.cleanedPath}'` : undefined,
463
- `getParentRoute: () => ${node.parent?.variableName ?? 'root'}Route`,
464
- ]
465
- .filter(Boolean)
466
- .join(',')}
467
- } as any)`,
468
- ].join(''),
469
- ].join('\n\n')
470
- })
471
- .join('\n\n'),
472
- '',
473
-
474
- '// Populate the FileRoutesByPath interface',
475
- `declare module '${ROUTE_TEMPLATE.fullPkg}' {
476
- interface FileRoutesByPath {
477
- ${routeNodes
478
- .map((routeNode) => {
479
- const filePathId = routeNode.routePath
480
-
481
- return `'${filePathId}': {
482
- id: '${filePathId}'
483
- path: '${inferPath(routeNode)}'
484
- fullPath: '${inferFullPath(routeNode)}'
485
- preLoaderRoute: typeof ${routeNode.variableName}RouteImport
486
- parentRoute: typeof ${
487
- routeNode.isVirtualParentRequired
488
- ? `${routeNode.parent?.variableName}Route`
489
- : routeNode.parent?.variableName
490
- ? `${routeNode.parent.variableName}RouteImport`
491
- : 'rootRoute'
492
- }
493
- }`
494
- })
495
- .join('\n')}
496
- }
497
- }`,
498
- `// Add type-safety to the createFileRoute function across the route tree`,
499
- routeNodes
500
- .map((routeNode) => {
501
- return `declare module './${getImportPath(routeNode)}' {
502
- const createServerFileRoute: CreateServerFileRoute<
503
- FileRoutesByPath['${routeNode.routePath}']['parentRoute'],
504
- FileRoutesByPath['${routeNode.routePath}']['id'],
505
- FileRoutesByPath['${routeNode.routePath}']['path'],
506
- FileRoutesByPath['${routeNode.routePath}']['fullPath'],
507
- ${routeNode.children?.length ? `${routeNode.variableName}RouteChildren` : 'unknown'}
508
- >
509
- }`
510
- })
511
- .join('\n'),
512
- '// Create and export the route tree',
513
- routeConfigChildrenText,
514
- `export interface FileRoutesByFullPath {
515
- ${[...createRouteNodesByFullPath(routeNodes).entries()].map(
516
- ([fullPath, routeNode]) => {
517
- return `'${fullPath}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
518
- },
519
- )}
520
- }`,
521
- `export interface FileRoutesByTo {
522
- ${[...createRouteNodesByTo(routeNodes).entries()].map(([to, routeNode]) => {
523
- return `'${to}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
524
- })}
525
- }`,
526
- `export interface FileRoutesById {
527
- '${rootRouteId}': typeof rootRoute,
528
- ${[...createRouteNodesById(routeNodes).entries()].map(([id, routeNode]) => {
529
- return `'${id}': typeof ${getResolvedRouteNodeVariableName(routeNode)}`
530
- })}
531
- }`,
532
- `export interface FileRouteTypes {
533
- fileRoutesByFullPath: FileRoutesByFullPath
534
- fullPaths: ${routeNodes.length > 0 ? [...createRouteNodesByFullPath(routeNodes).keys()].map((fullPath) => `'${fullPath}'`).join('|') : 'never'}
535
- fileRoutesByTo: FileRoutesByTo
536
- to: ${routeNodes.length > 0 ? [...createRouteNodesByTo(routeNodes).keys()].map((to) => `'${to}'`).join('|') : 'never'}
537
- id: ${[`'${rootRouteId}'`, ...[...createRouteNodesById(routeNodes).keys()].map((id) => `'${id}'`)].join('|')}
538
- fileRoutesById: FileRoutesById
539
- }`,
540
- `export interface RootRouteChildren {
541
- ${routeTree.map((child) => `${child.variableName}Route: typeof ${getResolvedRouteNodeVariableName(child)}`).join(',')}
542
- }`,
543
- `const rootRouteChildren: RootRouteChildren = {
544
- ${routeTree.map((child) => `${child.variableName}Route: ${getResolvedRouteNodeVariableName(child)}`).join(',')}
545
- }`,
546
- `export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileTypes<FileRouteTypes>()`,
547
- ]
548
- .filter(Boolean)
549
- .join('\n\n')
550
-
551
- const createRouteManifest = () => {
552
- const routesManifest = {
553
- [rootRouteId]: {
554
- filePath: rootRouteNode.filePath,
555
- children: routeTree.map((d) => d.routePath),
556
- },
557
- ...Object.fromEntries(
558
- routeNodes.map((d) => {
559
- const filePathId = d.routePath
560
-
561
- return [
562
- filePathId,
563
- {
564
- filePath: d.filePath,
565
- parent: d.parent?.routePath ? d.parent.routePath : undefined,
566
- children: d.children?.map((childRoute) => childRoute.routePath),
567
- },
568
- ]
569
- }),
570
- ),
571
- }
572
-
573
- return JSON.stringify(
574
- {
575
- routes: routesManifest,
576
- },
577
- null,
578
- 2,
579
- )
580
- }
581
-
582
- const includeManifest = ['react', 'solid']
583
- const routeConfigFileContent = !includeManifest.includes(config.target)
584
- ? routeImports
585
- : [
586
- routeImports,
587
- '\n',
588
- '/* ROUTE_MANIFEST_START',
589
- createRouteManifest(),
590
- 'ROUTE_MANIFEST_END */',
591
- ].join('\n')
592
-
593
- if (!checkLatest()) return
594
-
595
- const existingRouteTreeContent = await fsp
596
- .readFile(path.resolve(generatedServerRouteTreePath), 'utf-8')
597
- .catch((err) => {
598
- if (err.code === 'ENOENT') {
599
- return ''
600
- }
601
-
602
- throw err
603
- })
604
-
605
- if (!checkLatest()) return
606
-
607
- // Ensure the directory exists
608
- await fsp.mkdir(path.dirname(path.resolve(generatedServerRouteTreePath)), {
609
- recursive: true,
610
- })
611
-
612
- if (!checkLatest()) return
613
-
614
- // Write the route tree file, if it has changed
615
- const routeTreeWriteResult = await writeIfDifferent(
616
- path.resolve(generatedServerRouteTreePath),
617
- await format(existingRouteTreeContent, config),
618
- await format(routeConfigFileContent, config),
619
- {
620
- beforeWrite: () => {
621
- // logger.log(`🟡 Updating ${generatedRouteTreePath}`)
622
- },
623
- },
624
- )
625
-
626
- // Write declaration file
627
- const startDeclarationFilePath = path.join(
628
- path.resolve(root, config.srcDirectory),
629
- 'tanstack-start.d.ts',
630
- )
631
- const serverRoutesRelativePath = removeExt(
632
- path.relative(
633
- path.dirname(startDeclarationFilePath),
634
- generatedServerRouteTreePath,
635
- ),
636
- )
637
- const startDeclarationFileContent = buildStartDeclarationFile({
638
- serverRoutesRelativePath,
639
- })
640
- if (!fs.existsSync(startDeclarationFilePath)) {
641
- await writeIfDifferent(
642
- startDeclarationFilePath,
643
- '',
644
- startDeclarationFileContent,
645
- {
646
- beforeWrite: () => {
647
- logger.log(`🟡 Creating tanstack-start.d.ts`)
648
- },
649
- },
650
- )
651
- } else {
652
- const existingDeclarationFileContent = await fsp
653
- .readFile(startDeclarationFilePath, 'utf-8')
654
- .catch((err) => {
655
- if (err.code === 'ENOENT') {
656
- return ''
657
- }
658
- throw err
659
- })
660
- await writeIfDifferent(
661
- startDeclarationFilePath,
662
- existingDeclarationFileContent,
663
- startDeclarationFileContent,
664
- {
665
- beforeWrite: () => {
666
- logger.log(`🟡 Updating tanstack-start.d.ts`)
667
- },
668
- },
669
- )
670
- }
671
-
672
- if (routeTreeWriteResult && !checkLatest()) {
673
- return
674
- }
675
-
676
- // logger.log(
677
- // `✅ Processed ${routeNodes.length === 1 ? 'server route' : 'server routes'} in ${
678
- // Date.now() - start
679
- // }ms`,
680
- // )
681
- }
682
-
683
- function buildStartDeclarationFile({
684
- serverRoutesRelativePath,
685
- }: {
686
- serverRoutesRelativePath: string
687
- }) {
688
- const serverRoutesPath = replaceBackslash(serverRoutesRelativePath)
689
- return (
690
- [
691
- '/// <reference types="vite/client" />',
692
- `import '${serverRoutesPath}'`,
693
- ].join('\n') + '\n'
694
- )
695
- }
696
-
697
- function removeGroups(s: string) {
698
- return s.replace(possiblyNestedRouteGroupPatternRegex, '')
699
- }
700
-
701
- /**
702
- * The `node.path` is used as the `id` in the route definition.
703
- * This function checks if the given node has a parent and if so, it determines the correct path for the given node.
704
- * @param node - The node to determine the path for.
705
- * @returns The correct path for the given node.
706
- */
707
- function determineNodePath(node: RouteNode) {
708
- return (node.path = node.parent
709
- ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'
710
- : node.routePath)
711
- }
712
-
713
- /**
714
- * Removes the last segment from a given path. Segments are considered to be separated by a '/'.
715
- *
716
- * @param {string} routePath - The path from which to remove the last segment. Defaults to '/'.
717
- * @returns {string} The path with the last segment removed.
718
- * @example
719
- * removeLastSegmentFromPath('/workspace/_auth/foo') // '/workspace/_auth'
720
- */
721
- function removeLastSegmentFromPath(routePath: string = '/'): string {
722
- const segments = routePath.split('/')
723
- segments.pop() // Remove the last segment
724
- return segments.join('/')
725
- }
726
-
727
- /**
728
- * Removes all segments from a given path that start with an underscore ('_').
729
- *
730
- * @param {string} routePath - The path from which to remove segments. Defaults to '/'.
731
- * @returns {string} The path with all underscore-prefixed segments removed.
732
- * @example
733
- * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'
734
- */
735
- function removeLayoutSegments(routePath: string = '/'): string {
736
- const segments = routePath.split('/')
737
- const newSegments = segments.filter((segment) => !segment.startsWith('_'))
738
- return newSegments.join('/')
739
- }
740
-
741
- function hasParentRoute(
742
- routes: Array<RouteNode>,
743
- node: RouteNode,
744
- routePathToCheck: string | undefined,
745
- ): RouteNode | null {
746
- if (!routePathToCheck || routePathToCheck === '/') {
747
- return null
748
- }
749
-
750
- const sortedNodes = multiSortBy(routes, [
751
- (d) => d.routePath!.length * -1,
752
- (d) => d.variableName,
753
- ]).filter((d) => d.routePath !== `/${rootPathId}`)
754
-
755
- for (const route of sortedNodes) {
756
- if (route.routePath === '/') continue
757
-
758
- if (
759
- routePathToCheck.startsWith(`${route.routePath}/`) &&
760
- route.routePath !== routePathToCheck
761
- ) {
762
- return route
763
- }
764
- }
765
-
766
- const segments = routePathToCheck.split('/')
767
- segments.pop() // Remove the last segment
768
- const parentRoutePath = segments.join('/')
769
-
770
- return hasParentRoute(routes, node, parentRoutePath)
771
- }
772
-
773
- /**
774
- * Gets the final variable name for a route
775
- */
776
- const getResolvedRouteNodeVariableName = (routeNode: RouteNode): string => {
777
- return routeNode.children?.length
778
- ? `${routeNode.variableName}RouteWithChildren`
779
- : `${routeNode.variableName}Route`
780
- }
781
-
782
- /**
783
- * Creates a map from fullPath to routeNode
784
- */
785
- const createRouteNodesByFullPath = (
786
- routeNodes: Array<RouteNode>,
787
- ): Map<string, RouteNode> => {
788
- return new Map(
789
- routeNodes.map((routeNode) => [inferFullPath(routeNode), routeNode]),
790
- )
791
- }
792
-
793
- /**
794
- * Create a map from 'to' to a routeNode
795
- */
796
- const createRouteNodesByTo = (
797
- routeNodes: Array<RouteNode>,
798
- ): Map<string, RouteNode> => {
799
- return new Map(
800
- dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [
801
- inferTo(routeNode),
802
- routeNode,
803
- ]),
804
- )
805
- }
806
-
807
- /**
808
- * Create a map from 'id' to a routeNode
809
- */
810
- const createRouteNodesById = (
811
- routeNodes: Array<RouteNode>,
812
- ): Map<string, RouteNode> => {
813
- return new Map(
814
- routeNodes.map((routeNode) => {
815
- const id = routeNode.routePath ?? ''
816
- return [id, routeNode]
817
- }),
818
- )
819
- }
820
-
821
- /**
822
- * Infers the full path for use by TS
823
- */
824
- const inferFullPath = (routeNode: RouteNode): string => {
825
- const fullPath = removeGroups(
826
- removeUnderscores(removeLayoutSegments(routeNode.routePath)) ?? '',
827
- )
828
-
829
- return routeNode.cleanedPath === '/' ? fullPath : fullPath.replace(/\/$/, '')
830
- }
831
-
832
- /**
833
- * Infers the path for use by TS
834
- */
835
- const inferPath = (routeNode: RouteNode): string => {
836
- return routeNode.cleanedPath === '/'
837
- ? routeNode.cleanedPath
838
- : (routeNode.cleanedPath?.replace(/\/$/, '') ?? '')
839
- }
840
-
841
- /**
842
- * Infers to path
843
- */
844
- const inferTo = (routeNode: RouteNode): string => {
845
- const fullPath = inferFullPath(routeNode)
846
-
847
- if (fullPath === '/') return fullPath
848
-
849
- return fullPath.replace(/\/$/, '')
850
- }
851
-
852
- /**
853
- * Dedupes branches and index routes
854
- */
855
- const dedupeBranchesAndIndexRoutes = (
856
- routes: Array<RouteNode>,
857
- ): Array<RouteNode> => {
858
- return routes.filter((route) => {
859
- if (route.children?.find((child) => child.cleanedPath === '/')) return false
860
- return true
861
- })
862
- }
863
-
864
- function checkUnique<TElement>(routes: Array<TElement>, key: keyof TElement) {
865
- // Check no two routes have the same `key`
866
- // if they do, throw an error with the conflicting filePaths
867
- const keys = routes.map((d) => d[key])
868
- const uniqueKeys = new Set(keys)
869
- if (keys.length !== uniqueKeys.size) {
870
- const duplicateKeys = keys.filter((d, i) => keys.indexOf(d) !== i)
871
- const conflictingFiles = routes.filter((d) =>
872
- duplicateKeys.includes(d[key]),
873
- )
874
- return conflictingFiles
875
- }
876
- return undefined
877
- }
878
-
879
- function checkRouteFullPathUniqueness(
880
- _routes: Array<RouteNode>,
881
- config: Config,
882
- ) {
883
- const routes = _routes.map((d) => {
884
- const inferredFullPath = inferFullPath(d)
885
- return { ...d, inferredFullPath }
886
- })
887
-
888
- const conflictingFiles = checkUnique(routes, 'inferredFullPath')
889
-
890
- if (conflictingFiles !== undefined) {
891
- const errorMessage = `Conflicting configuration paths were found for the following route${conflictingFiles.length > 1 ? 's' : ''}: ${conflictingFiles
892
- .map((p) => `"${p.inferredFullPath}"`)
893
- .join(', ')}.
894
- Please ensure each Server Route has a unique full path.
895
- Conflicting files: \n ${conflictingFiles.map((d) => path.resolve(config.routesDirectory, d.filePath)).join('\n ')}\n`
896
- console.error(errorMessage)
897
- process.exit(1)
898
- }
899
- }