@richie-router/react 0.1.2 → 0.1.4
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 +352 -107
- package/dist/cjs/router.test.cjs +574 -0
- package/dist/esm/router.mjs +353 -107
- package/dist/esm/router.test.mjs +552 -0
- package/dist/types/router.d.ts +31 -2
- package/package.json +2 -2
package/dist/cjs/router.cjs
CHANGED
|
@@ -69,6 +69,7 @@ __export(exports_router, {
|
|
|
69
69
|
useParams: () => useParams,
|
|
70
70
|
useNavigate: () => useNavigate,
|
|
71
71
|
useMatches: () => useMatches,
|
|
72
|
+
useMatchRoute: () => useMatchRoute,
|
|
72
73
|
useMatch: () => useMatch,
|
|
73
74
|
useLocation: () => useLocation,
|
|
74
75
|
useElementScrollRestoration: () => useElementScrollRestoration,
|
|
@@ -97,16 +98,89 @@ module.exports = __toCommonJS(exports_router);
|
|
|
97
98
|
var import_react = __toESM(require("react"));
|
|
98
99
|
var import_core = require("@richie-router/core");
|
|
99
100
|
var import_history = require("./history.cjs");
|
|
100
|
-
var
|
|
101
|
+
var jsx_runtime = require("react/jsx-runtime");
|
|
101
102
|
var RouterContext = import_react.default.createContext(null);
|
|
102
103
|
var RouterStateContext = import_react.default.createContext(null);
|
|
103
104
|
var OutletContext = import_react.default.createContext(null);
|
|
104
105
|
var MatchContext = import_react.default.createContext(null);
|
|
105
106
|
var MANAGED_HEAD_ATTRIBUTE = "data-richie-router-head";
|
|
106
|
-
var EMPTY_HEAD =
|
|
107
|
+
var EMPTY_HEAD = [];
|
|
108
|
+
function ensureLeadingSlash(value) {
|
|
109
|
+
return value.startsWith("/") ? value : `/${value}`;
|
|
110
|
+
}
|
|
111
|
+
function normalizeBasePath(basePath) {
|
|
112
|
+
if (!basePath) {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
const trimmed = basePath.trim();
|
|
116
|
+
if (trimmed === "" || trimmed === "/") {
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
119
|
+
const normalized = ensureLeadingSlash(trimmed).replace(/\/+$/u, "");
|
|
120
|
+
return normalized === "/" ? "" : normalized;
|
|
121
|
+
}
|
|
122
|
+
function parseHref(href) {
|
|
123
|
+
if (href.startsWith("http://") || href.startsWith("https://")) {
|
|
124
|
+
return new URL(href);
|
|
125
|
+
}
|
|
126
|
+
return new URL(ensureLeadingSlash(href), "http://richie-router.local");
|
|
127
|
+
}
|
|
128
|
+
function stripBasePathFromPathname(pathname, basePath) {
|
|
129
|
+
if (!basePath) {
|
|
130
|
+
return pathname;
|
|
131
|
+
}
|
|
132
|
+
if (pathname === basePath) {
|
|
133
|
+
return "/";
|
|
134
|
+
}
|
|
135
|
+
return pathname.startsWith(`${basePath}/`) ? pathname.slice(basePath.length) || "/" : pathname;
|
|
136
|
+
}
|
|
137
|
+
function stripBasePathFromHref(href, basePath) {
|
|
138
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
139
|
+
if (!normalizedBasePath) {
|
|
140
|
+
return href;
|
|
141
|
+
}
|
|
142
|
+
const url = parseHref(href);
|
|
143
|
+
return `${stripBasePathFromPathname(url.pathname, normalizedBasePath)}${url.search}${url.hash}`;
|
|
144
|
+
}
|
|
145
|
+
function prependBasePathToPathname(pathname, basePath) {
|
|
146
|
+
if (!basePath) {
|
|
147
|
+
return pathname;
|
|
148
|
+
}
|
|
149
|
+
return pathname === "/" ? basePath : `${basePath}${ensureLeadingSlash(pathname)}`;
|
|
150
|
+
}
|
|
151
|
+
function prependBasePathToHref(href, basePath) {
|
|
152
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
153
|
+
if (!normalizedBasePath) {
|
|
154
|
+
return href;
|
|
155
|
+
}
|
|
156
|
+
const url = parseHref(href);
|
|
157
|
+
return `${prependBasePathToPathname(url.pathname, normalizedBasePath)}${url.search}${url.hash}`;
|
|
158
|
+
}
|
|
107
159
|
function routeHasRecord(value) {
|
|
108
160
|
return typeof value === "object" && value !== null;
|
|
109
161
|
}
|
|
162
|
+
function isDeepInclusiveMatch(expected, actual) {
|
|
163
|
+
if (Array.isArray(expected)) {
|
|
164
|
+
if (!Array.isArray(actual) || expected.length !== actual.length) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return expected.every((value, index) => isDeepInclusiveMatch(value, actual[index]));
|
|
168
|
+
}
|
|
169
|
+
if (routeHasRecord(expected)) {
|
|
170
|
+
if (!routeHasRecord(actual)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return Object.entries(expected).every(([key, value]) => (key in actual) && isDeepInclusiveMatch(value, actual[key]));
|
|
174
|
+
}
|
|
175
|
+
return Object.is(expected, actual);
|
|
176
|
+
}
|
|
177
|
+
function routeHasInlineHead(route) {
|
|
178
|
+
const headOption = route.options.head;
|
|
179
|
+
return Boolean(headOption && typeof headOption !== "string");
|
|
180
|
+
}
|
|
181
|
+
function matchesHaveInlineHead(matches) {
|
|
182
|
+
return matches.some((match) => routeHasInlineHead(match.route));
|
|
183
|
+
}
|
|
110
184
|
function resolveParamsInput(input, previous) {
|
|
111
185
|
if (input === undefined) {
|
|
112
186
|
return previous;
|
|
@@ -152,6 +226,8 @@ class Router {
|
|
|
152
226
|
headCache = new Map;
|
|
153
227
|
parseSearch;
|
|
154
228
|
stringifySearch;
|
|
229
|
+
basePath;
|
|
230
|
+
initialHeadSnapshot;
|
|
155
231
|
started = false;
|
|
156
232
|
unsubscribeHistory;
|
|
157
233
|
constructor(options) {
|
|
@@ -160,6 +236,7 @@ class Router {
|
|
|
160
236
|
this.history = options.history ?? (typeof window === "undefined" ? import_history.createMemoryHistory() : import_history.createBrowserHistory());
|
|
161
237
|
this.parseSearch = options.parseSearch ?? import_core.defaultParseSearch;
|
|
162
238
|
this.stringifySearch = options.stringifySearch ?? import_core.defaultStringifySearch;
|
|
239
|
+
this.basePath = normalizeBasePath(options.basePath);
|
|
163
240
|
for (const route of import_core.collectRoutes(this.routeTree)) {
|
|
164
241
|
this.routesByFullPath.set(route.fullPath, route);
|
|
165
242
|
}
|
|
@@ -167,15 +244,22 @@ class Router {
|
|
|
167
244
|
this.routesByTo.set(branch.leaf.to, branch.leaf);
|
|
168
245
|
}
|
|
169
246
|
const location = this.readLocation();
|
|
247
|
+
const initialMatches = this.buildMatches(location);
|
|
248
|
+
const rawHistoryHref = this.history.location.href;
|
|
170
249
|
const initialHeadSnapshot = typeof window !== "undefined" ? window.__RICHIE_ROUTER_HEAD__ : undefined;
|
|
171
|
-
const
|
|
250
|
+
const hasMatchingInitialHeadSnapshot = Boolean(initialHeadSnapshot && (initialHeadSnapshot.href === location.href || initialHeadSnapshot.href === rawHistoryHref));
|
|
251
|
+
const initialHead = hasMatchingInitialHeadSnapshot && initialHeadSnapshot ? initialHeadSnapshot.head : EMPTY_HEAD;
|
|
252
|
+
if (hasMatchingInitialHeadSnapshot && this.options.loadRouteHead === undefined && initialHeadSnapshot?.routeHeads !== undefined) {
|
|
253
|
+
this.seedHeadCacheFromRouteHeads(initialMatches, initialHeadSnapshot.routeHeads);
|
|
254
|
+
}
|
|
172
255
|
if (typeof window !== "undefined" && initialHeadSnapshot !== undefined) {
|
|
173
256
|
delete window.__RICHIE_ROUTER_HEAD__;
|
|
174
257
|
}
|
|
258
|
+
this.initialHeadSnapshot = hasMatchingInitialHeadSnapshot ? initialHeadSnapshot : undefined;
|
|
175
259
|
this.state = {
|
|
176
260
|
status: "loading",
|
|
177
261
|
location,
|
|
178
|
-
matches:
|
|
262
|
+
matches: initialMatches,
|
|
179
263
|
head: initialHead,
|
|
180
264
|
error: null
|
|
181
265
|
};
|
|
@@ -206,15 +290,17 @@ class Router {
|
|
|
206
290
|
}
|
|
207
291
|
async load(options) {
|
|
208
292
|
const nextLocation = this.readLocation();
|
|
293
|
+
const initialHeadSnapshot = this.initialHeadSnapshot?.href === nextLocation.href ? this.initialHeadSnapshot : undefined;
|
|
294
|
+
this.initialHeadSnapshot = undefined;
|
|
209
295
|
await this.commitLocation(nextLocation, {
|
|
210
296
|
request: options?.request,
|
|
211
297
|
replace: true,
|
|
212
|
-
writeHistory: false
|
|
298
|
+
writeHistory: false,
|
|
299
|
+
initialHeadSnapshot
|
|
213
300
|
});
|
|
214
301
|
}
|
|
215
302
|
async navigate(options) {
|
|
216
|
-
const
|
|
217
|
-
const location = import_core.createParsedLocation(href, options.state ?? null, this.parseSearch);
|
|
303
|
+
const location = this.buildLocation(options);
|
|
218
304
|
await this.commitLocation(location, {
|
|
219
305
|
replace: options.replace ?? false,
|
|
220
306
|
writeHistory: true,
|
|
@@ -222,8 +308,7 @@ class Router {
|
|
|
222
308
|
});
|
|
223
309
|
}
|
|
224
310
|
async preloadRoute(options) {
|
|
225
|
-
const
|
|
226
|
-
const location = import_core.createParsedLocation(href, options.state ?? null, this.parseSearch);
|
|
311
|
+
const location = this.buildLocation(options);
|
|
227
312
|
try {
|
|
228
313
|
await this.resolveLocation(location);
|
|
229
314
|
} catch {}
|
|
@@ -233,6 +318,12 @@ class Router {
|
|
|
233
318
|
await this.load();
|
|
234
319
|
}
|
|
235
320
|
buildHref(options) {
|
|
321
|
+
return prependBasePathToHref(this.buildLocationHref(options), this.basePath);
|
|
322
|
+
}
|
|
323
|
+
buildLocation(options) {
|
|
324
|
+
return import_core.createParsedLocation(this.buildLocationHref(options), options.state ?? null, this.parseSearch);
|
|
325
|
+
}
|
|
326
|
+
buildLocationHref(options) {
|
|
236
327
|
const targetRoute = this.routesByTo.get(options.to) ?? null;
|
|
237
328
|
const fromMatch = options.from ? this.findMatchByTo(options.from) : null;
|
|
238
329
|
const previousParams = fromMatch?.params ?? {};
|
|
@@ -247,7 +338,7 @@ class Router {
|
|
|
247
338
|
}
|
|
248
339
|
readLocation() {
|
|
249
340
|
const location = this.history.location;
|
|
250
|
-
return import_core.createParsedLocation(location.href, location.state, this.parseSearch);
|
|
341
|
+
return import_core.createParsedLocation(stripBasePathFromHref(location.href, this.basePath), location.state, this.parseSearch);
|
|
251
342
|
}
|
|
252
343
|
applyTrailingSlash(pathname, route) {
|
|
253
344
|
const trailingSlash = this.options.trailingSlash ?? "preserve";
|
|
@@ -342,29 +433,102 @@ class Router {
|
|
|
342
433
|
to: route.to
|
|
343
434
|
});
|
|
344
435
|
}
|
|
345
|
-
const head = await this.resolveLocationHead(matches, location, options?.request);
|
|
436
|
+
const head = await this.resolveLocationHead(matches, location, options?.request, options?.initialHeadSnapshot);
|
|
346
437
|
return { matches, head, error: null };
|
|
347
438
|
}
|
|
348
|
-
async resolveLocationHead(matches, location, request) {
|
|
439
|
+
async resolveLocationHead(matches, location, request, initialHeadSnapshot) {
|
|
349
440
|
const resolvedHeadByRoute = new Map;
|
|
350
|
-
|
|
351
|
-
|
|
441
|
+
const serverMatches = matches.filter((match) => match.route.serverHead);
|
|
442
|
+
if (serverMatches.length === 0) {
|
|
443
|
+
return import_core.resolveHeadConfig(matches, resolvedHeadByRoute);
|
|
444
|
+
}
|
|
445
|
+
if (this.options.loadRouteHead !== undefined) {
|
|
446
|
+
for (const match of serverMatches) {
|
|
447
|
+
resolvedHeadByRoute.set(match.route.fullPath, await this.loadRouteHead(match.route, match.params, match.search, location, request));
|
|
448
|
+
}
|
|
449
|
+
return import_core.resolveHeadConfig(matches, resolvedHeadByRoute);
|
|
450
|
+
}
|
|
451
|
+
if (initialHeadSnapshot?.href === location.href && initialHeadSnapshot.routeHeads === undefined && !matchesHaveInlineHead(matches)) {
|
|
452
|
+
return initialHeadSnapshot.head;
|
|
453
|
+
}
|
|
454
|
+
let needsDocumentHeadFetch = false;
|
|
455
|
+
for (const match of serverMatches) {
|
|
456
|
+
const cachedHead = this.getCachedRouteHead(match.route.fullPath, match.params, match.search);
|
|
457
|
+
if (cachedHead) {
|
|
458
|
+
resolvedHeadByRoute.set(match.route.fullPath, cachedHead);
|
|
352
459
|
continue;
|
|
353
460
|
}
|
|
354
|
-
|
|
461
|
+
needsDocumentHeadFetch = true;
|
|
462
|
+
}
|
|
463
|
+
if (!needsDocumentHeadFetch) {
|
|
464
|
+
return import_core.resolveHeadConfig(matches, resolvedHeadByRoute);
|
|
465
|
+
}
|
|
466
|
+
const documentHead = await this.fetchDocumentHead(location);
|
|
467
|
+
if ((documentHead.routeHeads?.length ?? 0) === 0 && !matchesHaveInlineHead(matches)) {
|
|
468
|
+
return documentHead.head;
|
|
469
|
+
}
|
|
470
|
+
const routeHeadsById = this.cacheRouteHeadsFromDocument(matches, documentHead.routeHeads ?? []);
|
|
471
|
+
for (const match of serverMatches) {
|
|
472
|
+
const responseHead = routeHeadsById.get(match.route.fullPath);
|
|
473
|
+
if (responseHead) {
|
|
474
|
+
resolvedHeadByRoute.set(match.route.fullPath, responseHead);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const cachedHead = this.getCachedRouteHead(match.route.fullPath, match.params, match.search);
|
|
478
|
+
if (cachedHead) {
|
|
479
|
+
resolvedHeadByRoute.set(match.route.fullPath, cachedHead);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const response = await this.fetchRouteHead(match.route, match.params, match.search);
|
|
483
|
+
this.setRouteHeadCache(match.route.fullPath, match.params, match.search, response);
|
|
484
|
+
resolvedHeadByRoute.set(match.route.fullPath, response.head);
|
|
355
485
|
}
|
|
356
486
|
return import_core.resolveHeadConfig(matches, resolvedHeadByRoute);
|
|
357
487
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
routeId
|
|
488
|
+
getRouteHeadCacheKey(routeId, params, search) {
|
|
489
|
+
return JSON.stringify({
|
|
490
|
+
routeId,
|
|
361
491
|
params,
|
|
362
492
|
search
|
|
363
493
|
});
|
|
364
|
-
|
|
494
|
+
}
|
|
495
|
+
getCachedRouteHead(routeId, params, search) {
|
|
496
|
+
const cached = this.headCache.get(this.getRouteHeadCacheKey(routeId, params, search));
|
|
365
497
|
if (cached && cached.expiresAt > Date.now()) {
|
|
366
498
|
return cached.head;
|
|
367
499
|
}
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
setRouteHeadCache(routeId, params, search, response) {
|
|
503
|
+
this.headCache.set(this.getRouteHeadCacheKey(routeId, params, search), {
|
|
504
|
+
head: response.head,
|
|
505
|
+
expiresAt: Date.now() + (response.staleTime ?? 0)
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
seedHeadCacheFromRouteHeads(matches, routeHeads) {
|
|
509
|
+
this.cacheRouteHeadsFromDocument(matches, routeHeads);
|
|
510
|
+
}
|
|
511
|
+
cacheRouteHeadsFromDocument(matches, routeHeads) {
|
|
512
|
+
const routeHeadsById = new Map(routeHeads.map((entry) => [entry.routeId, entry]));
|
|
513
|
+
const resolvedHeadByRoute = new Map;
|
|
514
|
+
for (const match of matches) {
|
|
515
|
+
if (!match.route.serverHead) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const entry = routeHeadsById.get(match.route.fullPath);
|
|
519
|
+
if (!entry) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
this.setRouteHeadCache(match.route.fullPath, match.params, match.search, entry);
|
|
523
|
+
resolvedHeadByRoute.set(match.route.fullPath, entry.head);
|
|
524
|
+
}
|
|
525
|
+
return resolvedHeadByRoute;
|
|
526
|
+
}
|
|
527
|
+
async loadRouteHead(route, params, search, location, request) {
|
|
528
|
+
const cachedHead = this.getCachedRouteHead(route.fullPath, params, search);
|
|
529
|
+
if (cachedHead) {
|
|
530
|
+
return cachedHead;
|
|
531
|
+
}
|
|
368
532
|
const response = this.options.loadRouteHead !== undefined ? await this.options.loadRouteHead({
|
|
369
533
|
route,
|
|
370
534
|
routeId: route.fullPath,
|
|
@@ -373,14 +537,11 @@ class Router {
|
|
|
373
537
|
location,
|
|
374
538
|
request
|
|
375
539
|
}) : await this.fetchRouteHead(route, params, search);
|
|
376
|
-
this.
|
|
377
|
-
head: response.head,
|
|
378
|
-
expiresAt: Date.now() + (response.staleTime ?? 0)
|
|
379
|
-
});
|
|
540
|
+
this.setRouteHeadCache(route.fullPath, params, search, response);
|
|
380
541
|
return response.head;
|
|
381
542
|
}
|
|
382
543
|
async fetchRouteHead(route, params, search) {
|
|
383
|
-
const basePath = this.options.headBasePath ?? "/head-api";
|
|
544
|
+
const basePath = this.options.headBasePath ?? prependBasePathToHref("/head-api", this.basePath);
|
|
384
545
|
const searchParams = new URLSearchParams({
|
|
385
546
|
routeId: route.fullPath,
|
|
386
547
|
params: JSON.stringify(params),
|
|
@@ -395,6 +556,20 @@ class Router {
|
|
|
395
556
|
}
|
|
396
557
|
return await response.json();
|
|
397
558
|
}
|
|
559
|
+
async fetchDocumentHead(location) {
|
|
560
|
+
const basePath = this.options.headBasePath ?? prependBasePathToHref("/head-api", this.basePath);
|
|
561
|
+
const searchParams = new URLSearchParams({
|
|
562
|
+
href: prependBasePathToHref(location.href, this.basePath)
|
|
563
|
+
});
|
|
564
|
+
const response = await fetch(`${basePath}?${searchParams.toString()}`);
|
|
565
|
+
if (!response.ok) {
|
|
566
|
+
if (response.status === 404) {
|
|
567
|
+
throw import_core.notFound();
|
|
568
|
+
}
|
|
569
|
+
throw new Error(`Failed to resolve server head for location "${location.href}"`);
|
|
570
|
+
}
|
|
571
|
+
return await response.json();
|
|
572
|
+
}
|
|
398
573
|
async commitLocation(location, options) {
|
|
399
574
|
this.state = {
|
|
400
575
|
...this.state,
|
|
@@ -404,13 +579,15 @@ class Router {
|
|
|
404
579
|
this.notify();
|
|
405
580
|
try {
|
|
406
581
|
const resolved = await this.resolveLocation(location, {
|
|
407
|
-
request: options.request
|
|
582
|
+
request: options.request,
|
|
583
|
+
initialHeadSnapshot: options.initialHeadSnapshot
|
|
408
584
|
});
|
|
585
|
+
const historyHref = prependBasePathToHref(location.href, this.basePath);
|
|
409
586
|
if (options.writeHistory) {
|
|
410
587
|
if (options.replace) {
|
|
411
|
-
this.history.replace(
|
|
588
|
+
this.history.replace(historyHref, location.state);
|
|
412
589
|
} else {
|
|
413
|
-
this.history.push(
|
|
590
|
+
this.history.push(historyHref, location.state);
|
|
414
591
|
}
|
|
415
592
|
}
|
|
416
593
|
this.state = {
|
|
@@ -431,11 +608,12 @@ class Router {
|
|
|
431
608
|
return;
|
|
432
609
|
}
|
|
433
610
|
const errorMatches = this.buildMatches(location);
|
|
611
|
+
const historyHref = prependBasePathToHref(location.href, this.basePath);
|
|
434
612
|
if (options.writeHistory) {
|
|
435
613
|
if (options.replace) {
|
|
436
|
-
this.history.replace(
|
|
614
|
+
this.history.replace(historyHref, location.state);
|
|
437
615
|
} else {
|
|
438
|
-
this.history.push(
|
|
616
|
+
this.history.push(historyHref, location.state);
|
|
439
617
|
}
|
|
440
618
|
}
|
|
441
619
|
this.state = {
|
|
@@ -525,61 +703,95 @@ function createManagedHeadElements(head) {
|
|
|
525
703
|
element.setAttribute(MANAGED_HEAD_ATTRIBUTE, "true");
|
|
526
704
|
return element;
|
|
527
705
|
};
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
706
|
+
const setAttributes = (element, attributes) => {
|
|
707
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
708
|
+
if (value === undefined || value === false) {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (value === true) {
|
|
712
|
+
element.setAttribute(key, "");
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
element.setAttribute(key, value);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
for (const element of head) {
|
|
719
|
+
if (element.tag === "title") {
|
|
533
720
|
continue;
|
|
534
721
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
722
|
+
if (element.tag === "meta") {
|
|
723
|
+
const tag2 = managed(document.createElement("meta"));
|
|
724
|
+
if ("charset" in element) {
|
|
725
|
+
tag2.setAttribute("charset", element.charset);
|
|
726
|
+
} else if ("name" in element) {
|
|
727
|
+
tag2.setAttribute("name", element.name);
|
|
728
|
+
tag2.setAttribute("content", element.content);
|
|
729
|
+
} else if ("property" in element) {
|
|
730
|
+
tag2.setAttribute("property", element.property);
|
|
731
|
+
tag2.setAttribute("content", element.content);
|
|
732
|
+
} else {
|
|
733
|
+
tag2.setAttribute("http-equiv", element.httpEquiv);
|
|
734
|
+
tag2.setAttribute("content", element.content);
|
|
735
|
+
}
|
|
736
|
+
elements.push(tag2);
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (element.tag === "link") {
|
|
740
|
+
const tag2 = managed(document.createElement("link"));
|
|
741
|
+
setAttributes(tag2, {
|
|
742
|
+
rel: element.rel,
|
|
743
|
+
href: element.href,
|
|
744
|
+
type: element.type,
|
|
745
|
+
media: element.media,
|
|
746
|
+
sizes: element.sizes,
|
|
747
|
+
crossorigin: element.crossorigin
|
|
748
|
+
});
|
|
749
|
+
elements.push(tag2);
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
if (element.tag === "style") {
|
|
753
|
+
const tag2 = managed(document.createElement("style"));
|
|
754
|
+
if (element.media) {
|
|
755
|
+
tag2.setAttribute("media", element.media);
|
|
756
|
+
}
|
|
757
|
+
tag2.textContent = element.children;
|
|
758
|
+
elements.push(tag2);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
if (element.tag === "script") {
|
|
762
|
+
const tag2 = managed(document.createElement("script"));
|
|
763
|
+
if (element.src) {
|
|
764
|
+
tag2.setAttribute("src", element.src);
|
|
765
|
+
}
|
|
766
|
+
if (element.type) {
|
|
767
|
+
tag2.setAttribute("type", element.type);
|
|
768
|
+
}
|
|
769
|
+
if (element.async) {
|
|
770
|
+
tag2.async = true;
|
|
771
|
+
}
|
|
772
|
+
if (element.defer) {
|
|
773
|
+
tag2.defer = true;
|
|
774
|
+
}
|
|
775
|
+
if (element.children) {
|
|
776
|
+
tag2.textContent = element.children;
|
|
777
|
+
}
|
|
778
|
+
elements.push(tag2);
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (element.tag === "base") {
|
|
782
|
+
const tag2 = managed(document.createElement("base"));
|
|
783
|
+
tag2.setAttribute("href", element.href);
|
|
784
|
+
if (element.target) {
|
|
785
|
+
tag2.setAttribute("target", element.target);
|
|
786
|
+
}
|
|
787
|
+
elements.push(tag2);
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
const tag = managed(document.createElement(element.name));
|
|
791
|
+
setAttributes(tag, element.attrs ?? {});
|
|
792
|
+
if (element.children) {
|
|
793
|
+
tag.textContent = element.children;
|
|
547
794
|
}
|
|
548
|
-
elements.push(tag);
|
|
549
|
-
}
|
|
550
|
-
for (const link of head.links ?? []) {
|
|
551
|
-
const tag = managed(document.createElement("link"));
|
|
552
|
-
tag.setAttribute("rel", link.rel);
|
|
553
|
-
tag.setAttribute("href", link.href);
|
|
554
|
-
if (link.type)
|
|
555
|
-
tag.setAttribute("type", link.type);
|
|
556
|
-
if (link.media)
|
|
557
|
-
tag.setAttribute("media", link.media);
|
|
558
|
-
if (link.sizes)
|
|
559
|
-
tag.setAttribute("sizes", link.sizes);
|
|
560
|
-
if (link.crossorigin)
|
|
561
|
-
tag.setAttribute("crossorigin", link.crossorigin);
|
|
562
|
-
elements.push(tag);
|
|
563
|
-
}
|
|
564
|
-
for (const style of head.styles ?? []) {
|
|
565
|
-
const tag = managed(document.createElement("style"));
|
|
566
|
-
if (style.media)
|
|
567
|
-
tag.setAttribute("media", style.media);
|
|
568
|
-
tag.textContent = style.children;
|
|
569
|
-
elements.push(tag);
|
|
570
|
-
}
|
|
571
|
-
for (const script of head.scripts ?? []) {
|
|
572
|
-
const tag = managed(document.createElement("script"));
|
|
573
|
-
if (script.src)
|
|
574
|
-
tag.setAttribute("src", script.src);
|
|
575
|
-
if (script.type)
|
|
576
|
-
tag.setAttribute("type", script.type);
|
|
577
|
-
if (script.async)
|
|
578
|
-
tag.async = true;
|
|
579
|
-
if (script.defer)
|
|
580
|
-
tag.defer = true;
|
|
581
|
-
if (script.children)
|
|
582
|
-
tag.textContent = script.children;
|
|
583
795
|
elements.push(tag);
|
|
584
796
|
}
|
|
585
797
|
return elements;
|
|
@@ -588,6 +800,10 @@ function reconcileDocumentHead(head) {
|
|
|
588
800
|
if (typeof document === "undefined") {
|
|
589
801
|
return;
|
|
590
802
|
}
|
|
803
|
+
const title = [...head].reverse().find((element) => element.tag === "title");
|
|
804
|
+
if (title && title.tag === "title") {
|
|
805
|
+
document.title = title.children;
|
|
806
|
+
}
|
|
591
807
|
for (const element of Array.from(document.head.querySelectorAll(`[${MANAGED_HEAD_ATTRIBUTE}]`))) {
|
|
592
808
|
element.remove();
|
|
593
809
|
}
|
|
@@ -602,17 +818,17 @@ function RenderMatches({ matches, index }) {
|
|
|
602
818
|
return null;
|
|
603
819
|
}
|
|
604
820
|
const Component = match.route.options.component;
|
|
605
|
-
const outlet = /* @__PURE__ */
|
|
821
|
+
const outlet = /* @__PURE__ */ jsx_runtime.jsx(RenderMatches, {
|
|
606
822
|
matches,
|
|
607
823
|
index: index + 1
|
|
608
|
-
}
|
|
609
|
-
return /* @__PURE__ */
|
|
824
|
+
});
|
|
825
|
+
return /* @__PURE__ */ jsx_runtime.jsx(MatchContext.Provider, {
|
|
610
826
|
value: match,
|
|
611
|
-
children: /* @__PURE__ */
|
|
827
|
+
children: /* @__PURE__ */ jsx_runtime.jsx(OutletContext.Provider, {
|
|
612
828
|
value: outlet,
|
|
613
|
-
children: Component ? /* @__PURE__ */
|
|
614
|
-
}
|
|
615
|
-
}
|
|
829
|
+
children: Component ? /* @__PURE__ */ jsx_runtime.jsx(Component, {}) : outlet
|
|
830
|
+
})
|
|
831
|
+
});
|
|
616
832
|
}
|
|
617
833
|
function renderError(error, matches, router) {
|
|
618
834
|
const reversed = [...matches].reverse();
|
|
@@ -621,9 +837,9 @@ function renderError(error, matches, router) {
|
|
|
621
837
|
if (NotFoundComponent) {
|
|
622
838
|
return import_react.default.createElement(NotFoundComponent);
|
|
623
839
|
}
|
|
624
|
-
return /* @__PURE__ */
|
|
840
|
+
return /* @__PURE__ */ jsx_runtime.jsx("div", {
|
|
625
841
|
children: "Not Found"
|
|
626
|
-
}
|
|
842
|
+
});
|
|
627
843
|
}
|
|
628
844
|
const ErrorComponent = reversed.find((match) => match.route.options.errorComponent)?.route.options.errorComponent ?? router.options.defaultErrorComponent;
|
|
629
845
|
if (ErrorComponent) {
|
|
@@ -634,9 +850,9 @@ function renderError(error, matches, router) {
|
|
|
634
850
|
}
|
|
635
851
|
});
|
|
636
852
|
}
|
|
637
|
-
return /* @__PURE__ */
|
|
853
|
+
return /* @__PURE__ */ jsx_runtime.jsx("pre", {
|
|
638
854
|
children: error instanceof Error ? error.message : "Unknown routing error"
|
|
639
|
-
}
|
|
855
|
+
});
|
|
640
856
|
}
|
|
641
857
|
function RouterProvider({ router }) {
|
|
642
858
|
const snapshot = import_react.default.useSyncExternalStore(router.subscribe, router.getSnapshot, router.getSnapshot);
|
|
@@ -649,17 +865,17 @@ function RouterProvider({ router }) {
|
|
|
649
865
|
import_react.default.useEffect(() => {
|
|
650
866
|
reconcileDocumentHead(snapshot.head);
|
|
651
867
|
}, [snapshot.head]);
|
|
652
|
-
const content = snapshot.error ? renderError(snapshot.error, snapshot.matches, router) : /* @__PURE__ */
|
|
868
|
+
const content = snapshot.error ? renderError(snapshot.error, snapshot.matches, router) : /* @__PURE__ */ jsx_runtime.jsx(RenderMatches, {
|
|
653
869
|
matches: snapshot.matches,
|
|
654
870
|
index: 0
|
|
655
|
-
}
|
|
656
|
-
return /* @__PURE__ */
|
|
871
|
+
});
|
|
872
|
+
return /* @__PURE__ */ jsx_runtime.jsx(RouterContext.Provider, {
|
|
657
873
|
value: router,
|
|
658
|
-
children: /* @__PURE__ */
|
|
874
|
+
children: /* @__PURE__ */ jsx_runtime.jsx(RouterStateContext.Provider, {
|
|
659
875
|
value: snapshot,
|
|
660
876
|
children: content
|
|
661
|
-
}
|
|
662
|
-
}
|
|
877
|
+
})
|
|
878
|
+
});
|
|
663
879
|
}
|
|
664
880
|
function Outlet() {
|
|
665
881
|
return import_react.default.useContext(OutletContext);
|
|
@@ -685,6 +901,31 @@ function useNavigate() {
|
|
|
685
901
|
await router.navigate(options);
|
|
686
902
|
}, [router]);
|
|
687
903
|
}
|
|
904
|
+
function useMatchRoute() {
|
|
905
|
+
const location = useLocation();
|
|
906
|
+
return import_react.default.useCallback((options) => {
|
|
907
|
+
const matched = import_core.matchPathname(options.to, location.pathname, {
|
|
908
|
+
partial: options.fuzzy === true
|
|
909
|
+
});
|
|
910
|
+
if (!matched) {
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
if (options.params) {
|
|
914
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
915
|
+
if (value !== undefined && matched.params[key] !== value) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (options.includeSearch) {
|
|
921
|
+
const expectedSearch = options.search ?? {};
|
|
922
|
+
if (!isDeepInclusiveMatch(expectedSearch, location.search)) {
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return matched.params;
|
|
927
|
+
}, [location.pathname, location.search]);
|
|
928
|
+
}
|
|
688
929
|
function useLocation() {
|
|
689
930
|
return useRouterStateContext().location;
|
|
690
931
|
}
|
|
@@ -728,12 +969,14 @@ function useElementScrollRestoration() {
|
|
|
728
969
|
ref: () => {}
|
|
729
970
|
};
|
|
730
971
|
}
|
|
731
|
-
function useResolvedLink(props) {
|
|
972
|
+
function useResolvedLink(props, activeOptions) {
|
|
732
973
|
const router = useRouterContext();
|
|
733
974
|
const href = router.buildHref(props);
|
|
734
975
|
const location = useLocation();
|
|
735
|
-
const
|
|
736
|
-
const isActive =
|
|
976
|
+
const targetPathname = stripBasePathFromHref(href, router.options.basePath).split(/[?#]/u)[0] ?? href;
|
|
977
|
+
const isActive = import_core.matchPathname(targetPathname, location.pathname, {
|
|
978
|
+
partial: activeOptions?.exact !== true
|
|
979
|
+
}) !== null;
|
|
737
980
|
return { href, isActive, router };
|
|
738
981
|
}
|
|
739
982
|
var LinkComponent = import_react.default.forwardRef(function LinkInner(props, ref) {
|
|
@@ -749,6 +992,7 @@ var LinkComponent = import_react.default.forwardRef(function LinkInner(props, re
|
|
|
749
992
|
state,
|
|
750
993
|
mask,
|
|
751
994
|
ignoreBlocker,
|
|
995
|
+
activeOptions,
|
|
752
996
|
activeProps,
|
|
753
997
|
children,
|
|
754
998
|
onClick,
|
|
@@ -769,7 +1013,7 @@ var LinkComponent = import_react.default.forwardRef(function LinkInner(props, re
|
|
|
769
1013
|
mask,
|
|
770
1014
|
ignoreBlocker
|
|
771
1015
|
};
|
|
772
|
-
const { href, isActive, router } = useResolvedLink(navigation);
|
|
1016
|
+
const { href, isActive, router } = useResolvedLink(navigation, activeOptions);
|
|
773
1017
|
const preloadMode = preload ?? router.options.defaultPreload;
|
|
774
1018
|
const preloadDelay = router.options.defaultPreloadDelay ?? 50;
|
|
775
1019
|
const preloadTimeout = import_react.default.useRef(null);
|
|
@@ -794,7 +1038,7 @@ var LinkComponent = import_react.default.forwardRef(function LinkInner(props, re
|
|
|
794
1038
|
}
|
|
795
1039
|
}, []);
|
|
796
1040
|
const renderedChildren = typeof children === "function" ? children({ isActive }) : children;
|
|
797
|
-
return /* @__PURE__ */
|
|
1041
|
+
return /* @__PURE__ */ jsx_runtime.jsx("a", {
|
|
798
1042
|
...anchorProps,
|
|
799
1043
|
...isActive ? activeProps : undefined,
|
|
800
1044
|
ref,
|
|
@@ -822,7 +1066,7 @@ var LinkComponent = import_react.default.forwardRef(function LinkInner(props, re
|
|
|
822
1066
|
},
|
|
823
1067
|
onBlur: cancelPreload,
|
|
824
1068
|
children: renderedChildren
|
|
825
|
-
}
|
|
1069
|
+
});
|
|
826
1070
|
});
|
|
827
1071
|
var Link = LinkComponent;
|
|
828
1072
|
function createLink(Component) {
|
|
@@ -839,6 +1083,7 @@ function createLink(Component) {
|
|
|
839
1083
|
state,
|
|
840
1084
|
mask,
|
|
841
1085
|
ignoreBlocker,
|
|
1086
|
+
activeOptions,
|
|
842
1087
|
activeProps,
|
|
843
1088
|
children,
|
|
844
1089
|
preload,
|
|
@@ -856,7 +1101,7 @@ function createLink(Component) {
|
|
|
856
1101
|
mask,
|
|
857
1102
|
ignoreBlocker
|
|
858
1103
|
};
|
|
859
|
-
const { href, isActive, router } = useResolvedLink(navigation);
|
|
1104
|
+
const { href, isActive, router } = useResolvedLink(navigation, activeOptions);
|
|
860
1105
|
const renderedChildren = typeof children === "function" ? children({ isActive }) : children;
|
|
861
1106
|
import_react.default.useEffect(() => {
|
|
862
1107
|
if (preload !== "render") {
|
|
@@ -864,12 +1109,12 @@ function createLink(Component) {
|
|
|
864
1109
|
}
|
|
865
1110
|
router.preloadRoute(navigation);
|
|
866
1111
|
}, [navigation, preload, router]);
|
|
867
|
-
return /* @__PURE__ */
|
|
1112
|
+
return /* @__PURE__ */ jsx_runtime.jsx(Component, {
|
|
868
1113
|
...componentProps,
|
|
869
1114
|
...isActive ? activeProps : undefined,
|
|
870
1115
|
href,
|
|
871
1116
|
children: renderedChildren
|
|
872
|
-
}
|
|
1117
|
+
});
|
|
873
1118
|
};
|
|
874
1119
|
}
|
|
875
1120
|
function linkOptions(options) {
|