@solidjs/router 0.10.0-beta.4 → 0.10.0-beta.6
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 +21 -9
- package/dist/components.d.ts +2 -24
- package/dist/components.jsx +3 -84
- package/dist/data/action.d.ts +1 -0
- package/dist/data/action.js +22 -29
- package/dist/data/cache.d.ts +6 -2
- package/dist/data/cache.js +55 -12
- package/dist/data/events.d.ts +2 -0
- package/dist/data/events.js +116 -0
- package/dist/data/index.d.ts +2 -2
- package/dist/data/response.d.ts +5 -1
- package/dist/data/response.js +17 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +521 -472
- package/dist/index.jsx +1 -1
- package/dist/routers/HashRouter.d.ts +5 -0
- package/dist/routers/HashRouter.js +36 -0
- package/dist/routers/MemoryRouter.d.ts +21 -0
- package/dist/routers/MemoryRouter.js +52 -0
- package/dist/routers/Router.d.ts +6 -0
- package/dist/routers/Router.js +28 -0
- package/dist/routers/StaticRouter.d.ts +6 -0
- package/dist/routers/StaticRouter.js +15 -0
- package/dist/routers/components.d.ts +17 -0
- package/dist/routers/components.jsx +70 -0
- package/dist/routers/createIntegration.d.ts +4 -0
- package/dist/routers/createIntegration.js +49 -0
- package/dist/routers/createIntegration.jsx +48 -0
- package/dist/routers/createRouter.d.ts +10 -0
- package/dist/routers/createRouter.js +49 -0
- package/dist/routers/index.d.ts +11 -0
- package/dist/routers/index.js +6 -0
- package/dist/routing.d.ts +2 -3
- package/dist/routing.js +53 -166
- package/dist/types.d.ts +9 -4
- package/package.json +1 -1
- package/dist/integration.d.ts +0 -15
- package/dist/integration.js +0 -154
package/dist/index.js
CHANGED
|
@@ -1,174 +1,7 @@
|
|
|
1
|
-
import { isServer,
|
|
2
|
-
import {
|
|
1
|
+
import { isServer, getRequestEvent, createComponent as createComponent$1, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
|
|
2
|
+
import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, createRoot, Show, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
|
|
3
3
|
import { createStore, reconcile } from 'solid-js/store';
|
|
4
4
|
|
|
5
|
-
function bindEvent(target, type, handler) {
|
|
6
|
-
target.addEventListener(type, handler);
|
|
7
|
-
return () => target.removeEventListener(type, handler);
|
|
8
|
-
}
|
|
9
|
-
function intercept([value, setValue], get, set) {
|
|
10
|
-
return [get ? () => get(value()) : value, set ? v => setValue(set(v)) : setValue];
|
|
11
|
-
}
|
|
12
|
-
function querySelector(selector) {
|
|
13
|
-
if (selector === "#") {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
// Guard against selector being an invalid CSS selector
|
|
17
|
-
try {
|
|
18
|
-
return document.querySelector(selector);
|
|
19
|
-
} catch (e) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function scrollToHash(hash, fallbackTop) {
|
|
24
|
-
const el = querySelector(`#${hash}`);
|
|
25
|
-
if (el) {
|
|
26
|
-
el.scrollIntoView();
|
|
27
|
-
} else if (fallbackTop) {
|
|
28
|
-
window.scrollTo(0, 0);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function createMemoryHistory() {
|
|
32
|
-
const entries = ["/"];
|
|
33
|
-
let index = 0;
|
|
34
|
-
const listeners = [];
|
|
35
|
-
const go = n => {
|
|
36
|
-
// https://github.com/remix-run/react-router/blob/682810ca929d0e3c64a76f8d6e465196b7a2ac58/packages/router/history.ts#L245
|
|
37
|
-
index = Math.max(0, Math.min(index + n, entries.length - 1));
|
|
38
|
-
const value = entries[index];
|
|
39
|
-
listeners.forEach(listener => listener(value));
|
|
40
|
-
};
|
|
41
|
-
return {
|
|
42
|
-
get: () => entries[index],
|
|
43
|
-
set: ({
|
|
44
|
-
value,
|
|
45
|
-
scroll,
|
|
46
|
-
replace
|
|
47
|
-
}) => {
|
|
48
|
-
if (replace) {
|
|
49
|
-
entries[index] = value;
|
|
50
|
-
} else {
|
|
51
|
-
entries.splice(index + 1, entries.length - index, value);
|
|
52
|
-
index++;
|
|
53
|
-
}
|
|
54
|
-
if (scroll) {
|
|
55
|
-
scrollToHash(value.split("#")[1] || "", true);
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
back: () => {
|
|
59
|
-
go(-1);
|
|
60
|
-
},
|
|
61
|
-
forward: () => {
|
|
62
|
-
go(1);
|
|
63
|
-
},
|
|
64
|
-
go,
|
|
65
|
-
listen: listener => {
|
|
66
|
-
listeners.push(listener);
|
|
67
|
-
return () => {
|
|
68
|
-
const index = listeners.indexOf(listener);
|
|
69
|
-
listeners.splice(index, 1);
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function createIntegration(get, set, init, utils) {
|
|
75
|
-
let ignore = false;
|
|
76
|
-
const wrap = value => typeof value === "string" ? {
|
|
77
|
-
value
|
|
78
|
-
} : value;
|
|
79
|
-
const signal = intercept(createSignal(wrap(get()), {
|
|
80
|
-
equals: (a, b) => a.value === b.value
|
|
81
|
-
}), undefined, next => {
|
|
82
|
-
!ignore && set(next);
|
|
83
|
-
return next;
|
|
84
|
-
});
|
|
85
|
-
init && onCleanup(init((value = get()) => {
|
|
86
|
-
ignore = true;
|
|
87
|
-
signal[1](wrap(value));
|
|
88
|
-
ignore = false;
|
|
89
|
-
}));
|
|
90
|
-
return {
|
|
91
|
-
signal,
|
|
92
|
-
utils
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function normalizeIntegration(integration) {
|
|
96
|
-
if (!integration) {
|
|
97
|
-
return {
|
|
98
|
-
signal: createSignal({
|
|
99
|
-
value: ""
|
|
100
|
-
})
|
|
101
|
-
};
|
|
102
|
-
} else if (Array.isArray(integration)) {
|
|
103
|
-
return {
|
|
104
|
-
signal: integration
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
return integration;
|
|
108
|
-
}
|
|
109
|
-
function staticIntegration(obj) {
|
|
110
|
-
return {
|
|
111
|
-
signal: [() => obj, next => Object.assign(obj, next)]
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function pathIntegration() {
|
|
115
|
-
return createIntegration(() => ({
|
|
116
|
-
value: window.location.pathname + window.location.search + window.location.hash,
|
|
117
|
-
state: history.state
|
|
118
|
-
}), ({
|
|
119
|
-
value,
|
|
120
|
-
replace,
|
|
121
|
-
scroll,
|
|
122
|
-
state
|
|
123
|
-
}) => {
|
|
124
|
-
if (replace) {
|
|
125
|
-
window.history.replaceState(state, "", value);
|
|
126
|
-
} else {
|
|
127
|
-
window.history.pushState(state, "", value);
|
|
128
|
-
}
|
|
129
|
-
scrollToHash(window.location.hash.slice(1), scroll);
|
|
130
|
-
}, notify => bindEvent(window, "popstate", () => notify()), {
|
|
131
|
-
go: delta => window.history.go(delta)
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
function hashIntegration() {
|
|
135
|
-
return createIntegration(() => window.location.hash.slice(1), ({
|
|
136
|
-
value,
|
|
137
|
-
replace,
|
|
138
|
-
scroll,
|
|
139
|
-
state
|
|
140
|
-
}) => {
|
|
141
|
-
if (replace) {
|
|
142
|
-
window.history.replaceState(state, "", "#" + value);
|
|
143
|
-
} else {
|
|
144
|
-
window.location.hash = value;
|
|
145
|
-
}
|
|
146
|
-
const hashIndex = value.indexOf("#");
|
|
147
|
-
const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
|
|
148
|
-
scrollToHash(hash, scroll);
|
|
149
|
-
}, notify => bindEvent(window, "hashchange", () => notify()), {
|
|
150
|
-
go: delta => window.history.go(delta),
|
|
151
|
-
renderPath: path => `#${path}`,
|
|
152
|
-
parsePath: str => {
|
|
153
|
-
const to = str.replace(/^.*?#/, "");
|
|
154
|
-
// Hash-only hrefs like `#foo` from plain anchors will come in as `/#foo` whereas a link to
|
|
155
|
-
// `/foo` will be `/#/foo`. Check if the to starts with a `/` and if not append it as a hash
|
|
156
|
-
// to the current path so we can handle these in-page anchors correctly.
|
|
157
|
-
if (!to.startsWith("/")) {
|
|
158
|
-
const [, path = "/"] = window.location.hash.split("#", 2);
|
|
159
|
-
return `${path}#${to}`;
|
|
160
|
-
}
|
|
161
|
-
return to;
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
function memoryIntegration() {
|
|
166
|
-
const memoryHistory = createMemoryHistory();
|
|
167
|
-
return createIntegration(memoryHistory.get, memoryHistory.set, memoryHistory.listen, {
|
|
168
|
-
go: memoryHistory.go
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
5
|
function createBeforeLeave() {
|
|
173
6
|
let listeners = new Set();
|
|
174
7
|
function subscribe(listener) {
|
|
@@ -521,10 +354,6 @@ function createLocation(path, state) {
|
|
|
521
354
|
query: createMemoObject(on(search, () => extractSearchParams(url())))
|
|
522
355
|
};
|
|
523
356
|
}
|
|
524
|
-
const actions = new Map();
|
|
525
|
-
function registerAction(url, fn) {
|
|
526
|
-
actions.set(url, fn);
|
|
527
|
-
}
|
|
528
357
|
let intent;
|
|
529
358
|
function getIntent() {
|
|
530
359
|
return intent;
|
|
@@ -533,12 +362,11 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
533
362
|
const {
|
|
534
363
|
signal: [source, setSource],
|
|
535
364
|
utils = {}
|
|
536
|
-
} =
|
|
365
|
+
} = integration;
|
|
537
366
|
const parsePath = utils.parsePath || (p => p);
|
|
538
367
|
const renderPath = utils.renderPath || (p => p);
|
|
539
368
|
const beforeLeave = utils.beforeLeave || createBeforeLeave();
|
|
540
369
|
const basePath = resolvePath("", options.base || "");
|
|
541
|
-
const actionBase = options.actionBase || "/_server";
|
|
542
370
|
if (basePath === undefined) {
|
|
543
371
|
throw new Error(`${basePath} is not a valid base path`);
|
|
544
372
|
} else if (basePath && !source().value) {
|
|
@@ -561,6 +389,7 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
561
389
|
const [state, setState] = createSignal(source().state);
|
|
562
390
|
const location = createLocation(reference, state);
|
|
563
391
|
const referrers = [];
|
|
392
|
+
const submissions = createSignal(initFromFlash(location.query));
|
|
564
393
|
const baseRoute = {
|
|
565
394
|
pattern: basePath,
|
|
566
395
|
params: {},
|
|
@@ -570,15 +399,37 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
570
399
|
return resolvePath(basePath, to);
|
|
571
400
|
}
|
|
572
401
|
};
|
|
573
|
-
|
|
402
|
+
createRenderEffect(() => {
|
|
403
|
+
const {
|
|
404
|
+
value,
|
|
405
|
+
state
|
|
406
|
+
} = source();
|
|
407
|
+
// Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
|
|
408
|
+
untrack(() => {
|
|
409
|
+
if (value !== reference()) {
|
|
410
|
+
start(() => {
|
|
411
|
+
intent = "native";
|
|
412
|
+
setReference(value);
|
|
413
|
+
setState(state);
|
|
414
|
+
resetErrorBoundaries();
|
|
415
|
+
submissions[1]([]);
|
|
416
|
+
}).then(() => {
|
|
417
|
+
intent = undefined;
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
574
423
|
base: baseRoute,
|
|
424
|
+
actionBase: options.actionBase || "/_server",
|
|
575
425
|
location,
|
|
576
426
|
isRouting,
|
|
577
427
|
renderPath,
|
|
578
428
|
parsePath,
|
|
579
429
|
navigatorFactory,
|
|
580
430
|
beforeLeave,
|
|
581
|
-
|
|
431
|
+
preloadRoute,
|
|
432
|
+
submissions
|
|
582
433
|
};
|
|
583
434
|
function navigateFromRoute(route, to, options) {
|
|
584
435
|
// Untrack in case someone navigates in an effect - don't want to track `reference` or route paths
|
|
@@ -636,6 +487,7 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
636
487
|
setReference(resolvedTo);
|
|
637
488
|
setState(nextState);
|
|
638
489
|
resetErrorBoundaries();
|
|
490
|
+
submissions[1]([]);
|
|
639
491
|
}).then(() => {
|
|
640
492
|
if (referrers.length === len) {
|
|
641
493
|
intent = undefined;
|
|
@@ -667,152 +519,41 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
667
519
|
referrers.length = 0;
|
|
668
520
|
}
|
|
669
521
|
}
|
|
522
|
+
function preloadRoute(url, preloadData) {
|
|
523
|
+
const matches = getRouteMatches(getBranches(), url.pathname);
|
|
524
|
+
const prevIntent = intent;
|
|
525
|
+
intent = "preload";
|
|
526
|
+
for (let match in matches) {
|
|
527
|
+
const {
|
|
528
|
+
route,
|
|
529
|
+
params
|
|
530
|
+
} = matches[match];
|
|
531
|
+
route.component && route.component.preload && route.component.preload();
|
|
532
|
+
preloadData && route.load && route.load({
|
|
533
|
+
params,
|
|
534
|
+
location: {
|
|
535
|
+
pathname: url.pathname,
|
|
536
|
+
search: url.search,
|
|
537
|
+
hash: url.hash,
|
|
538
|
+
query: extractSearchParams(url),
|
|
539
|
+
state: null,
|
|
540
|
+
key: ""
|
|
541
|
+
},
|
|
542
|
+
intent: "preload"
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
intent = prevIntent;
|
|
546
|
+
}
|
|
670
547
|
function initFromFlash(params) {
|
|
671
548
|
let param = params.form ? JSON.parse(params.form) : null;
|
|
672
549
|
if (!param || !param.result) return [];
|
|
673
550
|
const input = new Map(param.entries);
|
|
674
551
|
return [{
|
|
675
552
|
url: param.url,
|
|
676
|
-
result: param.error ? new Error(param.result
|
|
553
|
+
result: param.error ? new Error(param.result) : param.result,
|
|
677
554
|
input: input
|
|
678
555
|
}];
|
|
679
556
|
}
|
|
680
|
-
createRenderEffect(() => {
|
|
681
|
-
const {
|
|
682
|
-
value,
|
|
683
|
-
state
|
|
684
|
-
} = source();
|
|
685
|
-
// Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
|
|
686
|
-
untrack(() => {
|
|
687
|
-
if (value !== reference()) {
|
|
688
|
-
start(() => {
|
|
689
|
-
intent = "native";
|
|
690
|
-
setReference(value);
|
|
691
|
-
setState(state);
|
|
692
|
-
}).then(() => {
|
|
693
|
-
intent = undefined;
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
});
|
|
698
|
-
if (!isServer) {
|
|
699
|
-
let preloadTimeout = {};
|
|
700
|
-
function isSvg(el) {
|
|
701
|
-
return el.namespaceURI === "http://www.w3.org/2000/svg";
|
|
702
|
-
}
|
|
703
|
-
function handleAnchor(evt) {
|
|
704
|
-
if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
|
|
705
|
-
const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
|
|
706
|
-
if (!a) return;
|
|
707
|
-
const svg = isSvg(a);
|
|
708
|
-
const href = svg ? a.href.baseVal : a.href;
|
|
709
|
-
const target = svg ? a.target.baseVal : a.target;
|
|
710
|
-
if (target || !href && !a.hasAttribute("state")) return;
|
|
711
|
-
const rel = (a.getAttribute("rel") || "").split(/\s+/);
|
|
712
|
-
if (a.hasAttribute("download") || rel && rel.includes("external")) return;
|
|
713
|
-
const url = svg ? new URL(href, document.baseURI) : new URL(href);
|
|
714
|
-
if (url.origin !== window.location.origin || basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())) return;
|
|
715
|
-
return [a, url];
|
|
716
|
-
}
|
|
717
|
-
function handleAnchorClick(evt) {
|
|
718
|
-
const res = handleAnchor(evt);
|
|
719
|
-
if (!res) return;
|
|
720
|
-
const [a, url] = res;
|
|
721
|
-
const to = parsePath(url.pathname + url.search + url.hash);
|
|
722
|
-
const state = a.getAttribute("state");
|
|
723
|
-
evt.preventDefault();
|
|
724
|
-
navigateFromRoute(baseRoute, to, {
|
|
725
|
-
resolve: false,
|
|
726
|
-
replace: a.hasAttribute("replace"),
|
|
727
|
-
scroll: !a.hasAttribute("noscroll"),
|
|
728
|
-
state: state && JSON.parse(state)
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
function doPreload(a, url) {
|
|
732
|
-
const preload = a.getAttribute("preload") !== "false";
|
|
733
|
-
const matches = getRouteMatches(getBranches(), url.pathname);
|
|
734
|
-
const prevIntent = intent;
|
|
735
|
-
intent = "preload";
|
|
736
|
-
for (let match in matches) {
|
|
737
|
-
const {
|
|
738
|
-
route,
|
|
739
|
-
params
|
|
740
|
-
} = matches[match];
|
|
741
|
-
route.component && route.component.preload && route.component.preload();
|
|
742
|
-
preload && route.load && route.load({
|
|
743
|
-
params,
|
|
744
|
-
location: {
|
|
745
|
-
pathname: url.pathname,
|
|
746
|
-
search: url.search,
|
|
747
|
-
hash: url.hash,
|
|
748
|
-
query: extractSearchParams(url),
|
|
749
|
-
state: null,
|
|
750
|
-
key: ""
|
|
751
|
-
},
|
|
752
|
-
intent
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
intent = prevIntent;
|
|
756
|
-
}
|
|
757
|
-
function handleAnchorPreload(evt) {
|
|
758
|
-
const res = handleAnchor(evt);
|
|
759
|
-
if (!res) return;
|
|
760
|
-
const [a, url] = res;
|
|
761
|
-
if (!preloadTimeout[url.pathname]) doPreload(a, url);
|
|
762
|
-
}
|
|
763
|
-
function handleAnchorIn(evt) {
|
|
764
|
-
const res = handleAnchor(evt);
|
|
765
|
-
if (!res) return;
|
|
766
|
-
const [a, url] = res;
|
|
767
|
-
if (preloadTimeout[url.pathname]) return;
|
|
768
|
-
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
769
|
-
doPreload(a, url);
|
|
770
|
-
delete preloadTimeout[url.pathname];
|
|
771
|
-
}, 200);
|
|
772
|
-
}
|
|
773
|
-
function handleAnchorOut(evt) {
|
|
774
|
-
const res = handleAnchor(evt);
|
|
775
|
-
if (!res) return;
|
|
776
|
-
const [, url] = res;
|
|
777
|
-
if (preloadTimeout[url.pathname]) {
|
|
778
|
-
clearTimeout(preloadTimeout[url.pathname]);
|
|
779
|
-
delete preloadTimeout[url.pathname];
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
function handleFormSubmit(evt) {
|
|
783
|
-
let actionRef = evt.submitter && evt.submitter.getAttribute("formaction") || evt.target.action;
|
|
784
|
-
if (!actionRef) return;
|
|
785
|
-
if (!actionRef.startsWith("action:")) {
|
|
786
|
-
const url = new URL(actionRef);
|
|
787
|
-
actionRef = parsePath(url.pathname + url.search);
|
|
788
|
-
if (!actionRef.startsWith(actionBase)) return;
|
|
789
|
-
}
|
|
790
|
-
const handler = actions.get(actionRef);
|
|
791
|
-
if (handler) {
|
|
792
|
-
evt.preventDefault();
|
|
793
|
-
const data = new FormData(evt.target);
|
|
794
|
-
handler.call(router, data);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// ensure delegated event run first
|
|
799
|
-
delegateEvents(["click", "submit"]);
|
|
800
|
-
document.addEventListener("click", handleAnchorClick);
|
|
801
|
-
document.addEventListener("mouseover", handleAnchorIn);
|
|
802
|
-
document.addEventListener("mouseout", handleAnchorOut);
|
|
803
|
-
document.addEventListener("focusin", handleAnchorPreload);
|
|
804
|
-
document.addEventListener("touchstart", handleAnchorPreload);
|
|
805
|
-
document.addEventListener("submit", handleFormSubmit);
|
|
806
|
-
onCleanup(() => {
|
|
807
|
-
document.removeEventListener("click", handleAnchorClick);
|
|
808
|
-
document.removeEventListener("mouseover", handleAnchorIn);
|
|
809
|
-
document.removeEventListener("mouseout", handleAnchorOut);
|
|
810
|
-
document.removeEventListener("focusin", handleAnchorPreload);
|
|
811
|
-
document.removeEventListener("touchstart", handleAnchorPreload);
|
|
812
|
-
document.removeEventListener("submit", handleFormSubmit);
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
return router;
|
|
816
557
|
}
|
|
817
558
|
function createRouteContext(router, parent, outlet, match, params) {
|
|
818
559
|
const {
|
|
@@ -845,32 +586,26 @@ function createRouteContext(router, parent, outlet, match, params) {
|
|
|
845
586
|
load && load({
|
|
846
587
|
params,
|
|
847
588
|
location,
|
|
848
|
-
intent: intent || "
|
|
589
|
+
intent: intent || "initial"
|
|
849
590
|
});
|
|
850
591
|
return route;
|
|
851
592
|
}
|
|
852
593
|
|
|
853
|
-
const
|
|
854
|
-
const Router = props => {
|
|
855
|
-
let e;
|
|
594
|
+
const createRouterComponent = router => props => {
|
|
856
595
|
const {
|
|
857
|
-
source,
|
|
858
|
-
url,
|
|
859
596
|
base,
|
|
860
597
|
actionBase
|
|
861
598
|
} = props;
|
|
862
|
-
const integration = source || (isServer ? staticIntegration({
|
|
863
|
-
value: url || (e = getRequestEvent()) && getPath(e.request.url) || ""
|
|
864
|
-
}) : pathIntegration());
|
|
865
599
|
const routeDefs = children(() => props.children);
|
|
866
600
|
const branches = createMemo(() => createBranches(props.root ? {
|
|
867
601
|
component: props.root,
|
|
868
602
|
children: routeDefs()
|
|
869
603
|
} : routeDefs(), props.base || ""));
|
|
870
|
-
const routerState = createRouterContext(
|
|
604
|
+
const routerState = createRouterContext(router, branches, {
|
|
871
605
|
base,
|
|
872
606
|
actionBase
|
|
873
607
|
});
|
|
608
|
+
router.create && router.create(routerState);
|
|
874
609
|
return createComponent$1(RouterContextObj.Provider, {
|
|
875
610
|
value: routerState,
|
|
876
611
|
get children() {
|
|
@@ -883,10 +618,6 @@ const Router = props => {
|
|
|
883
618
|
}
|
|
884
619
|
});
|
|
885
620
|
};
|
|
886
|
-
function getPath(url) {
|
|
887
|
-
const u = new URL(url);
|
|
888
|
-
return u.pathname + u.search;
|
|
889
|
-
}
|
|
890
621
|
function Routes(props) {
|
|
891
622
|
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
892
623
|
const params = createMemoObject(() => {
|
|
@@ -960,150 +691,117 @@ const Route = props => {
|
|
|
960
691
|
}
|
|
961
692
|
});
|
|
962
693
|
};
|
|
963
|
-
function A(props) {
|
|
964
|
-
props = mergeProps({
|
|
965
|
-
inactiveClass: "inactive",
|
|
966
|
-
activeClass: "active"
|
|
967
|
-
}, props);
|
|
968
|
-
const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
|
|
969
|
-
const to = useResolvedPath(() => props.href);
|
|
970
|
-
const href = useHref(to);
|
|
971
|
-
const location = useLocation();
|
|
972
|
-
const isActive = createMemo(() => {
|
|
973
|
-
const to_ = to();
|
|
974
|
-
if (to_ === undefined) return false;
|
|
975
|
-
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
|
|
976
|
-
const loc = normalizePath(location.pathname).toLowerCase();
|
|
977
|
-
return props.end ? path === loc : loc.startsWith(path);
|
|
978
|
-
});
|
|
979
|
-
return (() => {
|
|
980
|
-
const _el$ = _tmpl$();
|
|
981
|
-
spread(_el$, mergeProps$1(rest, {
|
|
982
|
-
get href() {
|
|
983
|
-
return href() || props.href;
|
|
984
|
-
},
|
|
985
|
-
get state() {
|
|
986
|
-
return JSON.stringify(props.state);
|
|
987
|
-
},
|
|
988
|
-
get classList() {
|
|
989
|
-
return {
|
|
990
|
-
...(props.class && {
|
|
991
|
-
[props.class]: true
|
|
992
|
-
}),
|
|
993
|
-
[props.inactiveClass]: !isActive(),
|
|
994
|
-
[props.activeClass]: isActive(),
|
|
995
|
-
...rest.classList
|
|
996
|
-
};
|
|
997
|
-
},
|
|
998
|
-
get ["aria-current"]() {
|
|
999
|
-
return isActive() ? "page" : undefined;
|
|
1000
|
-
}
|
|
1001
|
-
}), false, false);
|
|
1002
|
-
return _el$;
|
|
1003
|
-
})();
|
|
1004
|
-
}
|
|
1005
|
-
function Navigate(props) {
|
|
1006
|
-
const navigate = useNavigate();
|
|
1007
|
-
const location = useLocation();
|
|
1008
|
-
const {
|
|
1009
|
-
href,
|
|
1010
|
-
state
|
|
1011
|
-
} = props;
|
|
1012
|
-
const path = typeof href === "function" ? href({
|
|
1013
|
-
navigate,
|
|
1014
|
-
location
|
|
1015
|
-
}) : href;
|
|
1016
|
-
navigate(path, {
|
|
1017
|
-
replace: true,
|
|
1018
|
-
state
|
|
1019
|
-
});
|
|
1020
|
-
return null;
|
|
1021
|
-
}
|
|
1022
694
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
*/
|
|
1026
|
-
function createAsync(fn, options) {
|
|
1027
|
-
const [resource] = createResource(() => subFetch(fn), v => v, options);
|
|
1028
|
-
return () => resource();
|
|
695
|
+
function intercept([value, setValue], get, set) {
|
|
696
|
+
return [get ? () => get(value()) : value, set ? v => setValue(set(v)) : setValue];
|
|
1029
697
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
static all() {
|
|
1034
|
-
return new MockPromise();
|
|
698
|
+
function querySelector(selector) {
|
|
699
|
+
if (selector === "#") {
|
|
700
|
+
return null;
|
|
1035
701
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
return
|
|
1041
|
-
}
|
|
1042
|
-
static race() {
|
|
1043
|
-
return new MockPromise();
|
|
1044
|
-
}
|
|
1045
|
-
static reject() {
|
|
1046
|
-
return new MockPromise();
|
|
1047
|
-
}
|
|
1048
|
-
static resolve() {
|
|
1049
|
-
return new MockPromise();
|
|
1050
|
-
}
|
|
1051
|
-
catch() {
|
|
1052
|
-
return new MockPromise();
|
|
1053
|
-
}
|
|
1054
|
-
then() {
|
|
1055
|
-
return new MockPromise();
|
|
1056
|
-
}
|
|
1057
|
-
finally() {
|
|
1058
|
-
return new MockPromise();
|
|
702
|
+
// Guard against selector being an invalid CSS selector
|
|
703
|
+
try {
|
|
704
|
+
return document.querySelector(selector);
|
|
705
|
+
} catch (e) {
|
|
706
|
+
return null;
|
|
1059
707
|
}
|
|
1060
708
|
}
|
|
1061
|
-
function
|
|
1062
|
-
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
709
|
+
function createRouter(config) {
|
|
710
|
+
let ignore = false;
|
|
711
|
+
const wrap = value => typeof value === "string" ? {
|
|
712
|
+
value
|
|
713
|
+
} : value;
|
|
714
|
+
const signal = intercept(createSignal(wrap(config.get()), {
|
|
715
|
+
equals: (a, b) => a.value === b.value
|
|
716
|
+
}), undefined, next => {
|
|
717
|
+
!ignore && config.set(next);
|
|
718
|
+
return next;
|
|
719
|
+
});
|
|
720
|
+
config.init && onCleanup(config.init((value = config.get()) => {
|
|
721
|
+
ignore = true;
|
|
722
|
+
signal[1](wrap(value));
|
|
723
|
+
ignore = false;
|
|
724
|
+
}));
|
|
725
|
+
return createRouterComponent({
|
|
726
|
+
signal,
|
|
727
|
+
create: config.create,
|
|
728
|
+
utils: config.utils
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
function bindEvent(target, type, handler) {
|
|
732
|
+
target.addEventListener(type, handler);
|
|
733
|
+
return () => target.removeEventListener(type, handler);
|
|
734
|
+
}
|
|
735
|
+
function scrollToHash(hash, fallbackTop) {
|
|
736
|
+
const el = querySelector(`#${hash}`);
|
|
737
|
+
if (el) {
|
|
738
|
+
el.scrollIntoView();
|
|
739
|
+
} else if (fallbackTop) {
|
|
740
|
+
window.scrollTo(0, 0);
|
|
1072
741
|
}
|
|
1073
742
|
}
|
|
1074
743
|
|
|
744
|
+
function getPath(url) {
|
|
745
|
+
const u = new URL(url);
|
|
746
|
+
return u.pathname + u.search;
|
|
747
|
+
}
|
|
748
|
+
function StaticRouter(props) {
|
|
749
|
+
let e;
|
|
750
|
+
const obj = {
|
|
751
|
+
value: props.url || (e = getRequestEvent()) && getPath(e.request.url) || ""
|
|
752
|
+
};
|
|
753
|
+
return createRouterComponent({
|
|
754
|
+
signal: [() => obj, next => Object.assign(obj, next)]
|
|
755
|
+
})(props);
|
|
756
|
+
}
|
|
757
|
+
|
|
1075
758
|
const LocationHeader = "Location";
|
|
1076
759
|
const PRELOAD_TIMEOUT = 5000;
|
|
760
|
+
const CACHE_TIMEOUT = 180000;
|
|
1077
761
|
let cacheMap = new Map();
|
|
762
|
+
|
|
763
|
+
// cleanup forward/back cache
|
|
764
|
+
if (!isServer) {
|
|
765
|
+
setInterval(() => {
|
|
766
|
+
const now = Date.now();
|
|
767
|
+
for (let [k, v] of cacheMap.entries()) {
|
|
768
|
+
if (!v[3].size && now - v[0] > CACHE_TIMEOUT) {
|
|
769
|
+
cacheMap.delete(k);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}, 300000);
|
|
773
|
+
}
|
|
1078
774
|
function getCache() {
|
|
1079
775
|
if (!isServer) return cacheMap;
|
|
1080
776
|
const req = getRequestEvent() || sharedConfig.context;
|
|
1081
777
|
return req.routerCache || (req.routerCache = new Map());
|
|
1082
778
|
}
|
|
1083
779
|
function revalidate(key) {
|
|
780
|
+
key && !Array.isArray(key) && (key = [key]);
|
|
1084
781
|
return startTransition(() => {
|
|
1085
782
|
const now = Date.now();
|
|
1086
783
|
for (let k of cacheMap.keys()) {
|
|
1087
|
-
if (key === undefined || k
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
784
|
+
if (key === undefined || matchKey(k, key)) {
|
|
785
|
+
const entry = cacheMap.get(k);
|
|
786
|
+
entry[0] = 0; //force cache miss
|
|
787
|
+
revalidateSignals(entry[3], now); // retrigger live signals
|
|
1091
788
|
}
|
|
1092
789
|
}
|
|
1093
790
|
});
|
|
1094
791
|
}
|
|
792
|
+
|
|
1095
793
|
function revalidateSignals(set, time) {
|
|
1096
794
|
for (let s of set) s[1](time);
|
|
1097
795
|
}
|
|
1098
796
|
function cache(fn, name, options) {
|
|
1099
797
|
const [store, setStore] = createStore({});
|
|
1100
|
-
|
|
798
|
+
const cachedFn = (...args) => {
|
|
1101
799
|
const cache = getCache();
|
|
1102
800
|
const intent = getIntent();
|
|
1103
801
|
const owner = getOwner();
|
|
1104
802
|
const navigate = owner ? useNavigate() : undefined;
|
|
1105
803
|
const now = Date.now();
|
|
1106
|
-
const key = name + (args
|
|
804
|
+
const key = name + hashKey(args);
|
|
1107
805
|
let cached = cache.get(key);
|
|
1108
806
|
let version;
|
|
1109
807
|
if (owner) {
|
|
@@ -1172,8 +870,31 @@ function cache(fn, name, options) {
|
|
|
1172
870
|
};
|
|
1173
871
|
}
|
|
1174
872
|
};
|
|
873
|
+
cachedFn.keyFor = (...args) => name + hashKey(args);
|
|
874
|
+
cachedFn.key = name;
|
|
875
|
+
return cachedFn;
|
|
876
|
+
}
|
|
877
|
+
function matchKey(key, keys) {
|
|
878
|
+
for (let k of keys) {
|
|
879
|
+
if (key.startsWith(k)) return true;
|
|
880
|
+
}
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Modified from the amazing Tanstack Query library (MIT)
|
|
885
|
+
// https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168
|
|
886
|
+
function hashKey(args) {
|
|
887
|
+
return JSON.stringify(args, (_, val) => isPlainObject(val) ? Object.keys(val).sort().reduce((result, key) => {
|
|
888
|
+
result[key] = val[key];
|
|
889
|
+
return result;
|
|
890
|
+
}, {}) : val);
|
|
891
|
+
}
|
|
892
|
+
function isPlainObject(obj) {
|
|
893
|
+
let proto;
|
|
894
|
+
return obj != null && typeof obj === "object" && (!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype);
|
|
1175
895
|
}
|
|
1176
896
|
|
|
897
|
+
const actions = /* #__PURE__ */new Map();
|
|
1177
898
|
function useSubmissions(fn, filter) {
|
|
1178
899
|
const router = useRouter();
|
|
1179
900
|
const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.toString() && (!filter || filter(s.input))));
|
|
@@ -1187,26 +908,11 @@ function useSubmissions(fn, filter) {
|
|
|
1187
908
|
}
|
|
1188
909
|
function useSubmission(fn, filter) {
|
|
1189
910
|
const submissions = useSubmissions(fn, filter);
|
|
1190
|
-
return {
|
|
1191
|
-
get
|
|
1192
|
-
return submissions[submissions.length - 1]?.
|
|
1193
|
-
},
|
|
1194
|
-
get retry() {
|
|
1195
|
-
return submissions[submissions.length - 1]?.retry;
|
|
1196
|
-
},
|
|
1197
|
-
get url() {
|
|
1198
|
-
return submissions[submissions.length - 1]?.url;
|
|
1199
|
-
},
|
|
1200
|
-
get input() {
|
|
1201
|
-
return submissions[submissions.length - 1]?.input;
|
|
1202
|
-
},
|
|
1203
|
-
get result() {
|
|
1204
|
-
return submissions[submissions.length - 1]?.result;
|
|
1205
|
-
},
|
|
1206
|
-
get pending() {
|
|
1207
|
-
return submissions[submissions.length - 1]?.pending;
|
|
911
|
+
return new Proxy({}, {
|
|
912
|
+
get(_, property) {
|
|
913
|
+
return submissions[submissions.length - 1]?.[property];
|
|
1208
914
|
}
|
|
1209
|
-
};
|
|
915
|
+
});
|
|
1210
916
|
}
|
|
1211
917
|
function useAction(action) {
|
|
1212
918
|
const router = useRouter();
|
|
@@ -1252,35 +958,378 @@ function action(fn, name) {
|
|
|
1252
958
|
if (!url) throw new Error("Client Actions need explicit names if server rendered");
|
|
1253
959
|
return url;
|
|
1254
960
|
};
|
|
1255
|
-
if (!isServer)
|
|
961
|
+
if (!isServer) actions.set(url, mutate);
|
|
1256
962
|
return mutate;
|
|
1257
963
|
}
|
|
1258
964
|
async function handleResponse(response, navigate) {
|
|
1259
965
|
let data;
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
if (
|
|
1263
|
-
|
|
1264
|
-
}
|
|
1265
|
-
|
|
966
|
+
let keys;
|
|
967
|
+
if (response instanceof Response) {
|
|
968
|
+
if (response.headers.has("X-Revalidate")) {
|
|
969
|
+
keys = response.headers.get("X-Revalidate").split(",");
|
|
970
|
+
}
|
|
971
|
+
if (response.customBody) data = await response.customBody();
|
|
972
|
+
if (redirectStatusCodes.has(response.status)) {
|
|
973
|
+
const locationUrl = response.headers.get("Location") || "/";
|
|
974
|
+
if (locationUrl.startsWith("http")) {
|
|
975
|
+
window.location.href = locationUrl;
|
|
976
|
+
} else {
|
|
977
|
+
navigate(locationUrl);
|
|
978
|
+
}
|
|
1266
979
|
}
|
|
1267
980
|
} else data = response;
|
|
1268
|
-
|
|
1269
|
-
await revalidate();
|
|
981
|
+
await revalidate(keys);
|
|
1270
982
|
return data;
|
|
1271
983
|
}
|
|
1272
984
|
|
|
985
|
+
function setupNativeEvents(router) {
|
|
986
|
+
const basePath = router.base.path();
|
|
987
|
+
const navigateFromRoute = router.navigatorFactory(router.base);
|
|
988
|
+
let preloadTimeout = {};
|
|
989
|
+
function isSvg(el) {
|
|
990
|
+
return el.namespaceURI === "http://www.w3.org/2000/svg";
|
|
991
|
+
}
|
|
992
|
+
function handleAnchor(evt) {
|
|
993
|
+
if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
|
|
994
|
+
const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
|
|
995
|
+
if (!a) return;
|
|
996
|
+
const svg = isSvg(a);
|
|
997
|
+
const href = svg ? a.href.baseVal : a.href;
|
|
998
|
+
const target = svg ? a.target.baseVal : a.target;
|
|
999
|
+
if (target || !href && !a.hasAttribute("state")) return;
|
|
1000
|
+
const rel = (a.getAttribute("rel") || "").split(/\s+/);
|
|
1001
|
+
if (a.hasAttribute("download") || rel && rel.includes("external")) return;
|
|
1002
|
+
const url = svg ? new URL(href, document.baseURI) : new URL(href);
|
|
1003
|
+
if (url.origin !== window.location.origin || basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())) return;
|
|
1004
|
+
return [a, url];
|
|
1005
|
+
}
|
|
1006
|
+
function handleAnchorClick(evt) {
|
|
1007
|
+
const res = handleAnchor(evt);
|
|
1008
|
+
if (!res) return;
|
|
1009
|
+
const [a, url] = res;
|
|
1010
|
+
const to = router.parsePath(url.pathname + url.search + url.hash);
|
|
1011
|
+
const state = a.getAttribute("state");
|
|
1012
|
+
evt.preventDefault();
|
|
1013
|
+
navigateFromRoute(to, {
|
|
1014
|
+
resolve: false,
|
|
1015
|
+
replace: a.hasAttribute("replace"),
|
|
1016
|
+
scroll: !a.hasAttribute("noscroll"),
|
|
1017
|
+
state: state && JSON.parse(state)
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
function handleAnchorPreload(evt) {
|
|
1021
|
+
const res = handleAnchor(evt);
|
|
1022
|
+
if (!res) return;
|
|
1023
|
+
const [a, url] = res;
|
|
1024
|
+
if (!preloadTimeout[url.pathname]) router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
1025
|
+
}
|
|
1026
|
+
function handleAnchorIn(evt) {
|
|
1027
|
+
const res = handleAnchor(evt);
|
|
1028
|
+
if (!res) return;
|
|
1029
|
+
const [a, url] = res;
|
|
1030
|
+
if (preloadTimeout[url.pathname]) return;
|
|
1031
|
+
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
1032
|
+
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
1033
|
+
delete preloadTimeout[url.pathname];
|
|
1034
|
+
}, 200);
|
|
1035
|
+
}
|
|
1036
|
+
function handleAnchorOut(evt) {
|
|
1037
|
+
const res = handleAnchor(evt);
|
|
1038
|
+
if (!res) return;
|
|
1039
|
+
const [, url] = res;
|
|
1040
|
+
if (preloadTimeout[url.pathname]) {
|
|
1041
|
+
clearTimeout(preloadTimeout[url.pathname]);
|
|
1042
|
+
delete preloadTimeout[url.pathname];
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
function handleFormSubmit(evt) {
|
|
1046
|
+
let actionRef = evt.submitter && evt.submitter.getAttribute("formaction") || evt.target.action;
|
|
1047
|
+
if (!actionRef) return;
|
|
1048
|
+
if (!actionRef.startsWith("action:")) {
|
|
1049
|
+
const url = new URL(actionRef);
|
|
1050
|
+
actionRef = router.parsePath(url.pathname + url.search);
|
|
1051
|
+
if (!actionRef.startsWith(router.actionBase)) return;
|
|
1052
|
+
}
|
|
1053
|
+
const handler = actions.get(actionRef);
|
|
1054
|
+
if (handler) {
|
|
1055
|
+
evt.preventDefault();
|
|
1056
|
+
const data = new FormData(evt.target);
|
|
1057
|
+
handler.call(router, data);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// ensure delegated event run first
|
|
1062
|
+
delegateEvents(["click", "submit"]);
|
|
1063
|
+
document.addEventListener("click", handleAnchorClick);
|
|
1064
|
+
document.addEventListener("mouseover", handleAnchorIn);
|
|
1065
|
+
document.addEventListener("mouseout", handleAnchorOut);
|
|
1066
|
+
document.addEventListener("focusin", handleAnchorPreload);
|
|
1067
|
+
document.addEventListener("touchstart", handleAnchorPreload);
|
|
1068
|
+
document.addEventListener("submit", handleFormSubmit);
|
|
1069
|
+
onCleanup(() => {
|
|
1070
|
+
document.removeEventListener("click", handleAnchorClick);
|
|
1071
|
+
document.removeEventListener("mouseover", handleAnchorIn);
|
|
1072
|
+
document.removeEventListener("mouseout", handleAnchorOut);
|
|
1073
|
+
document.removeEventListener("focusin", handleAnchorPreload);
|
|
1074
|
+
document.removeEventListener("touchstart", handleAnchorPreload);
|
|
1075
|
+
document.removeEventListener("submit", handleFormSubmit);
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function Router(props) {
|
|
1080
|
+
if (isServer) return StaticRouter(props);
|
|
1081
|
+
return createRouter({
|
|
1082
|
+
get: () => ({
|
|
1083
|
+
value: window.location.pathname + window.location.search + window.location.hash,
|
|
1084
|
+
state: history.state
|
|
1085
|
+
}),
|
|
1086
|
+
set({
|
|
1087
|
+
value,
|
|
1088
|
+
replace,
|
|
1089
|
+
scroll,
|
|
1090
|
+
state
|
|
1091
|
+
}) {
|
|
1092
|
+
if (replace) {
|
|
1093
|
+
window.history.replaceState(state, "", value);
|
|
1094
|
+
} else {
|
|
1095
|
+
window.history.pushState(state, "", value);
|
|
1096
|
+
}
|
|
1097
|
+
scrollToHash(window.location.hash.slice(1), scroll);
|
|
1098
|
+
},
|
|
1099
|
+
init: notify => bindEvent(window, "popstate", () => notify()),
|
|
1100
|
+
create: setupNativeEvents,
|
|
1101
|
+
utils: {
|
|
1102
|
+
go: delta => window.history.go(delta)
|
|
1103
|
+
}
|
|
1104
|
+
})(props);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
function hashParser(str) {
|
|
1108
|
+
const to = str.replace(/^.*?#/, "");
|
|
1109
|
+
// Hash-only hrefs like `#foo` from plain anchors will come in as `/#foo` whereas a link to
|
|
1110
|
+
// `/foo` will be `/#/foo`. Check if the to starts with a `/` and if not append it as a hash
|
|
1111
|
+
// to the current path so we can handle these in-page anchors correctly.
|
|
1112
|
+
if (!to.startsWith("/")) {
|
|
1113
|
+
const [, path = "/"] = window.location.hash.split("#", 2);
|
|
1114
|
+
return `${path}#${to}`;
|
|
1115
|
+
}
|
|
1116
|
+
return to;
|
|
1117
|
+
}
|
|
1118
|
+
function HashRouter(props) {
|
|
1119
|
+
return createRouter({
|
|
1120
|
+
get: () => window.location.hash.slice(1),
|
|
1121
|
+
set({
|
|
1122
|
+
value,
|
|
1123
|
+
replace,
|
|
1124
|
+
scroll,
|
|
1125
|
+
state
|
|
1126
|
+
}) {
|
|
1127
|
+
if (replace) {
|
|
1128
|
+
window.history.replaceState(state, "", "#" + value);
|
|
1129
|
+
} else {
|
|
1130
|
+
window.location.hash = value;
|
|
1131
|
+
}
|
|
1132
|
+
const hashIndex = value.indexOf("#");
|
|
1133
|
+
const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
|
|
1134
|
+
scrollToHash(hash, scroll);
|
|
1135
|
+
},
|
|
1136
|
+
init: notify => bindEvent(window, "hashchange", () => notify()),
|
|
1137
|
+
create: setupNativeEvents,
|
|
1138
|
+
utils: {
|
|
1139
|
+
go: delta => window.history.go(delta),
|
|
1140
|
+
renderPath: path => `#${path}`,
|
|
1141
|
+
parsePath: hashParser
|
|
1142
|
+
}
|
|
1143
|
+
})(props);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function createMemoryHistory() {
|
|
1147
|
+
const entries = ["/"];
|
|
1148
|
+
let index = 0;
|
|
1149
|
+
const listeners = [];
|
|
1150
|
+
const go = n => {
|
|
1151
|
+
// https://github.com/remix-run/react-router/blob/682810ca929d0e3c64a76f8d6e465196b7a2ac58/packages/router/history.ts#L245
|
|
1152
|
+
index = Math.max(0, Math.min(index + n, entries.length - 1));
|
|
1153
|
+
const value = entries[index];
|
|
1154
|
+
listeners.forEach(listener => listener(value));
|
|
1155
|
+
};
|
|
1156
|
+
return {
|
|
1157
|
+
get: () => entries[index],
|
|
1158
|
+
set: ({
|
|
1159
|
+
value,
|
|
1160
|
+
scroll,
|
|
1161
|
+
replace
|
|
1162
|
+
}) => {
|
|
1163
|
+
if (replace) {
|
|
1164
|
+
entries[index] = value;
|
|
1165
|
+
} else {
|
|
1166
|
+
entries.splice(index + 1, entries.length - index, value);
|
|
1167
|
+
index++;
|
|
1168
|
+
}
|
|
1169
|
+
if (scroll) {
|
|
1170
|
+
scrollToHash(value.split("#")[1] || "", true);
|
|
1171
|
+
}
|
|
1172
|
+
},
|
|
1173
|
+
back: () => {
|
|
1174
|
+
go(-1);
|
|
1175
|
+
},
|
|
1176
|
+
forward: () => {
|
|
1177
|
+
go(1);
|
|
1178
|
+
},
|
|
1179
|
+
go,
|
|
1180
|
+
listen: listener => {
|
|
1181
|
+
listeners.push(listener);
|
|
1182
|
+
return () => {
|
|
1183
|
+
const index = listeners.indexOf(listener);
|
|
1184
|
+
listeners.splice(index, 1);
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
function MemoryRouter(props) {
|
|
1190
|
+
const memoryHistory = props.history || createMemoryHistory();
|
|
1191
|
+
return createRouter({
|
|
1192
|
+
get: memoryHistory.get,
|
|
1193
|
+
set: memoryHistory.set,
|
|
1194
|
+
init: memoryHistory.listen,
|
|
1195
|
+
utils: {
|
|
1196
|
+
go: memoryHistory.go
|
|
1197
|
+
}
|
|
1198
|
+
})(props);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
const _tmpl$ = /*#__PURE__*/template(`<a>`);
|
|
1202
|
+
function A(props) {
|
|
1203
|
+
props = mergeProps({
|
|
1204
|
+
inactiveClass: "inactive",
|
|
1205
|
+
activeClass: "active"
|
|
1206
|
+
}, props);
|
|
1207
|
+
const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
|
|
1208
|
+
const to = useResolvedPath(() => props.href);
|
|
1209
|
+
const href = useHref(to);
|
|
1210
|
+
const location = useLocation();
|
|
1211
|
+
const isActive = createMemo(() => {
|
|
1212
|
+
const to_ = to();
|
|
1213
|
+
if (to_ === undefined) return false;
|
|
1214
|
+
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
|
|
1215
|
+
const loc = normalizePath(location.pathname).toLowerCase();
|
|
1216
|
+
return props.end ? path === loc : loc.startsWith(path);
|
|
1217
|
+
});
|
|
1218
|
+
return (() => {
|
|
1219
|
+
const _el$ = _tmpl$();
|
|
1220
|
+
spread(_el$, mergeProps$1(rest, {
|
|
1221
|
+
get href() {
|
|
1222
|
+
return href() || props.href;
|
|
1223
|
+
},
|
|
1224
|
+
get state() {
|
|
1225
|
+
return JSON.stringify(props.state);
|
|
1226
|
+
},
|
|
1227
|
+
get classList() {
|
|
1228
|
+
return {
|
|
1229
|
+
...(props.class && {
|
|
1230
|
+
[props.class]: true
|
|
1231
|
+
}),
|
|
1232
|
+
[props.inactiveClass]: !isActive(),
|
|
1233
|
+
[props.activeClass]: isActive(),
|
|
1234
|
+
...rest.classList
|
|
1235
|
+
};
|
|
1236
|
+
},
|
|
1237
|
+
get ["aria-current"]() {
|
|
1238
|
+
return isActive() ? "page" : undefined;
|
|
1239
|
+
}
|
|
1240
|
+
}), false, false);
|
|
1241
|
+
return _el$;
|
|
1242
|
+
})();
|
|
1243
|
+
}
|
|
1244
|
+
function Navigate(props) {
|
|
1245
|
+
const navigate = useNavigate();
|
|
1246
|
+
const location = useLocation();
|
|
1247
|
+
const {
|
|
1248
|
+
href,
|
|
1249
|
+
state
|
|
1250
|
+
} = props;
|
|
1251
|
+
const path = typeof href === "function" ? href({
|
|
1252
|
+
navigate,
|
|
1253
|
+
location
|
|
1254
|
+
}) : href;
|
|
1255
|
+
navigate(path, {
|
|
1256
|
+
replace: true,
|
|
1257
|
+
state
|
|
1258
|
+
});
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
|
|
1264
|
+
*/
|
|
1265
|
+
function createAsync(fn, options) {
|
|
1266
|
+
const [resource] = createResource(() => subFetch(fn), v => v, options);
|
|
1267
|
+
return () => resource();
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// mock promise while hydrating to prevent fetching
|
|
1271
|
+
class MockPromise {
|
|
1272
|
+
static all() {
|
|
1273
|
+
return new MockPromise();
|
|
1274
|
+
}
|
|
1275
|
+
static allSettled() {
|
|
1276
|
+
return new MockPromise();
|
|
1277
|
+
}
|
|
1278
|
+
static any() {
|
|
1279
|
+
return new MockPromise();
|
|
1280
|
+
}
|
|
1281
|
+
static race() {
|
|
1282
|
+
return new MockPromise();
|
|
1283
|
+
}
|
|
1284
|
+
static reject() {
|
|
1285
|
+
return new MockPromise();
|
|
1286
|
+
}
|
|
1287
|
+
static resolve() {
|
|
1288
|
+
return new MockPromise();
|
|
1289
|
+
}
|
|
1290
|
+
catch() {
|
|
1291
|
+
return new MockPromise();
|
|
1292
|
+
}
|
|
1293
|
+
then() {
|
|
1294
|
+
return new MockPromise();
|
|
1295
|
+
}
|
|
1296
|
+
finally() {
|
|
1297
|
+
return new MockPromise();
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
function subFetch(fn) {
|
|
1301
|
+
if (isServer || !sharedConfig.context) return fn();
|
|
1302
|
+
const ogFetch = fetch;
|
|
1303
|
+
const ogPromise = Promise;
|
|
1304
|
+
try {
|
|
1305
|
+
window.fetch = () => new MockPromise();
|
|
1306
|
+
Promise = MockPromise;
|
|
1307
|
+
return fn();
|
|
1308
|
+
} finally {
|
|
1309
|
+
window.fetch = ogFetch;
|
|
1310
|
+
Promise = ogPromise;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1273
1314
|
function redirect(url, init = 302) {
|
|
1274
|
-
let responseInit
|
|
1275
|
-
|
|
1315
|
+
let responseInit;
|
|
1316
|
+
let revalidate;
|
|
1317
|
+
if (typeof init === "number") {
|
|
1276
1318
|
responseInit = {
|
|
1277
|
-
status:
|
|
1319
|
+
status: init
|
|
1278
1320
|
};
|
|
1279
|
-
} else
|
|
1280
|
-
|
|
1321
|
+
} else {
|
|
1322
|
+
({
|
|
1323
|
+
revalidate,
|
|
1324
|
+
...responseInit
|
|
1325
|
+
} = init);
|
|
1326
|
+
if (typeof responseInit.status === "undefined") {
|
|
1327
|
+
responseInit.status = 302;
|
|
1328
|
+
}
|
|
1281
1329
|
}
|
|
1282
1330
|
const headers = new Headers(responseInit.headers);
|
|
1283
1331
|
headers.set("Location", url);
|
|
1332
|
+
revalidate && headers.set("X-Revalidate", revalidate.toString());
|
|
1284
1333
|
const response = new Response(null, {
|
|
1285
1334
|
...responseInit,
|
|
1286
1335
|
headers: headers
|
|
@@ -1288,4 +1337,4 @@ function redirect(url, init = 302) {
|
|
|
1288
1337
|
return response;
|
|
1289
1338
|
}
|
|
1290
1339
|
|
|
1291
|
-
export { A,
|
|
1340
|
+
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, redirect, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|