@octanejs/tanstack-router 0.1.10 → 0.1.12

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 (61) hide show
  1. package/README.md +24 -11
  2. package/package.json +26 -11
  3. package/src/Asset.tsrx +122 -0
  4. package/src/Asset.tsrx.d.ts +9 -0
  5. package/src/Await.tsrx +2 -1
  6. package/src/Await.tsrx.d.ts +8 -6
  7. package/src/Body.ts +31 -0
  8. package/src/CatchBoundary.tsrx +1 -3
  9. package/src/ClientOnly.tsrx +4 -2
  10. package/src/Head.ts +22 -0
  11. package/src/HeadContent.tsrx +41 -0
  12. package/src/HeadContent.tsrx.d.ts +8 -0
  13. package/src/Html.ts +19 -0
  14. package/src/Link.tsrx +25 -7
  15. package/src/Link.tsrx.d.ts +3 -4
  16. package/src/Match.tsrx +40 -13
  17. package/src/Matches.tsrx +7 -7
  18. package/src/Outlet.tsrx +3 -3
  19. package/src/RouteNotFound.tsrx +1 -1
  20. package/src/RouterProvider.tsrx +2 -1
  21. package/src/ScriptOnce.tsrx +23 -0
  22. package/src/ScriptOnce.tsrx.d.ts +3 -0
  23. package/src/Scripts.tsrx +42 -0
  24. package/src/Scripts.tsrx.d.ts +3 -0
  25. package/src/Transitioner.tsrx +15 -13
  26. package/src/assetKeys.ts +11 -0
  27. package/src/context.ts +6 -3
  28. package/src/externalHydration.ts +77 -0
  29. package/src/fileRoute.ts +277 -0
  30. package/src/frameworkTypes.ts +42 -0
  31. package/src/generator-plugin.d.ts +20 -0
  32. package/src/generator-plugin.js +108 -0
  33. package/src/headContentUtils.ts +172 -0
  34. package/src/hooks.ts +151 -0
  35. package/src/index.ts +84 -4
  36. package/src/lazyRouteComponent.ts +2 -1
  37. package/src/link.ts +25 -4
  38. package/src/linkTypes.ts +95 -0
  39. package/src/not-found.tsrx +2 -2
  40. package/src/octane-compiler.d.ts +12 -0
  41. package/src/route.ts +438 -42
  42. package/src/routeHookTypes.ts +228 -0
  43. package/src/router.ts +31 -6
  44. package/src/scriptContentUtils.ts +64 -0
  45. package/src/scroll-restoration.tsrx +16 -0
  46. package/src/scroll-restoration.tsrx.d.ts +3 -0
  47. package/src/ssr/RouterClient.tsrx +36 -0
  48. package/src/ssr/RouterClient.tsrx.d.ts +4 -0
  49. package/src/ssr/RouterServer.tsrx +6 -0
  50. package/src/ssr/RouterServer.tsrx.d.ts +4 -0
  51. package/src/ssr/client.ts +4 -0
  52. package/src/ssr/defaultRenderHandler.ts +11 -0
  53. package/src/ssr/defaultStreamHandler.ts +12 -0
  54. package/src/ssr/renderRouterToStream.ts +195 -0
  55. package/src/ssr/renderRouterToString.ts +58 -0
  56. package/src/ssr/server.ts +8 -0
  57. package/src/structuralSharing.ts +41 -0
  58. package/src/typePrimitives.ts +77 -0
  59. package/src/useAwaited.ts +7 -2
  60. package/src/useBlocker.tsrx +2 -1
  61. package/src/useRouterState.ts +20 -0
