@richie-router/react 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/router.cjs +288 -96
- package/dist/cjs/router.test.cjs +413 -0
- package/dist/esm/router.mjs +288 -96
- package/dist/esm/router.test.mjs +418 -0
- package/dist/types/router.d.ts +28 -19
- package/package.json +2 -2
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
// packages/react/src/router.test.ts
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
import {
|
|
4
|
+
createFileRoute,
|
|
5
|
+
createMemoryHistory,
|
|
6
|
+
createRootRoute,
|
|
7
|
+
createRouter
|
|
8
|
+
} from "./router.mjs";
|
|
9
|
+
function createTestRouteTree(options) {
|
|
10
|
+
const rootRoute = createRootRoute({
|
|
11
|
+
component: () => null
|
|
12
|
+
});
|
|
13
|
+
const indexRoute = createFileRoute("/")({
|
|
14
|
+
component: () => null
|
|
15
|
+
});
|
|
16
|
+
const aboutRoute = createFileRoute("/about")({
|
|
17
|
+
component: () => null
|
|
18
|
+
});
|
|
19
|
+
aboutRoute._setServerHead(options?.serverHead);
|
|
20
|
+
return rootRoute._addFileChildren({
|
|
21
|
+
index: indexRoute,
|
|
22
|
+
about: aboutRoute
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function createNestedHeadRouteTree() {
|
|
26
|
+
const rootRoute = createRootRoute({
|
|
27
|
+
component: () => null
|
|
28
|
+
});
|
|
29
|
+
const postsRoute = createFileRoute("/posts")({
|
|
30
|
+
component: () => null,
|
|
31
|
+
head: [
|
|
32
|
+
{ tag: "title", children: "Posts" }
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
const postRoute = createFileRoute("/posts/$postId")({
|
|
36
|
+
component: () => null
|
|
37
|
+
});
|
|
38
|
+
rootRoute._setServerHead(true);
|
|
39
|
+
postRoute._setServerHead(true);
|
|
40
|
+
postsRoute._addFileChildren({
|
|
41
|
+
post: postRoute
|
|
42
|
+
});
|
|
43
|
+
return rootRoute._addFileChildren({
|
|
44
|
+
posts: postsRoute
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function createRootServerHeadTree() {
|
|
48
|
+
const rootRoute = createRootRoute({
|
|
49
|
+
component: () => null
|
|
50
|
+
});
|
|
51
|
+
const indexRoute = createFileRoute("/")({
|
|
52
|
+
component: () => null
|
|
53
|
+
});
|
|
54
|
+
const aboutRoute = createFileRoute("/about")({
|
|
55
|
+
component: () => null
|
|
56
|
+
});
|
|
57
|
+
rootRoute._setServerHead(true);
|
|
58
|
+
return rootRoute._addFileChildren({
|
|
59
|
+
index: indexRoute,
|
|
60
|
+
about: aboutRoute
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function createNestedServerHeadTree() {
|
|
64
|
+
const rootRoute = createRootRoute({
|
|
65
|
+
component: () => null
|
|
66
|
+
});
|
|
67
|
+
const postsRoute = createFileRoute("/posts")({
|
|
68
|
+
component: () => null
|
|
69
|
+
});
|
|
70
|
+
const postRoute = createFileRoute("/posts/$postId")({
|
|
71
|
+
component: () => null
|
|
72
|
+
});
|
|
73
|
+
rootRoute._setServerHead(true);
|
|
74
|
+
postsRoute._setServerHead(true);
|
|
75
|
+
postRoute._setServerHead(true);
|
|
76
|
+
postsRoute._addFileChildren({
|
|
77
|
+
post: postRoute
|
|
78
|
+
});
|
|
79
|
+
return rootRoute._addFileChildren({
|
|
80
|
+
posts: postsRoute
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
describe("createRouter basePath", () => {
|
|
84
|
+
test("strips the basePath from the current history location", () => {
|
|
85
|
+
const history = createMemoryHistory({
|
|
86
|
+
initialEntries: ["/project/about?tab=team#bio"]
|
|
87
|
+
});
|
|
88
|
+
const router = createRouter({
|
|
89
|
+
routeTree: createTestRouteTree(),
|
|
90
|
+
history,
|
|
91
|
+
basePath: "/project"
|
|
92
|
+
});
|
|
93
|
+
expect(router.state.location.pathname).toBe("/about");
|
|
94
|
+
expect(router.state.location.href).toBe("/about?tab=team#bio");
|
|
95
|
+
expect(router.state.matches.at(-1)?.route.fullPath).toBe("/about");
|
|
96
|
+
});
|
|
97
|
+
test("prefixes generated hrefs and history writes with the basePath", async () => {
|
|
98
|
+
const history = createMemoryHistory({
|
|
99
|
+
initialEntries: ["/project"]
|
|
100
|
+
});
|
|
101
|
+
const router = createRouter({
|
|
102
|
+
routeTree: createTestRouteTree(),
|
|
103
|
+
history,
|
|
104
|
+
basePath: "/project"
|
|
105
|
+
});
|
|
106
|
+
expect(router.buildHref({ to: "/about" })).toBe("/project/about");
|
|
107
|
+
await router.navigate({
|
|
108
|
+
to: "/about",
|
|
109
|
+
search: {
|
|
110
|
+
tab: "team"
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
expect(history.location.href).toBe("/project/about?tab=team");
|
|
114
|
+
expect(router.state.location.href).toBe("/about?tab=team");
|
|
115
|
+
});
|
|
116
|
+
test("uses the basePath for the default head API endpoint", async () => {
|
|
117
|
+
const history = createMemoryHistory({
|
|
118
|
+
initialEntries: ["/project/about"]
|
|
119
|
+
});
|
|
120
|
+
const fetchCalls = [];
|
|
121
|
+
const originalFetch = globalThis.fetch;
|
|
122
|
+
globalThis.fetch = async (input) => {
|
|
123
|
+
fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
|
|
124
|
+
return new Response(JSON.stringify({
|
|
125
|
+
head: [],
|
|
126
|
+
routeHeads: [
|
|
127
|
+
{ routeId: "/about", head: [] }
|
|
128
|
+
]
|
|
129
|
+
}), {
|
|
130
|
+
status: 200,
|
|
131
|
+
headers: {
|
|
132
|
+
"content-type": "application/json"
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
try {
|
|
137
|
+
const router = createRouter({
|
|
138
|
+
routeTree: createTestRouteTree({ serverHead: true }),
|
|
139
|
+
history,
|
|
140
|
+
basePath: "/project"
|
|
141
|
+
});
|
|
142
|
+
await router.load();
|
|
143
|
+
expect(fetchCalls).toHaveLength(1);
|
|
144
|
+
expect(fetchCalls[0]).toBe("/project/head-api?href=%2Fproject%2Fabout");
|
|
145
|
+
} finally {
|
|
146
|
+
globalThis.fetch = originalFetch;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
test("uses one document head request and preserves inline head precedence", async () => {
|
|
150
|
+
const history = createMemoryHistory({
|
|
151
|
+
initialEntries: ["/posts/alpha"]
|
|
152
|
+
});
|
|
153
|
+
const fetchCalls = [];
|
|
154
|
+
const originalFetch = globalThis.fetch;
|
|
155
|
+
globalThis.fetch = async (input) => {
|
|
156
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
157
|
+
fetchCalls.push(url);
|
|
158
|
+
return new Response(JSON.stringify({
|
|
159
|
+
head: [
|
|
160
|
+
{ tag: "title", children: "Site" },
|
|
161
|
+
{ tag: "title", children: "Alpha" }
|
|
162
|
+
],
|
|
163
|
+
routeHeads: [
|
|
164
|
+
{
|
|
165
|
+
routeId: "__root__",
|
|
166
|
+
head: [{ tag: "title", children: "Site" }],
|
|
167
|
+
staleTime: 60000
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
routeId: "/posts/$postId",
|
|
171
|
+
head: [{ tag: "title", children: "Alpha" }],
|
|
172
|
+
staleTime: 1e4
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
staleTime: 1e4
|
|
176
|
+
}), {
|
|
177
|
+
status: 200,
|
|
178
|
+
headers: {
|
|
179
|
+
"content-type": "application/json"
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
try {
|
|
184
|
+
const router = createRouter({
|
|
185
|
+
routeTree: createNestedHeadRouteTree(),
|
|
186
|
+
history
|
|
187
|
+
});
|
|
188
|
+
await router.load();
|
|
189
|
+
expect(fetchCalls).toEqual(["/head-api?href=%2Fposts%2Falpha"]);
|
|
190
|
+
expect(router.state.head).toEqual([
|
|
191
|
+
{ tag: "title", children: "Alpha" }
|
|
192
|
+
]);
|
|
193
|
+
} finally {
|
|
194
|
+
globalThis.fetch = originalFetch;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
test("reuses a fresh root server head across navigations without refetching", async () => {
|
|
198
|
+
const history = createMemoryHistory({
|
|
199
|
+
initialEntries: ["/"]
|
|
200
|
+
});
|
|
201
|
+
const fetchCalls = [];
|
|
202
|
+
const originalFetch = globalThis.fetch;
|
|
203
|
+
globalThis.fetch = async (input) => {
|
|
204
|
+
fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
|
|
205
|
+
return new Response(JSON.stringify({
|
|
206
|
+
head: [
|
|
207
|
+
{ tag: "title", children: "Site" }
|
|
208
|
+
],
|
|
209
|
+
routeHeads: [
|
|
210
|
+
{
|
|
211
|
+
routeId: "__root__",
|
|
212
|
+
head: [{ tag: "title", children: "Site" }],
|
|
213
|
+
staleTime: 60000
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
staleTime: 60000
|
|
217
|
+
}), {
|
|
218
|
+
status: 200,
|
|
219
|
+
headers: {
|
|
220
|
+
"content-type": "application/json"
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
try {
|
|
225
|
+
const router = createRouter({
|
|
226
|
+
routeTree: createRootServerHeadTree(),
|
|
227
|
+
history
|
|
228
|
+
});
|
|
229
|
+
await router.load();
|
|
230
|
+
await router.navigate({
|
|
231
|
+
to: "/about"
|
|
232
|
+
});
|
|
233
|
+
expect(fetchCalls).toEqual(["/head-api?href=%2F"]);
|
|
234
|
+
} finally {
|
|
235
|
+
globalThis.fetch = originalFetch;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
test("seeds the route head cache from the dehydrated snapshot", async () => {
|
|
239
|
+
const history = createMemoryHistory({
|
|
240
|
+
initialEntries: ["/about"]
|
|
241
|
+
});
|
|
242
|
+
const fetchCalls = [];
|
|
243
|
+
const originalFetch = globalThis.fetch;
|
|
244
|
+
const globalWithWindow = globalThis;
|
|
245
|
+
const originalWindow = globalWithWindow.window;
|
|
246
|
+
globalThis.fetch = async (input) => {
|
|
247
|
+
fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
|
|
248
|
+
return new Response(JSON.stringify({
|
|
249
|
+
head: [
|
|
250
|
+
{ tag: "title", children: "Site" }
|
|
251
|
+
],
|
|
252
|
+
routeHeads: [
|
|
253
|
+
{
|
|
254
|
+
routeId: "__root__",
|
|
255
|
+
head: [{ tag: "title", children: "Site" }],
|
|
256
|
+
staleTime: 60000
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
staleTime: 60000
|
|
260
|
+
}), {
|
|
261
|
+
status: 200,
|
|
262
|
+
headers: {
|
|
263
|
+
"content-type": "application/json"
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
globalWithWindow.window = {
|
|
268
|
+
__RICHIE_ROUTER_HEAD__: {
|
|
269
|
+
href: "/about",
|
|
270
|
+
head: [
|
|
271
|
+
{ tag: "title", children: "Site" }
|
|
272
|
+
],
|
|
273
|
+
routeHeads: [
|
|
274
|
+
{
|
|
275
|
+
routeId: "__root__",
|
|
276
|
+
head: [{ tag: "title", children: "Site" }],
|
|
277
|
+
staleTime: 60000
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
try {
|
|
283
|
+
const router = createRouter({
|
|
284
|
+
routeTree: createRootServerHeadTree(),
|
|
285
|
+
history
|
|
286
|
+
});
|
|
287
|
+
await router.load();
|
|
288
|
+
expect(fetchCalls).toHaveLength(0);
|
|
289
|
+
expect(router.state.head).toEqual([
|
|
290
|
+
{ tag: "title", children: "Site" }
|
|
291
|
+
]);
|
|
292
|
+
} finally {
|
|
293
|
+
globalThis.fetch = originalFetch;
|
|
294
|
+
if (originalWindow === undefined) {
|
|
295
|
+
Reflect.deleteProperty(globalWithWindow, "window");
|
|
296
|
+
} else {
|
|
297
|
+
originalWindow.__RICHIE_ROUTER_HEAD__ = undefined;
|
|
298
|
+
globalWithWindow.window = originalWindow;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
test("reuses the initial merged head snapshot without fetching when the branch has no inline head", async () => {
|
|
303
|
+
const history = createMemoryHistory({
|
|
304
|
+
initialEntries: ["/about"]
|
|
305
|
+
});
|
|
306
|
+
const fetchCalls = [];
|
|
307
|
+
const originalFetch = globalThis.fetch;
|
|
308
|
+
const globalWithWindow = globalThis;
|
|
309
|
+
const originalWindow = globalWithWindow.window;
|
|
310
|
+
globalThis.fetch = async (input) => {
|
|
311
|
+
fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
|
|
312
|
+
return new Response("{}", {
|
|
313
|
+
status: 500
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
globalWithWindow.window = {
|
|
317
|
+
__RICHIE_ROUTER_HEAD__: {
|
|
318
|
+
href: "/about",
|
|
319
|
+
head: [
|
|
320
|
+
{ tag: "title", children: "About from SSR" }
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
try {
|
|
325
|
+
const router = createRouter({
|
|
326
|
+
routeTree: createTestRouteTree({ serverHead: true }),
|
|
327
|
+
history
|
|
328
|
+
});
|
|
329
|
+
await router.load();
|
|
330
|
+
expect(fetchCalls).toHaveLength(0);
|
|
331
|
+
expect(router.state.head).toEqual([
|
|
332
|
+
{ tag: "title", children: "About from SSR" }
|
|
333
|
+
]);
|
|
334
|
+
} finally {
|
|
335
|
+
globalThis.fetch = originalFetch;
|
|
336
|
+
if (originalWindow === undefined) {
|
|
337
|
+
Reflect.deleteProperty(globalWithWindow, "window");
|
|
338
|
+
} else {
|
|
339
|
+
originalWindow.__RICHIE_ROUTER_HEAD__ = undefined;
|
|
340
|
+
globalWithWindow.window = originalWindow;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
test("uses the merged document head without route fallback requests when the branch has no inline head", async () => {
|
|
345
|
+
const history = createMemoryHistory({
|
|
346
|
+
initialEntries: ["/posts/alpha"]
|
|
347
|
+
});
|
|
348
|
+
const fetchCalls = [];
|
|
349
|
+
const originalFetch = globalThis.fetch;
|
|
350
|
+
globalThis.fetch = async (input) => {
|
|
351
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
352
|
+
fetchCalls.push(url);
|
|
353
|
+
return new Response(JSON.stringify({
|
|
354
|
+
head: [
|
|
355
|
+
{ tag: "meta", name: "description", content: "Nested server head" },
|
|
356
|
+
{ tag: "title", children: "Alpha" }
|
|
357
|
+
],
|
|
358
|
+
staleTime: 1e4
|
|
359
|
+
}), {
|
|
360
|
+
status: 200,
|
|
361
|
+
headers: {
|
|
362
|
+
"content-type": "application/json"
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
try {
|
|
367
|
+
const router = createRouter({
|
|
368
|
+
routeTree: createNestedServerHeadTree(),
|
|
369
|
+
history
|
|
370
|
+
});
|
|
371
|
+
await router.load();
|
|
372
|
+
expect(fetchCalls).toEqual(["/head-api?href=%2Fposts%2Falpha"]);
|
|
373
|
+
expect(router.state.head).toEqual([
|
|
374
|
+
{ tag: "meta", name: "description", content: "Nested server head" },
|
|
375
|
+
{ tag: "title", children: "Alpha" }
|
|
376
|
+
]);
|
|
377
|
+
} finally {
|
|
378
|
+
globalThis.fetch = originalFetch;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
test("keeps loadRouteHead as the route-scoped override path", async () => {
|
|
382
|
+
const history = createMemoryHistory({
|
|
383
|
+
initialEntries: ["/about"]
|
|
384
|
+
});
|
|
385
|
+
const fetchCalls = [];
|
|
386
|
+
const loadRouteHeadCalls = [];
|
|
387
|
+
const originalFetch = globalThis.fetch;
|
|
388
|
+
globalThis.fetch = async (input) => {
|
|
389
|
+
fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
|
|
390
|
+
return new Response("{}", {
|
|
391
|
+
status: 500
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
try {
|
|
395
|
+
const router = createRouter({
|
|
396
|
+
routeTree: createTestRouteTree({ serverHead: true }),
|
|
397
|
+
history,
|
|
398
|
+
loadRouteHead: async ({ routeId }) => {
|
|
399
|
+
loadRouteHeadCalls.push(routeId);
|
|
400
|
+
return {
|
|
401
|
+
head: [
|
|
402
|
+
{ tag: "title", children: "About override" }
|
|
403
|
+
],
|
|
404
|
+
staleTime: 1000
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
await router.load();
|
|
409
|
+
expect(loadRouteHeadCalls).toEqual(["/about"]);
|
|
410
|
+
expect(fetchCalls).toHaveLength(0);
|
|
411
|
+
expect(router.state.head).toEqual([
|
|
412
|
+
{ tag: "title", children: "About override" }
|
|
413
|
+
]);
|
|
414
|
+
} finally {
|
|
415
|
+
globalThis.fetch = originalFetch;
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
});
|
package/dist/types/router.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { RouteNode, isNotFound, isRedirect, notFound, redirect } from '@richie-router/core';
|
|
3
|
-
import type { AnyComponent, AnyRoute, DehydratedHeadState, HeadConfig, NormalizeRouteId, ParsedLocation, ResolveAllParams, RouteMatch, RouteOptions as CoreRouteOptions
|
|
3
|
+
import type { AnyComponent, AnyRoute, DehydratedHeadState, HeadConfig, NormalizeRouteId, ParsedLocation, ResolveAllParams, RouteMatch, RouteOptions as CoreRouteOptions } from '@richie-router/core';
|
|
4
4
|
import { createBrowserHistory, createHashHistory, createMemoryHistory } from './history';
|
|
5
5
|
import type { MemoryHistoryOptions, RouterHistory } from './history';
|
|
6
6
|
declare global {
|
|
@@ -10,12 +10,9 @@ declare global {
|
|
|
10
10
|
}
|
|
11
11
|
export interface Register {
|
|
12
12
|
}
|
|
13
|
-
type
|
|
14
|
-
|
|
15
|
-
} ?
|
|
16
|
-
type RegisteredHeadTagSearchSchema = Register extends {
|
|
17
|
-
headTagSearchSchema: infer THeadTagSearchSchema;
|
|
18
|
-
} ? THeadTagSearchSchema : Record<string, {}>;
|
|
13
|
+
type RegisteredRouteSearchSchema = Register extends {
|
|
14
|
+
routeSearchSchema: infer TRouteSearchSchema;
|
|
15
|
+
} ? TRouteSearchSchema : Record<string, {}>;
|
|
19
16
|
type RegisteredRouteTree = Register extends {
|
|
20
17
|
routeTree: infer TRouteTree;
|
|
21
18
|
} ? TRouteTree : AnyRoute;
|
|
@@ -65,8 +62,7 @@ type MatchOfRoute<TRoute> = {
|
|
|
65
62
|
type SafeRouteByTo<TTo extends string> = [RouteById<TTo>] extends [never] ? AnyRoute : RouteById<TTo>;
|
|
66
63
|
type ParamsForTo<TTo extends string> = ParamsOfRoute<SafeRouteByTo<TTo>>;
|
|
67
64
|
type SearchForTo<TTo extends string> = SearchOfRoute<SafeRouteByTo<TTo>>;
|
|
68
|
-
type
|
|
69
|
-
type HeadTagNameForFullPath<TPath extends string> = TPath extends keyof RegisteredHeadTagMap ? RegisteredHeadTagMap[TPath] : string;
|
|
65
|
+
type SearchForRouteId<TPath extends string> = TPath extends keyof RegisteredRouteSearchSchema ? RegisteredRouteSearchSchema[TPath] : {};
|
|
70
66
|
type ParamsInput<TParams> = TParams | ((previous: TParams) => TParams);
|
|
71
67
|
type SearchInput<TSearch> = TSearch | ((previous: TSearch) => TSearch) | true;
|
|
72
68
|
type ParamsOption<TParams> = keyof TParams extends never ? {
|
|
@@ -79,11 +75,12 @@ type ClientHeadOption<TPath extends string, TSearch> = HeadConfig | ((ctx: {
|
|
|
79
75
|
search: TSearch;
|
|
80
76
|
matches: RouteMatch[];
|
|
81
77
|
}) => HeadConfig);
|
|
82
|
-
type RouteHeadOption<TPath extends string, TSearch> = ClientHeadOption<TPath, TSearch> | (HeadTagNameForFullPath<TPath> extends never ? never : HeadTagNameForFullPath<TPath>);
|
|
83
78
|
type RouteOptionsInput<TPath extends string, TSearch> = Omit<CoreRouteOptions<TPath, TSearch>, 'head'> & {
|
|
84
|
-
head?:
|
|
79
|
+
head?: ClientHeadOption<TPath, TSearch>;
|
|
80
|
+
};
|
|
81
|
+
type FileRouteInstance<TPath extends string, TSearch, TFileTypes = unknown, THasInlineHead extends boolean = boolean> = RouteNode<TPath, NormalizeRouteId<TPath>, ResolveAllParams<TPath>, TSearch, TFileTypes> & RouteApiMethods<RouteNode<TPath, NormalizeRouteId<TPath>, ResolveAllParams<TPath>, TSearch, TFileTypes>> & {
|
|
82
|
+
__hasInlineHead: THasInlineHead;
|
|
85
83
|
};
|
|
86
|
-
type FileRouteInstance<TPath extends string, TSearch, TFileTypes = unknown> = RouteNode<TPath, NormalizeRouteId<TPath>, ResolveAllParams<TPath>, TSearch, TFileTypes> & RouteApiMethods<RouteNode<TPath, NormalizeRouteId<TPath>, ResolveAllParams<TPath>, TSearch, TFileTypes>>;
|
|
87
84
|
export interface TypedRouteMatch<TPath extends string, TSearch, TRoute extends AnyRoute = AnyRoute> extends Omit<RouteMatch<TRoute>, 'params' | 'search'> {
|
|
88
85
|
params: ResolveAllParams<TPath>;
|
|
89
86
|
search: TSearch;
|
|
@@ -128,6 +125,7 @@ export interface RouterState {
|
|
|
128
125
|
export interface RouterOptions<TRouteTree extends AnyRoute> {
|
|
129
126
|
routeTree: TRouteTree;
|
|
130
127
|
history?: RouterHistory;
|
|
128
|
+
basePath?: string;
|
|
131
129
|
defaultPreload?: 'intent' | 'render' | false;
|
|
132
130
|
defaultPreloadDelay?: number;
|
|
133
131
|
defaultPendingMs?: number;
|
|
@@ -142,7 +140,7 @@ export interface RouterOptions<TRouteTree extends AnyRoute> {
|
|
|
142
140
|
stringifySearch?: (search: Record<string, unknown>) => string;
|
|
143
141
|
loadRouteHead?: (ctx: {
|
|
144
142
|
route: AnyRoute;
|
|
145
|
-
|
|
143
|
+
routeId: string;
|
|
146
144
|
params: Record<string, string>;
|
|
147
145
|
search: unknown;
|
|
148
146
|
location: ParsedLocation;
|
|
@@ -156,12 +154,12 @@ type InternalRouteMatch = RouteMatch & {
|
|
|
156
154
|
id: string;
|
|
157
155
|
};
|
|
158
156
|
type Selector<TSelection> = (state: RouterState) => TSelection;
|
|
159
|
-
export declare function createFileRoute<TPath extends string>(path: TPath): <
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
export declare function createRootRoute<
|
|
163
|
-
|
|
164
|
-
}
|
|
157
|
+
export declare function createFileRoute<TPath extends string>(path: TPath): <TOptions extends RouteOptionsInput<TPath, SearchForRouteId<TPath>>>(options: TOptions) => FileRouteInstance<TPath, SearchForRouteId<TPath>, unknown, TOptions extends {
|
|
158
|
+
head: any;
|
|
159
|
+
} ? true : false>;
|
|
160
|
+
export declare function createRootRoute<TOptions extends RouteOptionsInput<'__root__', SearchForRouteId<'__root__'>>>(options: TOptions): FileRouteInstance<'__root__', SearchForRouteId<'__root__'>, unknown, TOptions extends {
|
|
161
|
+
head: any;
|
|
162
|
+
} ? true : false>;
|
|
165
163
|
export declare class Router<TRouteTree extends AnyRoute> {
|
|
166
164
|
readonly routeTree: TRouteTree;
|
|
167
165
|
readonly history: RouterHistory;
|
|
@@ -173,6 +171,8 @@ export declare class Router<TRouteTree extends AnyRoute> {
|
|
|
173
171
|
private readonly headCache;
|
|
174
172
|
private readonly parseSearch;
|
|
175
173
|
private readonly stringifySearch;
|
|
174
|
+
private readonly basePath;
|
|
175
|
+
private initialHeadSnapshot?;
|
|
176
176
|
private started;
|
|
177
177
|
private unsubscribeHistory?;
|
|
178
178
|
constructor(options: RouterOptions<TRouteTree>);
|
|
@@ -187,6 +187,8 @@ export declare class Router<TRouteTree extends AnyRoute> {
|
|
|
187
187
|
preloadRoute<TTo extends RoutePaths>(options: NavigateOptions<TTo>): Promise<void>;
|
|
188
188
|
invalidate(): Promise<void>;
|
|
189
189
|
buildHref<TTo extends RoutePaths>(options: NavigateOptions<TTo>): string;
|
|
190
|
+
private buildLocation;
|
|
191
|
+
private buildLocationHref;
|
|
190
192
|
private readLocation;
|
|
191
193
|
private applyTrailingSlash;
|
|
192
194
|
private notify;
|
|
@@ -195,14 +197,21 @@ export declare class Router<TRouteTree extends AnyRoute> {
|
|
|
195
197
|
private resolveSearch;
|
|
196
198
|
resolveLocation(location: ParsedLocation, options?: {
|
|
197
199
|
request?: Request;
|
|
200
|
+
initialHeadSnapshot?: DehydratedHeadState;
|
|
198
201
|
}): Promise<{
|
|
199
202
|
matches: InternalRouteMatch[];
|
|
200
203
|
head: HeadConfig;
|
|
201
204
|
error: unknown;
|
|
202
205
|
}>;
|
|
203
206
|
private resolveLocationHead;
|
|
207
|
+
private getRouteHeadCacheKey;
|
|
208
|
+
private getCachedRouteHead;
|
|
209
|
+
private setRouteHeadCache;
|
|
210
|
+
private seedHeadCacheFromRouteHeads;
|
|
211
|
+
private cacheRouteHeadsFromDocument;
|
|
204
212
|
private loadRouteHead;
|
|
205
213
|
private fetchRouteHead;
|
|
214
|
+
private fetchDocumentHead;
|
|
206
215
|
private commitLocation;
|
|
207
216
|
private restoreScroll;
|
|
208
217
|
private handleHistoryChange;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@richie-router/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "React runtime, components, and hooks for Richie Router",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@richie-router/core": "^0.1.
|
|
16
|
+
"@richie-router/core": "^0.1.3"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"react": "^19"
|