@octanejs/tanstack-router 0.2.7 → 0.2.9
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/README.md +68 -0
- package/package.json +3 -2
- package/src/Asset.tsrx +3 -135
- package/src/ClientOnly.tsrx +2 -0
- package/src/Link.tsrx +2 -53
- package/src/Matches.tsrx +6 -4
- package/src/Scripts.tsrx +2 -19
- package/src/Transitioner.tsrx +8 -7
- package/src/assetComponent.tsrx +146 -0
- package/src/compiled/Document.tsrx +101 -0
- package/src/compiled/Link.tsrx +18 -0
- package/src/compiled/Matches.tsrx +275 -0
- package/src/compiled/bindings.ts +42 -0
- package/src/compiled/route.tsrx +71 -0
- package/src/compiled.ts +67 -0
- package/src/fileRoute.ts +10 -60
- package/src/frameworkTypes.ts +11 -8
- package/src/headContentBinding.ts +178 -0
- package/src/headContentUtils.ts +2 -170
- package/src/hookBindings.ts +396 -0
- package/src/hooks.ts +17 -374
- package/src/index.ts +2 -0
- package/src/internal.ts +2 -0
- package/src/lazyRouteComponent.ts +6 -50
- package/src/lazyRouteLoader.ts +49 -0
- package/src/link.ts +5 -379
- package/src/linkBinding.ts +391 -0
- package/src/linkComponent.tsrx +61 -0
- package/src/route.ts +187 -51
- package/src/routeHookBindings.ts +146 -0
- package/src/routerStateBinding.ts +41 -0
- package/src/scriptComponent.tsrx +27 -0
- package/src/scriptContentBinding.ts +43 -0
- package/src/scriptContentUtils.ts +2 -35
- package/src/useRouterState.ts +3 -35
package/src/route.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
AnyContext,
|
|
13
13
|
AnyRoute,
|
|
14
14
|
AnyRouter,
|
|
15
|
+
Constrain,
|
|
15
16
|
ConstrainLiteral,
|
|
16
17
|
ErrorComponentProps,
|
|
17
18
|
NotFoundError,
|
|
@@ -35,6 +36,7 @@ import type {
|
|
|
35
36
|
UseNavigateResult,
|
|
36
37
|
} from '@tanstack/router-core';
|
|
37
38
|
import { createElement } from 'octane';
|
|
39
|
+
import { createRouteHookBindings } from './routeHookBindings';
|
|
38
40
|
import type { ComponentBody } from 'octane';
|
|
39
41
|
import {
|
|
40
42
|
useMatch,
|
|
@@ -106,59 +108,13 @@ declare module '@tanstack/router-core' {
|
|
|
106
108
|
// Attach the hook accessors to a route-shaped instance (Route / RootRoute /
|
|
107
109
|
// RouteApi). `strictLoaderHooks: false` is RouteApi's mode — its loader hooks
|
|
108
110
|
// pass `strict: false` upstream (the api may be read from ancestor layouts).
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return useMatch(
|
|
114
|
-
{ select: opts.select, structuralSharing: opts.structuralSharing, from: self.id },
|
|
115
|
-
subSlot(slot, 'r:m'),
|
|
116
|
-
);
|
|
117
|
-
};
|
|
118
|
-
self.useRouteContext = (...args: any[]) => {
|
|
119
|
-
const [user, slot] = splitSlot(args);
|
|
120
|
-
const opts = user[0] ?? {};
|
|
121
|
-
return useRouteContext({ ...opts, from: self.id }, subSlot(slot, 'r:ctx'));
|
|
122
|
-
};
|
|
123
|
-
self.useSearch = (...args: any[]) => {
|
|
124
|
-
const [user, slot] = splitSlot(args);
|
|
125
|
-
const opts = user[0] ?? {};
|
|
126
|
-
return useSearch(
|
|
127
|
-
{ select: opts.select, structuralSharing: opts.structuralSharing, from: self.id },
|
|
128
|
-
subSlot(slot, 'r:s'),
|
|
129
|
-
);
|
|
130
|
-
};
|
|
131
|
-
self.useParams = (...args: any[]) => {
|
|
132
|
-
const [user, slot] = splitSlot(args);
|
|
133
|
-
const opts = user[0] ?? {};
|
|
134
|
-
return useParams(
|
|
135
|
-
{ select: opts.select, structuralSharing: opts.structuralSharing, from: self.id },
|
|
136
|
-
subSlot(slot, 'r:p'),
|
|
137
|
-
);
|
|
138
|
-
};
|
|
139
|
-
self.useLoaderDeps = (...args: any[]) => {
|
|
140
|
-
const [user, slot] = splitSlot(args);
|
|
141
|
-
const opts = user[0] ?? {};
|
|
142
|
-
return useLoaderDeps(
|
|
143
|
-
strictLoaderHooks ? { ...opts, from: self.id } : { ...opts, from: self.id, strict: false },
|
|
144
|
-
subSlot(slot, 'r:d'),
|
|
145
|
-
);
|
|
146
|
-
};
|
|
147
|
-
self.useLoaderData = (...args: any[]) => {
|
|
148
|
-
const [user, slot] = splitSlot(args);
|
|
149
|
-
const opts = user[0] ?? {};
|
|
150
|
-
return useLoaderData(
|
|
151
|
-
strictLoaderHooks ? { ...opts, from: self.id } : { ...opts, from: self.id, strict: false },
|
|
152
|
-
subSlot(slot, 'r:l'),
|
|
153
|
-
);
|
|
154
|
-
};
|
|
155
|
-
}
|
|
111
|
+
const { attachRouteHooks, attachRouteNavigation: attachNavigation } = createRouteHookBindings(
|
|
112
|
+
{ useMatch, useParams, useSearch, useLoaderData, useLoaderDeps, useRouteContext, useNavigate },
|
|
113
|
+
useRouter,
|
|
114
|
+
);
|
|
156
115
|
|
|
157
116
|
function attachRouteNavigation(self: any): void {
|
|
158
|
-
self
|
|
159
|
-
const [, slot] = splitSlot(args);
|
|
160
|
-
return useNavigate({ from: self.fullPath }, subSlot(slot, 'r:n'));
|
|
161
|
-
};
|
|
117
|
+
attachNavigation(self);
|
|
162
118
|
self.Link = (props: Record<string, unknown>) =>
|
|
163
119
|
createElement(Link as any, { from: self.fullPath, ...props });
|
|
164
120
|
}
|
|
@@ -233,6 +189,96 @@ export class Route<
|
|
|
233
189
|
declare useLoaderData: UseLoaderDataRoute<TId>;
|
|
234
190
|
declare useNavigate: () => UseNavigateResult<TFullPath>;
|
|
235
191
|
declare Link: LinkComponentRoute<TFullPath>;
|
|
192
|
+
// These core combinators return this same binding instance. Only their
|
|
193
|
+
// children/file metadata changes; the ordinary bound methods stay available.
|
|
194
|
+
declare addChildren: <const TNewChildren>(
|
|
195
|
+
children: Constrain<TNewChildren, ReadonlyArray<AnyRoute> | Record<string, AnyRoute>>,
|
|
196
|
+
) => Route<
|
|
197
|
+
TRegister,
|
|
198
|
+
TParentRoute,
|
|
199
|
+
TPath,
|
|
200
|
+
TFullPath,
|
|
201
|
+
TCustomId,
|
|
202
|
+
TId,
|
|
203
|
+
TSearchValidator,
|
|
204
|
+
TParams,
|
|
205
|
+
TRouterContext,
|
|
206
|
+
TRouteContextFn,
|
|
207
|
+
TBeforeLoadFn,
|
|
208
|
+
TLoaderDeps,
|
|
209
|
+
TLoaderFn,
|
|
210
|
+
TNewChildren,
|
|
211
|
+
TFileRouteTypes,
|
|
212
|
+
TSSR,
|
|
213
|
+
TServerMiddlewares,
|
|
214
|
+
THandlers
|
|
215
|
+
>;
|
|
216
|
+
declare _addFileChildren: <const TNewChildren>(
|
|
217
|
+
children: TNewChildren,
|
|
218
|
+
) => Route<
|
|
219
|
+
TRegister,
|
|
220
|
+
TParentRoute,
|
|
221
|
+
TPath,
|
|
222
|
+
TFullPath,
|
|
223
|
+
TCustomId,
|
|
224
|
+
TId,
|
|
225
|
+
TSearchValidator,
|
|
226
|
+
TParams,
|
|
227
|
+
TRouterContext,
|
|
228
|
+
TRouteContextFn,
|
|
229
|
+
TBeforeLoadFn,
|
|
230
|
+
TLoaderDeps,
|
|
231
|
+
TLoaderFn,
|
|
232
|
+
TNewChildren,
|
|
233
|
+
TFileRouteTypes,
|
|
234
|
+
TSSR,
|
|
235
|
+
TServerMiddlewares,
|
|
236
|
+
THandlers
|
|
237
|
+
>;
|
|
238
|
+
declare _addFileTypes: <TNewFileRouteTypes>() => Route<
|
|
239
|
+
TRegister,
|
|
240
|
+
TParentRoute,
|
|
241
|
+
TPath,
|
|
242
|
+
TFullPath,
|
|
243
|
+
TCustomId,
|
|
244
|
+
TId,
|
|
245
|
+
TSearchValidator,
|
|
246
|
+
TParams,
|
|
247
|
+
TRouterContext,
|
|
248
|
+
TRouteContextFn,
|
|
249
|
+
TBeforeLoadFn,
|
|
250
|
+
TLoaderDeps,
|
|
251
|
+
TLoaderFn,
|
|
252
|
+
TChildren,
|
|
253
|
+
TNewFileRouteTypes,
|
|
254
|
+
TSSR,
|
|
255
|
+
TServerMiddlewares,
|
|
256
|
+
THandlers
|
|
257
|
+
>;
|
|
258
|
+
declare lazy: (
|
|
259
|
+
...args: Parameters<
|
|
260
|
+
RouteCore<
|
|
261
|
+
TRegister,
|
|
262
|
+
TParentRoute,
|
|
263
|
+
TPath,
|
|
264
|
+
TFullPath,
|
|
265
|
+
TCustomId,
|
|
266
|
+
TId,
|
|
267
|
+
TSearchValidator,
|
|
268
|
+
TParams,
|
|
269
|
+
TRouterContext,
|
|
270
|
+
TRouteContextFn,
|
|
271
|
+
TBeforeLoadFn,
|
|
272
|
+
TLoaderDeps,
|
|
273
|
+
TLoaderFn,
|
|
274
|
+
TChildren,
|
|
275
|
+
TFileRouteTypes,
|
|
276
|
+
TSSR,
|
|
277
|
+
TServerMiddlewares,
|
|
278
|
+
THandlers
|
|
279
|
+
>['lazy']
|
|
280
|
+
>
|
|
281
|
+
) => this;
|
|
236
282
|
|
|
237
283
|
/** @deprecated Use the `createRoute` function instead. */
|
|
238
284
|
constructor(
|
|
@@ -313,6 +359,96 @@ export class RootRoute<
|
|
|
313
359
|
declare useLoaderData: UseLoaderDataRoute<RootRouteId>;
|
|
314
360
|
declare useNavigate: () => UseNavigateResult<'/'>;
|
|
315
361
|
declare Link: LinkComponentRoute<'/'>;
|
|
362
|
+
// These core combinators return this same binding instance. Only their
|
|
363
|
+
// children/file metadata changes; the ordinary bound methods stay available.
|
|
364
|
+
declare addChildren: <const TNewChildren>(
|
|
365
|
+
children: Constrain<TNewChildren, ReadonlyArray<AnyRoute> | Record<string, AnyRoute>>,
|
|
366
|
+
) => Route<
|
|
367
|
+
TRegister,
|
|
368
|
+
any,
|
|
369
|
+
'/',
|
|
370
|
+
'/',
|
|
371
|
+
string,
|
|
372
|
+
RootRouteId,
|
|
373
|
+
TSearchValidator,
|
|
374
|
+
{},
|
|
375
|
+
TRouterContext,
|
|
376
|
+
TRouteContextFn,
|
|
377
|
+
TBeforeLoadFn,
|
|
378
|
+
TLoaderDeps,
|
|
379
|
+
TLoaderFn,
|
|
380
|
+
TNewChildren,
|
|
381
|
+
TFileRouteTypes,
|
|
382
|
+
TSSR,
|
|
383
|
+
TServerMiddlewares,
|
|
384
|
+
THandlers
|
|
385
|
+
>;
|
|
386
|
+
declare _addFileChildren: <const TNewChildren>(
|
|
387
|
+
children: TNewChildren,
|
|
388
|
+
) => Route<
|
|
389
|
+
TRegister,
|
|
390
|
+
any,
|
|
391
|
+
'/',
|
|
392
|
+
'/',
|
|
393
|
+
string,
|
|
394
|
+
RootRouteId,
|
|
395
|
+
TSearchValidator,
|
|
396
|
+
{},
|
|
397
|
+
TRouterContext,
|
|
398
|
+
TRouteContextFn,
|
|
399
|
+
TBeforeLoadFn,
|
|
400
|
+
TLoaderDeps,
|
|
401
|
+
TLoaderFn,
|
|
402
|
+
TNewChildren,
|
|
403
|
+
TFileRouteTypes,
|
|
404
|
+
TSSR,
|
|
405
|
+
TServerMiddlewares,
|
|
406
|
+
THandlers
|
|
407
|
+
>;
|
|
408
|
+
declare _addFileTypes: <TNewFileRouteTypes>() => Route<
|
|
409
|
+
TRegister,
|
|
410
|
+
any,
|
|
411
|
+
'/',
|
|
412
|
+
'/',
|
|
413
|
+
string,
|
|
414
|
+
RootRouteId,
|
|
415
|
+
TSearchValidator,
|
|
416
|
+
{},
|
|
417
|
+
TRouterContext,
|
|
418
|
+
TRouteContextFn,
|
|
419
|
+
TBeforeLoadFn,
|
|
420
|
+
TLoaderDeps,
|
|
421
|
+
TLoaderFn,
|
|
422
|
+
TChildren,
|
|
423
|
+
TNewFileRouteTypes,
|
|
424
|
+
TSSR,
|
|
425
|
+
TServerMiddlewares,
|
|
426
|
+
THandlers
|
|
427
|
+
>;
|
|
428
|
+
declare lazy: (
|
|
429
|
+
...args: Parameters<
|
|
430
|
+
RouteCore<
|
|
431
|
+
TRegister,
|
|
432
|
+
any,
|
|
433
|
+
'/',
|
|
434
|
+
'/',
|
|
435
|
+
string,
|
|
436
|
+
RootRouteId,
|
|
437
|
+
TSearchValidator,
|
|
438
|
+
{},
|
|
439
|
+
TRouterContext,
|
|
440
|
+
TRouteContextFn,
|
|
441
|
+
TBeforeLoadFn,
|
|
442
|
+
TLoaderDeps,
|
|
443
|
+
TLoaderFn,
|
|
444
|
+
TChildren,
|
|
445
|
+
TFileRouteTypes,
|
|
446
|
+
TSSR,
|
|
447
|
+
TServerMiddlewares,
|
|
448
|
+
THandlers
|
|
449
|
+
>['lazy']
|
|
450
|
+
>
|
|
451
|
+
) => this;
|
|
316
452
|
|
|
317
453
|
/** @deprecated Use `createRootRoute()` instead. */
|
|
318
454
|
constructor(
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strong';
|
|
2
|
+
|
|
3
|
+
import type * as Hooks from './hooks';
|
|
4
|
+
import type { useRouter as UseRouter } from './context';
|
|
5
|
+
import { splitSlot, subSlot } from './internal';
|
|
6
|
+
|
|
7
|
+
type RouteHooks = Pick<
|
|
8
|
+
typeof Hooks,
|
|
9
|
+
| 'useMatch'
|
|
10
|
+
| 'useParams'
|
|
11
|
+
| 'useSearch'
|
|
12
|
+
| 'useLoaderData'
|
|
13
|
+
| 'useLoaderDeps'
|
|
14
|
+
| 'useRouteContext'
|
|
15
|
+
| 'useNavigate'
|
|
16
|
+
>;
|
|
17
|
+
|
|
18
|
+
// Each entry supplies its context once. Routes retain the same hook slots and
|
|
19
|
+
// inherited router-core combinators without creating ordinary-renderer routes.
|
|
20
|
+
export function createRouteHookBindings(hooks: RouteHooks, useRouter: typeof UseRouter) {
|
|
21
|
+
const {
|
|
22
|
+
useMatch,
|
|
23
|
+
useParams,
|
|
24
|
+
useSearch,
|
|
25
|
+
useLoaderData,
|
|
26
|
+
useLoaderDeps,
|
|
27
|
+
useRouteContext,
|
|
28
|
+
useNavigate,
|
|
29
|
+
} = hooks;
|
|
30
|
+
function attachRouteHooks(self: any, strictLoaderHooks: boolean): void {
|
|
31
|
+
self.useMatch = (...args: any[]) => {
|
|
32
|
+
const [user, slot] = splitSlot(args);
|
|
33
|
+
const opts = user[0] ?? {};
|
|
34
|
+
return useMatch(
|
|
35
|
+
{ select: opts.select, structuralSharing: opts.structuralSharing, from: self.id },
|
|
36
|
+
subSlot(slot, 'r:m'),
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
self.useRouteContext = (...args: any[]) => {
|
|
40
|
+
const [user, slot] = splitSlot(args);
|
|
41
|
+
const opts = user[0] ?? {};
|
|
42
|
+
return useRouteContext({ ...opts, from: self.id }, subSlot(slot, 'r:ctx'));
|
|
43
|
+
};
|
|
44
|
+
self.useSearch = (...args: any[]) => {
|
|
45
|
+
const [user, slot] = splitSlot(args);
|
|
46
|
+
const opts = user[0] ?? {};
|
|
47
|
+
return useSearch(
|
|
48
|
+
{ select: opts.select, structuralSharing: opts.structuralSharing, from: self.id },
|
|
49
|
+
subSlot(slot, 'r:s'),
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
self.useParams = (...args: any[]) => {
|
|
53
|
+
const [user, slot] = splitSlot(args);
|
|
54
|
+
const opts = user[0] ?? {};
|
|
55
|
+
return useParams(
|
|
56
|
+
{ select: opts.select, structuralSharing: opts.structuralSharing, from: self.id },
|
|
57
|
+
subSlot(slot, 'r:p'),
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
self.useLoaderDeps = (...args: any[]) => {
|
|
61
|
+
const [user, slot] = splitSlot(args);
|
|
62
|
+
const opts = user[0] ?? {};
|
|
63
|
+
return useLoaderDeps(
|
|
64
|
+
strictLoaderHooks ? { ...opts, from: self.id } : { ...opts, from: self.id, strict: false },
|
|
65
|
+
subSlot(slot, 'r:d'),
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
self.useLoaderData = (...args: any[]) => {
|
|
69
|
+
const [user, slot] = splitSlot(args);
|
|
70
|
+
const opts = user[0] ?? {};
|
|
71
|
+
return useLoaderData(
|
|
72
|
+
strictLoaderHooks ? { ...opts, from: self.id } : { ...opts, from: self.id, strict: false },
|
|
73
|
+
subSlot(slot, 'r:l'),
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function attachRouteNavigation(self: any): void {
|
|
79
|
+
self.useNavigate = (...args: any[]) => {
|
|
80
|
+
const [, slot] = splitSlot(args);
|
|
81
|
+
return useNavigate({ from: self.fullPath }, subSlot(slot, 'r:n'));
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function attachLazyRouteHooks(self: any) {
|
|
86
|
+
const id = self.options.id;
|
|
87
|
+
self.useMatch = ((...args: Array<any>) => {
|
|
88
|
+
const [user, slot] = splitSlot(args);
|
|
89
|
+
const options = user[0] ?? {};
|
|
90
|
+
return useMatch(
|
|
91
|
+
{
|
|
92
|
+
select: options.select,
|
|
93
|
+
from: id,
|
|
94
|
+
structuralSharing: options.structuralSharing,
|
|
95
|
+
},
|
|
96
|
+
subSlot(slot, 'lr:m'),
|
|
97
|
+
);
|
|
98
|
+
}) as typeof self.useMatch;
|
|
99
|
+
self.useRouteContext = ((...args: Array<any>) => {
|
|
100
|
+
const [user, slot] = splitSlot(args);
|
|
101
|
+
return useRouteContext({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:c'));
|
|
102
|
+
}) as typeof self.useRouteContext;
|
|
103
|
+
self.useSearch = ((...args: Array<any>) => {
|
|
104
|
+
const [user, slot] = splitSlot(args);
|
|
105
|
+
const options = user[0] ?? {};
|
|
106
|
+
return useSearch(
|
|
107
|
+
{
|
|
108
|
+
select: options.select,
|
|
109
|
+
from: id,
|
|
110
|
+
structuralSharing: options.structuralSharing,
|
|
111
|
+
},
|
|
112
|
+
subSlot(slot, 'lr:s'),
|
|
113
|
+
);
|
|
114
|
+
}) as typeof self.useSearch;
|
|
115
|
+
self.useParams = ((...args: Array<any>) => {
|
|
116
|
+
const [user, slot] = splitSlot(args);
|
|
117
|
+
const options = user[0] ?? {};
|
|
118
|
+
return useParams(
|
|
119
|
+
{
|
|
120
|
+
select: options.select,
|
|
121
|
+
from: id,
|
|
122
|
+
structuralSharing: options.structuralSharing,
|
|
123
|
+
},
|
|
124
|
+
subSlot(slot, 'lr:p'),
|
|
125
|
+
);
|
|
126
|
+
}) as typeof self.useParams;
|
|
127
|
+
self.useLoaderDeps = ((...args: Array<any>) => {
|
|
128
|
+
const [user, slot] = splitSlot(args);
|
|
129
|
+
return useLoaderDeps({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:d'));
|
|
130
|
+
}) as typeof self.useLoaderDeps;
|
|
131
|
+
self.useLoaderData = ((...args: Array<any>) => {
|
|
132
|
+
const [user, slot] = splitSlot(args);
|
|
133
|
+
return useLoaderData({ ...(user[0] ?? {}), from: id }, subSlot(slot, 'lr:l'));
|
|
134
|
+
}) as typeof self.useLoaderData;
|
|
135
|
+
self.useNavigate = ((...args: Array<any>) => {
|
|
136
|
+
const [, slot] = splitSlot(args);
|
|
137
|
+
const router = useRouter();
|
|
138
|
+
return useNavigate(
|
|
139
|
+
{ from: (router.routesById as Record<string, any>)[id].fullPath },
|
|
140
|
+
subSlot(slot, 'lr:n'),
|
|
141
|
+
);
|
|
142
|
+
}) as typeof self.useNavigate;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { attachRouteHooks, attachRouteNavigation, attachLazyRouteHooks };
|
|
146
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// The reactive base every router hook reads through: subscribe to the router's
|
|
2
|
+
// canonical snapshot store (`router.stores.__store`) and select a slice.
|
|
3
|
+
import { useStore } from './useStore';
|
|
4
|
+
import type { useRouter as StandardUseRouter } from './context';
|
|
5
|
+
import { splitSlot, subSlot } from './internal';
|
|
6
|
+
import type { AnyRouter, RegisteredRouter, RouterState } from '@tanstack/router-core';
|
|
7
|
+
import type { StructuralSharingOption, ValidateSelected } from './structuralSharing';
|
|
8
|
+
|
|
9
|
+
export type UseRouterStateOptions<TRouter extends AnyRouter, TSelected, TStructuralSharing> = {
|
|
10
|
+
router?: TRouter;
|
|
11
|
+
select?: (
|
|
12
|
+
state: RouterState<TRouter['routeTree']>,
|
|
13
|
+
) => ValidateSelected<TRouter, TSelected, TStructuralSharing>;
|
|
14
|
+
} & StructuralSharingOption<TRouter, TSelected, TStructuralSharing>;
|
|
15
|
+
|
|
16
|
+
export type UseRouterStateResult<TRouter extends AnyRouter, TSelected> = unknown extends TSelected
|
|
17
|
+
? RouterState<TRouter['routeTree']>
|
|
18
|
+
: TSelected;
|
|
19
|
+
|
|
20
|
+
export function createUseRouterState(useRouter: typeof StandardUseRouter) {
|
|
21
|
+
function useRouterState<
|
|
22
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
23
|
+
TSelected = unknown,
|
|
24
|
+
TStructuralSharing extends boolean = boolean,
|
|
25
|
+
>(
|
|
26
|
+
opts?: UseRouterStateOptions<TRouter, TSelected, TStructuralSharing>,
|
|
27
|
+
): UseRouterStateResult<TRouter, TSelected>;
|
|
28
|
+
function useRouterState(...args: any[]): any {
|
|
29
|
+
const [user, slot] = splitSlot(args);
|
|
30
|
+
const opts = (user[0] ?? {}) as { select?: (s: any) => any; router?: any };
|
|
31
|
+
const router = useRouter(opts.router ? { router: opts.router } : undefined);
|
|
32
|
+
return useStore(
|
|
33
|
+
router.stores.__store,
|
|
34
|
+
opts.select ?? ((s: any) => s),
|
|
35
|
+
undefined,
|
|
36
|
+
subSlot(slot, 'rs'),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return useRouterState;
|
|
41
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createElement, useId } from 'octane';
|
|
2
|
+
import type { RouterManagedTag } from '@tanstack/router-core';
|
|
3
|
+
import { getAssetKeys } from './assetKeys.ts';
|
|
4
|
+
|
|
5
|
+
// router-core's initial hydration scripts remove themselves before adoption.
|
|
6
|
+
// Host descriptors leave no per-item template frames behind when they do, and
|
|
7
|
+
// create live script elements that execute on later client navigation. This
|
|
8
|
+
// shared owner intentionally remains outside Strong mode for both entries.
|
|
9
|
+
export function createScripts(useScripts: () => Array<RouterManagedTag>) {
|
|
10
|
+
function Scripts() @{
|
|
11
|
+
const owner = useId();
|
|
12
|
+
const scripts = useScripts();
|
|
13
|
+
const keys = getAssetKeys('body', owner, scripts);
|
|
14
|
+
<>
|
|
15
|
+
{scripts.map((script, index) => {
|
|
16
|
+
const { assetKey, managedKey } = keys[index]!;
|
|
17
|
+
return createElement('script', {
|
|
18
|
+
...script.attrs,
|
|
19
|
+
key: assetKey,
|
|
20
|
+
'data-tsr-managed-key': managedKey,
|
|
21
|
+
dangerouslySetInnerHTML: { __html: script.children ?? '' },
|
|
22
|
+
});
|
|
23
|
+
})}
|
|
24
|
+
</>
|
|
25
|
+
}
|
|
26
|
+
return Scripts;
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strong';
|
|
2
|
+
|
|
3
|
+
import { composeSsrBodyScripts, deepEqual, getSsrBodyScriptParts } from '@tanstack/router-core';
|
|
4
|
+
import { isServer } from '@tanstack/router-core/isServer';
|
|
5
|
+
import type { useRouter as UseRouter } from './context';
|
|
6
|
+
import { splitSlot, subSlot } from './internal';
|
|
7
|
+
import { useStore } from './useStore';
|
|
8
|
+
import type { AnyRouteMatch, RouterManagedTag } from '@tanstack/router-core';
|
|
9
|
+
|
|
10
|
+
// router-core 1.171.34 composes body scripts through `getSsrBodyScriptParts` +
|
|
11
|
+
// `composeSsrBodyScripts`, and the initial hydration `<Scripts>` take moved from
|
|
12
|
+
// the removed `serverSsr.takeBufferedScripts()` (a single buffered tag) to
|
|
13
|
+
// `serverSsr.takeInitialHydrationScriptTags()` (hydration tags plus the streaming
|
|
14
|
+
// boundary), which `composeSsrBodyScripts` interleaves.
|
|
15
|
+
const routeScriptAttrs = { suppressHydrationWarning: true };
|
|
16
|
+
|
|
17
|
+
export function createUseScripts(useRouter: typeof UseRouter) {
|
|
18
|
+
function useScripts(...args: Array<unknown>): Array<RouterManagedTag> {
|
|
19
|
+
const [, slot] = splitSlot(args);
|
|
20
|
+
const router = useRouter();
|
|
21
|
+
const nonce = router.options.ssr?.nonce;
|
|
22
|
+
const getScripts = (matches: Array<AnyRouteMatch>) =>
|
|
23
|
+
composeSsrBodyScripts(
|
|
24
|
+
getSsrBodyScriptParts(matches, router.ssr?.manifest, nonce, routeScriptAttrs),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (isServer ?? router.isServer) {
|
|
28
|
+
return composeSsrBodyScripts(
|
|
29
|
+
getSsrBodyScriptParts(
|
|
30
|
+
router.stores.matches.get(),
|
|
31
|
+
router.ssr?.manifest,
|
|
32
|
+
nonce,
|
|
33
|
+
routeScriptAttrs,
|
|
34
|
+
),
|
|
35
|
+
router.serverSsr?.takeInitialHydrationScriptTags(),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return useStore(router.stores.matches, getScripts, deepEqual, subSlot(slot, 'body:scripts'));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return useScripts;
|
|
43
|
+
}
|
|
@@ -1,37 +1,4 @@
|
|
|
1
|
-
import { composeSsrBodyScripts, deepEqual, getSsrBodyScriptParts } from '@tanstack/router-core';
|
|
2
|
-
import { isServer } from '@tanstack/router-core/isServer';
|
|
3
1
|
import { useRouter } from './context';
|
|
4
|
-
import {
|
|
5
|
-
import { useStore } from './useStore';
|
|
6
|
-
import type { AnyRouteMatch, RouterManagedTag } from '@tanstack/router-core';
|
|
2
|
+
import { createUseScripts } from './scriptContentBinding';
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
// `composeSsrBodyScripts`, and the initial hydration `<Scripts>` take moved from
|
|
10
|
-
// the removed `serverSsr.takeBufferedScripts()` (a single buffered tag) to
|
|
11
|
-
// `serverSsr.takeInitialHydrationScriptTags()` (hydration tags plus the streaming
|
|
12
|
-
// boundary), which `composeSsrBodyScripts` interleaves.
|
|
13
|
-
const routeScriptAttrs = { suppressHydrationWarning: true };
|
|
14
|
-
|
|
15
|
-
export function useScripts(...args: Array<unknown>): Array<RouterManagedTag> {
|
|
16
|
-
const [, slot] = splitSlot(args);
|
|
17
|
-
const router = useRouter();
|
|
18
|
-
const nonce = router.options.ssr?.nonce;
|
|
19
|
-
const getScripts = (matches: Array<AnyRouteMatch>) =>
|
|
20
|
-
composeSsrBodyScripts(
|
|
21
|
-
getSsrBodyScriptParts(matches, router.ssr?.manifest, nonce, routeScriptAttrs),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
if (isServer ?? router.isServer) {
|
|
25
|
-
return composeSsrBodyScripts(
|
|
26
|
-
getSsrBodyScriptParts(
|
|
27
|
-
router.stores.matches.get(),
|
|
28
|
-
router.ssr?.manifest,
|
|
29
|
-
nonce,
|
|
30
|
-
routeScriptAttrs,
|
|
31
|
-
),
|
|
32
|
-
router.serverSsr?.takeInitialHydrationScriptTags(),
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return useStore(router.stores.matches, getScripts, deepEqual, subSlot(slot, 'body:scripts'));
|
|
37
|
-
}
|
|
4
|
+
export const useScripts = createUseScripts(useRouter);
|
package/src/useRouterState.ts
CHANGED
|
@@ -1,37 +1,5 @@
|
|
|
1
|
-
// The reactive base every router hook reads through: subscribe to the router's
|
|
2
|
-
// canonical snapshot store (`router.stores.__store`) and select a slice.
|
|
3
|
-
import { useStore } from './useStore';
|
|
4
1
|
import { useRouter } from './context';
|
|
5
|
-
import {
|
|
6
|
-
import type { AnyRouter, RegisteredRouter, RouterState } from '@tanstack/router-core';
|
|
7
|
-
import type { StructuralSharingOption, ValidateSelected } from './structuralSharing';
|
|
2
|
+
import { createUseRouterState } from './routerStateBinding';
|
|
8
3
|
|
|
9
|
-
export type UseRouterStateOptions
|
|
10
|
-
|
|
11
|
-
select?: (
|
|
12
|
-
state: RouterState<TRouter['routeTree']>,
|
|
13
|
-
) => ValidateSelected<TRouter, TSelected, TStructuralSharing>;
|
|
14
|
-
} & StructuralSharingOption<TRouter, TSelected, TStructuralSharing>;
|
|
15
|
-
|
|
16
|
-
export type UseRouterStateResult<TRouter extends AnyRouter, TSelected> = unknown extends TSelected
|
|
17
|
-
? RouterState<TRouter['routeTree']>
|
|
18
|
-
: TSelected;
|
|
19
|
-
|
|
20
|
-
export function useRouterState<
|
|
21
|
-
TRouter extends AnyRouter = RegisteredRouter,
|
|
22
|
-
TSelected = unknown,
|
|
23
|
-
TStructuralSharing extends boolean = boolean,
|
|
24
|
-
>(
|
|
25
|
-
opts?: UseRouterStateOptions<TRouter, TSelected, TStructuralSharing>,
|
|
26
|
-
): UseRouterStateResult<TRouter, TSelected>;
|
|
27
|
-
export function useRouterState(...args: any[]): any {
|
|
28
|
-
const [user, slot] = splitSlot(args);
|
|
29
|
-
const opts = (user[0] ?? {}) as { select?: (s: any) => any; router?: any };
|
|
30
|
-
const router = useRouter(opts.router ? { router: opts.router } : undefined);
|
|
31
|
-
return useStore(
|
|
32
|
-
router.stores.__store,
|
|
33
|
-
opts.select ?? ((s: any) => s),
|
|
34
|
-
undefined,
|
|
35
|
-
subSlot(slot, 'rs'),
|
|
36
|
-
);
|
|
37
|
-
}
|
|
4
|
+
export type { UseRouterStateOptions, UseRouterStateResult } from './routerStateBinding';
|
|
5
|
+
export const useRouterState = createUseRouterState(useRouter);
|