@@ -0,0 +1,108 @@
1
+ import { compileToVolarMappings } from 'octane/compiler/volar';
2
+
3
+ /**
4
+ * @typedef {object} AstNode
5
+ * @property {AstNode | Array<AstNode>} [body]
6
+ * @property {number} [start]
7
+ * @property {number} [end]
8
+ * @property {{ native_tsrx_body?: boolean }} [metadata]
9
+ */
10
+
11
+ /**
12
+ * Makes TSRX route modules parseable by the router generator without changing
13
+ * source offsets. The generator applies edits to the original source, so the
14
+ * authored Octane template bodies remain byte-for-byte intact.
15
+ *
16
+ * @param {string} source
17
+ * @param {string} [filename]
18
+ * @returns {string}
19
+ */
20
+ export function maskOctaneRouteSource(source, filename = 'route.tsrx') {
21
+ // Only .tsrx carries the native template dialect. Plain .ts/.tsx route
22
+ // files (robots.txt.ts-style server routes, shared route helpers) must
23
+ // pass through untouched — the TSRX parser is not a general TS parser and
24
+ // rejects valid TS it was never meant to see.
25
+ if (!filename.endsWith('.tsrx')) {
26
+ return source;
27
+ }
28
+
29
+ const { sourceAst } = compileToVolarMappings(source, filename);
30
+ const output = source.split('');
31
+
32
+ for (const body of findNativeTemplateBodies(/** @type {AstNode} */ (sourceAst))) {
33
+ const { start, end } = body;
34
+ output[start] = ' ';
35
+ output[start + 1] = '{';
36
+ for (let index = start + 2; index < end - 1; index++) {
37
+ if (source[index] !== '\n' && source[index] !== '\r') {
38
+ output[index] = ' ';
39
+ }
40
+ }
41
+ output[end - 1] = '}';
42
+ }
43
+
44
+ return output.join('');
45
+ }
46
+
47
+ /**
48
+ * @returns {{
49
+ * name: string
50
+ * transformRouteSource: (options: { source: string, filename: string }) => string
51
+ * formatRoute: (options: { source: string }) => string
52
+ * }}
53
+ */
54
+ export function octaneRouteGeneratorPlugin() {
55
+ return {
56
+ name: 'octane-route-source',
57
+ transformRouteSource: ({ source, filename }) => maskOctaneRouteSource(source, filename),
58
+ // Router scaffolds are already formatted. Returning them unchanged avoids
59
+ // passing TSRX's `@{}` syntax through a TypeScript-only formatter.
60
+ formatRoute: ({ source }) => source,
61
+ };
62
+ }
63
+
64
+ /**
65
+ * @param {AstNode} root
66
+ * @returns {Array<{ start: number, end: number }>}
67
+ */
68
+ function findNativeTemplateBodies(root) {
69
+ /** @type {Array<{ start: number, end: number }>} */
70
+ const bodies = [];
71
+ const visited = new WeakSet();
72
+
73
+ /** @param {unknown} value */
74
+ const visit = (value) => {
75
+ if (!value || typeof value !== 'object' || visited.has(value)) {
76
+ return;
77
+ }
78
+ visited.add(value);
79
+
80
+ if (Array.isArray(value)) {
81
+ for (const item of value) {
82
+ visit(item);
83
+ }
84
+ return;
85
+ }
86
+
87
+ const node = /** @type {AstNode} */ (value);
88
+ if (
89
+ node.metadata?.native_tsrx_body === true &&
90
+ node.body &&
91
+ !Array.isArray(node.body) &&
92
+ typeof node.body.start === 'number' &&
93
+ typeof node.body.end === 'number'
94
+ ) {
95
+ bodies.push({ start: node.body.start, end: node.body.end });
96
+ return;
97
+ }
98
+
99
+ for (const [key, child] of Object.entries(node)) {
100
+ if (key !== 'metadata' && key !== 'loc') {
101
+ visit(child);
102
+ }
103
+ }
104
+ };
105
+
106
+ visit(root);
107
+ return bodies;
108
+ }
@@ -0,0 +1,172 @@
1
+ import {
2
+ appendUniqueUserTags,
3
+ deepEqual,
4
+ escapeHtml,
5
+ getAssetCrossOrigin,
6
+ getScriptPreloadAttrs,
7
+ resolveManifestCssLink,
8
+ } from '@tanstack/router-core';
9
+ import { isServer } from '@tanstack/router-core/isServer';
10
+ import { useRouter } from './context';
11
+ import { splitSlot, subSlot } from './internal';
12
+ import { useStore } from './useStore';
13
+ import type {
14
+ AnyRouteMatch,
15
+ AnyRouter,
16
+ AssetCrossOriginConfig,
17
+ RouterManagedTag,
18
+ } from '@tanstack/router-core';
19
+
20
+ function buildTagsFromMatches(
21
+ router: AnyRouter,
22
+ nonce: string | undefined,
23
+ matches: Array<AnyRouteMatch>,
24
+ assetCrossOrigin?: AssetCrossOriginConfig,
25
+ ): Array<RouterManagedTag> {
26
+ const routeMeta = matches.map((match) => match.meta).filter((meta) => meta !== undefined);
27
+
28
+ const resultMeta: Array<RouterManagedTag> = [];
29
+ const metaByAttribute: Record<string, true> = {};
30
+ let title: RouterManagedTag | undefined;
31
+ for (let i = routeMeta.length - 1; i >= 0; i--) {
32
+ const metas = routeMeta[i]!;
33
+ for (let j = metas.length - 1; j >= 0; j--) {
34
+ const meta = metas[j];
35
+ if (!meta) {
36
+ continue;
37
+ }
38
+
39
+ if ('title' in meta && typeof meta.title === 'string') {
40
+ title ??= { tag: 'title', children: meta.title };
41
+ } else if ('script:ld+json' in meta) {
42
+ try {
43
+ resultMeta.push({
44
+ tag: 'script',
45
+ attrs: { type: 'application/ld+json' },
46
+ children: escapeHtml(JSON.stringify(meta['script:ld+json'])),
47
+ });
48
+ } catch {
49
+ // Ignore values that cannot be serialized as JSON-LD.
50
+ }
51
+ } else {
52
+ const attribute =
53
+ ('name' in meta && typeof meta.name === 'string' ? meta.name : undefined) ??
54
+ ('property' in meta && typeof meta.property === 'string' ? meta.property : undefined);
55
+ if (attribute && metaByAttribute[attribute]) {
56
+ continue;
57
+ }
58
+ if (attribute) {
59
+ metaByAttribute[attribute] = true;
60
+ }
61
+ resultMeta.push({ tag: 'meta', attrs: { ...meta, nonce } });
62
+ }
63
+ }
64
+ }
65
+
66
+ if (title) {
67
+ resultMeta.push(title);
68
+ }
69
+ if (nonce) {
70
+ resultMeta.push({
71
+ tag: 'meta',
72
+ attrs: { property: 'csp-nonce', content: nonce },
73
+ });
74
+ }
75
+ resultMeta.reverse();
76
+
77
+ const links = matches
78
+ .flatMap((match) => match.links ?? [])
79
+ .filter((link) => link !== undefined)
80
+ .map((link) => ({ tag: 'link', attrs: { ...link, nonce } }) satisfies RouterManagedTag);
81
+
82
+ const manifestTags: Array<RouterManagedTag> = [];
83
+ const preloadTags: Array<RouterManagedTag> = [];
84
+ const manifest = router.ssr?.manifest;
85
+ if (manifest) {
86
+ for (const match of matches) {
87
+ for (const link of manifest.routes[match.routeId]?.css ?? []) {
88
+ const resolvedLink = resolveManifestCssLink(link);
89
+ manifestTags.push({
90
+ tag: 'link',
91
+ attrs: {
92
+ rel: 'stylesheet',
93
+ ...resolvedLink,
94
+ crossOrigin:
95
+ getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ?? resolvedLink.crossOrigin,
96
+ nonce,
97
+ },
98
+ });
99
+ }
100
+ for (const preload of manifest.routes[match.routeId]?.preloads ?? []) {
101
+ preloadTags.push({
102
+ tag: 'link',
103
+ attrs: {
104
+ ...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin),
105
+ nonce,
106
+ },
107
+ });
108
+ }
109
+ }
110
+
111
+ if (manifest.inlineStyle) {
112
+ manifestTags.push({
113
+ tag: 'style',
114
+ attrs: { ...manifest.inlineStyle.attrs, nonce },
115
+ children: manifest.inlineStyle.children,
116
+ inlineCss: true,
117
+ });
118
+ }
119
+ }
120
+
121
+ const styles = matches
122
+ .flatMap((match) => match.styles ?? [])
123
+ .filter((style) => style !== undefined)
124
+ .map(
125
+ ({ children, ...attrs }) =>
126
+ ({
127
+ tag: 'style',
128
+ attrs: { ...attrs, nonce },
129
+ children: children,
130
+ }) satisfies RouterManagedTag,
131
+ );
132
+
133
+ const headScripts = matches
134
+ .flatMap((match) => match.headScripts ?? [])
135
+ .filter((script) => script !== undefined)
136
+ .map(
137
+ ({ children, ...attrs }) =>
138
+ ({
139
+ tag: 'script',
140
+ attrs: { ...attrs, nonce },
141
+ children: children,
142
+ }) satisfies RouterManagedTag,
143
+ );
144
+
145
+ const tags: Array<RouterManagedTag> = [];
146
+ appendUniqueUserTags(tags, resultMeta);
147
+ tags.push(...preloadTags);
148
+ appendUniqueUserTags(tags, links);
149
+ tags.push(...manifestTags);
150
+ appendUniqueUserTags(tags, styles);
151
+ appendUniqueUserTags(tags, headScripts);
152
+ return tags;
153
+ }
154
+
155
+ export function useTags(...args: Array<unknown>): Array<RouterManagedTag> {
156
+ const [userArgs, slot] = splitSlot(args);
157
+ const assetCrossOrigin = userArgs[0] as AssetCrossOriginConfig | undefined;
158
+ const router = useRouter();
159
+ const nonce = router.options.ssr?.nonce;
160
+
161
+ if (isServer ?? router.isServer) {
162
+ return buildTagsFromMatches(router, nonce, router.stores.matches.get(), assetCrossOrigin);
163
+ }
164
+
165
+ return useStore(
166
+ router.stores.matches,
167
+ (matches: Array<AnyRouteMatch>) =>
168
+ buildTagsFromMatches(router, nonce, matches, assetCrossOrigin),
169
+ deepEqual,
170
+ subSlot(slot, 'head:tags'),
171
+ );
172
+ }
package/src/hooks.ts CHANGED
@@ -14,6 +14,33 @@ import { replaceEqualDeep } from '@tanstack/router-core';
14
14
  import { useRouter, matchContext } from './context';
15
15
  import { useStore } from './useStore';
16
16
  import { splitSlot, subSlot } from './internal';
17
+ import type {
18
+ AnyRouter,
19
+ FromPathOption,
20
+ RegisteredRouter,
21
+ ThrowConstraint,
22
+ ThrowOrOptional,
23
+ UseLoaderDataResult,
24
+ UseLoaderDepsResult,
25
+ UseNavigateResult,
26
+ UseParamsResult,
27
+ UseRouteContextOptions,
28
+ UseRouteContextResult,
29
+ UseSearchResult,
30
+ } from '@tanstack/router-core';
31
+ import type {
32
+ UseLoaderDataOptions,
33
+ UseLoaderDepsOptions,
34
+ UseLocationBaseOptions,
35
+ UseLocationResult,
36
+ UseMatchOptions,
37
+ UseMatchResult,
38
+ UseMatchesBaseOptions,
39
+ UseMatchesResult,
40
+ UseParamsOptions,
41
+ UseSearchOptions,
42
+ } from './routeHookTypes';
43
+ import type { StructuralSharingOption } from './structuralSharing';
17
44
 
18
45
  // Sentinel store + selection for "no match at this id" (upstream's dummyStore).
19
46
  const dummyStore = {
@@ -37,6 +64,24 @@ function useStructuralSharing(opts: any, router: any, slot: symbol | undefined)
37
64
  };
38
65
  }
39
66
 
67
+ export function useMatch<
68
+ TRouter extends AnyRouter = RegisteredRouter,
69
+ const TFrom extends string | undefined = undefined,
70
+ TStrict extends boolean = true,
71
+ TThrow extends boolean = true,
72
+ TSelected = unknown,
73
+ TStructuralSharing extends boolean = boolean,
74
+ >(
75
+ opts: UseMatchOptions<
76
+ TRouter,
77
+ TFrom,
78
+ TStrict,
79
+ ThrowConstraint<TStrict, TThrow>,
80
+ TSelected,
81
+ TStructuralSharing
82
+ >,
83
+ ): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
84
+ export function useMatch(opts: any, slot: symbol | undefined): any;
40
85
  export function useMatch(...args: any[]): any {
41
86
  const [user, slot] = splitSlot(args);
42
87
  const opts = user[0] ?? {};
@@ -67,6 +112,24 @@ export function useMatch(...args: any[]): any {
67
112
  return undefined;
68
113
  }
69
114
 
115
+ export function useParams<
116
+ TRouter extends AnyRouter = RegisteredRouter,
117
+ const TFrom extends string | undefined = undefined,
118
+ TStrict extends boolean = true,
119
+ TThrow extends boolean = true,
120
+ TSelected = unknown,
121
+ TStructuralSharing extends boolean = boolean,
122
+ >(
123
+ opts: UseParamsOptions<
124
+ TRouter,
125
+ TFrom,
126
+ TStrict,
127
+ ThrowConstraint<TStrict, TThrow>,
128
+ TSelected,
129
+ TStructuralSharing
130
+ >,
131
+ ): ThrowOrOptional<UseParamsResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
132
+ export function useParams(opts: any, slot: symbol | undefined): any;
70
133
  export function useParams(...args: any[]): any {
71
134
  const [user, slot] = splitSlot(args);
72
135
  const opts = user[0] ?? {};
@@ -85,6 +148,24 @@ export function useParams(...args: any[]): any {
85
148
  );
86
149
  }
87
150
 
151
+ export function useSearch<
152
+ TRouter extends AnyRouter = RegisteredRouter,
153
+ const TFrom extends string | undefined = undefined,
154
+ TStrict extends boolean = true,
155
+ TThrow extends boolean = true,
156
+ TSelected = unknown,
157
+ TStructuralSharing extends boolean = boolean,
158
+ >(
159
+ opts: UseSearchOptions<
160
+ TRouter,
161
+ TFrom,
162
+ TStrict,
163
+ ThrowConstraint<TStrict, TThrow>,
164
+ TSelected,
165
+ TStructuralSharing
166
+ >,
167
+ ): ThrowOrOptional<UseSearchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>;
168
+ export function useSearch(opts: any, slot: symbol | undefined): any;
88
169
  export function useSearch(...args: any[]): any {
89
170
  const [user, slot] = splitSlot(args);
90
171
  const opts = user[0] ?? {};
@@ -100,6 +181,16 @@ export function useSearch(...args: any[]): any {
100
181
  );
101
182
  }
102
183
 
184
+ export function useLoaderData<
185
+ TRouter extends AnyRouter = RegisteredRouter,
186
+ const TFrom extends string | undefined = undefined,
187
+ TStrict extends boolean = true,
188
+ TSelected = unknown,
189
+ TStructuralSharing extends boolean = boolean,
190
+ >(
191
+ opts: UseLoaderDataOptions<TRouter, TFrom, TStrict, TSelected, TStructuralSharing>,
192
+ ): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>;
193
+ export function useLoaderData(opts: any, slot: symbol | undefined): any;
103
194
  export function useLoaderData(...args: any[]): any {
104
195
  const [user, slot] = splitSlot(args);
105
196
  const opts = user[0] ?? {};
@@ -114,6 +205,15 @@ export function useLoaderData(...args: any[]): any {
114
205
  );
115
206
  }
116
207
 
208
+ export function useLoaderDeps<
209
+ TRouter extends AnyRouter = RegisteredRouter,
210
+ const TFrom extends string | undefined = undefined,
211
+ TSelected = unknown,
212
+ TStructuralSharing extends boolean = boolean,
213
+ >(
214
+ opts: UseLoaderDepsOptions<TRouter, TFrom, TSelected, TStructuralSharing>,
215
+ ): UseLoaderDepsResult<TRouter, TFrom, TSelected>;
216
+ export function useLoaderDeps(opts: any, slot: symbol | undefined): any;
117
217
  export function useLoaderDeps(...args: any[]): any {
118
218
  const [user, slot] = splitSlot(args);
119
219
  const opts = user[0] ?? {};
@@ -127,6 +227,15 @@ export function useLoaderDeps(...args: any[]): any {
127
227
  );
128
228
  }
129
229
 
230
+ export function useRouteContext<
231
+ TRouter extends AnyRouter = RegisteredRouter,
232
+ const TFrom extends string | undefined = undefined,
233
+ TStrict extends boolean = true,
234
+ TSelected = unknown,
235
+ >(
236
+ opts: UseRouteContextOptions<TRouter, TFrom, TStrict, TSelected>,
237
+ ): UseRouteContextResult<TRouter, TFrom, TStrict, TSelected>;
238
+ export function useRouteContext(opts: any, slot: symbol | undefined): any;
130
239
  export function useRouteContext(...args: any[]): any {
131
240
  const [user, slot] = splitSlot(args);
132
241
  const opts = user[0] ?? {};
@@ -139,6 +248,15 @@ export function useRouteContext(...args: any[]): any {
139
248
  );
140
249
  }
141
250
 
251
+ export function useLocation<
252
+ TRouter extends AnyRouter = RegisteredRouter,
253
+ TSelected = unknown,
254
+ TStructuralSharing extends boolean = boolean,
255
+ >(
256
+ opts?: UseLocationBaseOptions<TRouter, TSelected, TStructuralSharing> &
257
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
258
+ ): UseLocationResult<TRouter, TSelected>;
259
+ export function useLocation(opts: any, slot: symbol | undefined): any;
142
260
  export function useLocation(...args: any[]): any {
143
261
  const [user, slot] = splitSlot(args);
144
262
  const opts = user[0] ?? {};
@@ -151,6 +269,15 @@ export function useLocation(...args: any[]): any {
151
269
  );
152
270
  }
153
271
 
272
+ export function useMatches<
273
+ TRouter extends AnyRouter = RegisteredRouter,
274
+ TSelected = unknown,
275
+ TStructuralSharing extends boolean = boolean,
276
+ >(
277
+ opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
278
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
279
+ ): UseMatchesResult<TRouter, TSelected>;
280
+ export function useMatches(opts: any, slot: symbol | undefined): any;
154
281
  export function useMatches(...args: any[]): any {
155
282
  const [user, slot] = splitSlot(args);
156
283
  const opts = user[0] ?? {};
@@ -163,6 +290,15 @@ export function useMatches(...args: any[]): any {
163
290
  );
164
291
  }
165
292
 
293
+ export function useParentMatches<
294
+ TRouter extends AnyRouter = RegisteredRouter,
295
+ TSelected = unknown,
296
+ TStructuralSharing extends boolean = boolean,
297
+ >(
298
+ opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
299
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
300
+ ): UseMatchesResult<TRouter, TSelected>;
301
+ export function useParentMatches(opts: any, slot: symbol | undefined): any;
166
302
  export function useParentMatches(...args: any[]): any {
167
303
  const [user, slot] = splitSlot(args);
168
304
  const opts = user[0] ?? {};
@@ -182,6 +318,15 @@ export function useParentMatches(...args: any[]): any {
182
318
  );
183
319
  }
184
320
 
321
+ export function useChildMatches<
322
+ TRouter extends AnyRouter = RegisteredRouter,
323
+ TSelected = unknown,
324
+ TStructuralSharing extends boolean = boolean,
325
+ >(
326
+ opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
327
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
328
+ ): UseMatchesResult<TRouter, TSelected>;
329
+ export function useChildMatches(opts: any, slot: symbol | undefined): any;
185
330
  export function useChildMatches(...args: any[]): any {
186
331
  const [user, slot] = splitSlot(args);
187
332
  const opts = user[0] ?? {};
@@ -200,6 +345,11 @@ export function useChildMatches(...args: any[]): any {
200
345
 
201
346
  // Returns a STABLE navigate function (upstream useCallback([from, router])) that
202
347
  // forwards to `router.navigate`, defaulting `from` to the hook's option.
348
+ export function useNavigate<
349
+ TRouter extends AnyRouter = RegisteredRouter,
350
+ TDefaultFrom extends string = string,
351
+ >(options?: { from?: FromPathOption<TRouter, TDefaultFrom> }): UseNavigateResult<TDefaultFrom>;
352
+ export function useNavigate(options: any, slot: symbol | undefined): (to: any) => any;
203
353
  export function useNavigate(...args: any[]): (to: any) => any {
204
354
  const [user, slot] = splitSlot(args);
205
355
  const opts = user[0] ?? {};
@@ -213,6 +363,7 @@ export function useNavigate(...args: any[]): (to: any) => any {
213
363
 
214
364
  // True when the current history entry isn't the first (there is somewhere to go
215
365
  // back to) — per upstream useCanGoBack (location.state.__TSR_index !== 0).
366
+ export function useCanGoBack(): boolean;
216
367
  export function useCanGoBack(...args: any[]): boolean {
217
368
  const [, slot] = splitSlot(args);
218
369
  const router = useRouter();
package/src/index.ts CHANGED
@@ -19,9 +19,11 @@
19
19
  // createLink/useLinkProps, navigation blocking (useBlocker/Block), the full
20
20
  // read-hook set (useMatch and friends, nearest-match resolution via
21
21
  // matchContext), Route/getRouteApi hook accessors, Await/useAwaited, lazy
22
- // routes, and search validation/middleware from core. Deferred: file-based
23
- // routing + codegen (`createFileRoute`, @tanstack/router-plugin), SSR entries
24
- // (RouterServer/RouterClient, HeadContent/Scripts), and devtools.
22
+ // routes, search validation/middleware from core, generated file routes,
23
+ // document/head assets, and Start-compatible SSR/hydration. Devtools remain
24
+ // separate.
25
+ import './frameworkTypes';
26
+
25
27
  export * from '@tanstack/router-core';
26
28
  export {
27
29
  createHistory,
@@ -48,25 +50,39 @@ export {
48
50
  createRoute,
49
51
  createRootRoute,
50
52
  createRootRouteWithContext,
53
+ rootRouteWithContext,
51
54
  createRouteMask,
52
55
  getRouteApi,
53
56
  Route,
54
57
  RootRoute,
55
58
  RouteApi,
59
+ NotFoundRoute,
56
60
  } from './route';
61
+ export {
62
+ FileRoute,
63
+ createFileRoute,
64
+ FileRouteLoader,
65
+ LazyRoute,
66
+ createLazyRoute,
67
+ createLazyFileRoute,
68
+ } from './fileRoute';
57
69
  // Framework-facing component types (react-router parity, on octane renderables).
58
70
  // route.ts also narrows router-core's *Extensions interfaces to these via module
59
71
  // augmentation — mirroring upstream's route.tsx/router.tsx `declare module`.
60
72
  export type {
73
+ DefaultRouteTypes,
61
74
  SyncRouteComponent,
62
75
  AsyncRouteComponent,
63
76
  RouteComponent,
77
+ RouteTypes,
64
78
  ErrorRouteComponent,
65
79
  NotFoundRouteComponent,
80
+ AnyRootRoute,
66
81
  } from './route';
67
82
  export { routerContext, getRouterContext, matchContext, useRouter } from './context';
68
83
  export { useStore } from './useStore';
69
84
  export { useRouterState } from './useRouterState';
85
+ export type { UseRouterStateOptions, UseRouterStateResult } from './useRouterState';
70
86
  export {
71
87
  useMatch,
72
88
  useLocation,
@@ -81,10 +97,53 @@ export {
81
97
  useNavigate,
82
98
  useCanGoBack,
83
99
  } from './hooks';
100
+ export type {
101
+ UseLoaderDataBaseOptions,
102
+ UseLoaderDataOptions,
103
+ UseLoaderDataRoute,
104
+ UseLoaderDepsBaseOptions,
105
+ UseLoaderDepsOptions,
106
+ UseLoaderDepsRoute,
107
+ UseLocationBaseOptions,
108
+ UseLocationResult,
109
+ UseMatchBaseOptions,
110
+ UseMatchOptions,
111
+ UseMatchResult,
112
+ UseMatchRoute,
113
+ UseMatchesBaseOptions,
114
+ UseMatchesResult,
115
+ UseParamsBaseOptions,
116
+ UseParamsOptions,
117
+ UseParamsRoute,
118
+ UseRouteContextRoute,
119
+ UseSearchBaseOptions,
120
+ UseSearchOptions,
121
+ UseSearchRoute,
122
+ } from './routeHookTypes';
84
123
  export { useAwaited } from './useAwaited';
124
+ export type { AwaitOptions } from './useAwaited';
85
125
  export { useLinkProps, createLink, linkOptions } from './link';
126
+ export type { LinkOptionsFn, LinkOptionsFnOptions } from './link';
127
+ export type {
128
+ ActiveLinkOptionProps,
129
+ ActiveLinkOptions,
130
+ CreateLinkProps,
131
+ LinkComponent,
132
+ LinkComponentProps,
133
+ LinkComponentRoute,
134
+ LinkProps,
135
+ LinkPropsChildren,
136
+ OctaneAnchorProps,
137
+ OctaneRenderable,
138
+ UseLinkPropsOptions,
139
+ } from './linkTypes';
86
140
  export { useBlocker, Block } from './useBlocker.tsrx';
87
- export type { UseBlockerOpts, ShouldBlockFn } from './useBlocker.tsrx';
141
+ export type {
142
+ BlockerResolver,
143
+ ShouldBlockFn,
144
+ ShouldBlockFnArgs,
145
+ UseBlockerOpts,
146
+ } from './useBlocker.tsrx';
88
147
  export { useMatchRoute, MatchRoute } from './MatchRoute.tsrx';
89
148
  export { useElementScrollRestoration } from './useElementScrollRestoration';
90
149
  export { lazyRouteComponent } from './lazyRouteComponent';
@@ -101,3 +160,24 @@ export { Match } from './Match.tsrx';
101
160
  export { CatchBoundary, ErrorComponent } from './CatchBoundary.tsrx';
102
161
  export { CatchNotFound, DefaultGlobalNotFound } from './not-found.tsrx';
103
162
  export { ClientOnly, useHydrated } from './ClientOnly.tsrx';
163
+ export { HeadContent } from './HeadContent.tsrx';
164
+ export type { HeadContentProps } from './HeadContent.tsrx';
165
+ export { Scripts } from './Scripts.tsrx';
166
+ export { ScriptOnce } from './ScriptOnce.tsrx';
167
+ export { Asset } from './Asset.tsrx';
168
+ export type { AssetProps } from './Asset.tsrx';
169
+ export { useTags } from './headContentUtils';
170
+ export { Html } from './Html';
171
+ export type { HtmlProps } from './Html';
172
+ export { Head } from './Head';
173
+ export type { HeadProps } from './Head';
174
+ export { Body } from './Body';
175
+ export type { BodyProps } from './Body';
176
+ export type { OctaneElementAttributes, OctaneScriptAttributes } from './frameworkTypes';
177
+ export type {
178
+ InferStructuralSharing,
179
+ ValidateLinkOptions,
180
+ ValidateLinkOptionsArray,
181
+ ValidateUseParamsOptions,
182
+ ValidateUseSearchOptions,
183
+ } from './typePrimitives';
@@ -6,6 +6,7 @@
6
6
  // createRoute({ path: 'item/$id', component: lazyRouteComponent(() => import('./Item')) })
7
7
  import { use, createElement } from 'octane';
8
8
  import { isModuleNotFoundError } from '@tanstack/router-core';
9
+ import { toExternalHydrationThenable } from './externalHydration';
9
10
 
10
11
  export function lazyRouteComponent(
11
12
  importer: () => Promise<any>,
@@ -48,7 +49,7 @@ export function lazyRouteComponent(
48
49
  throw new Promise(() => {});
49
50
  }
50
51
  if (error) throw error;
51
- if (!comp) use(load());
52
+ if (!comp) use(toExternalHydrationThenable(load()));
52
53
  return createElement(comp, props);
53
54
  };
54
55
  Lazy.preload = load;