@solidjs/router 0.9.1 → 0.10.0-beta.1
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 +323 -282
- package/dist/components.d.ts +7 -21
- package/dist/components.jsx +18 -30
- package/dist/data/action.d.ts +8 -0
- package/dist/data/action.js +113 -0
- package/dist/data/cache.d.ts +3 -0
- package/dist/data/cache.js +114 -0
- package/dist/data/createAsync.d.ts +5 -0
- package/dist/data/createAsync.js +54 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +441 -121
- package/dist/index.jsx +2 -1
- package/dist/routing.d.ts +8 -9
- package/dist/routing.js +159 -72
- package/dist/types.d.ts +26 -20
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isServer, delegateEvents, getRequestEvent, createComponent as createComponent$1, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
|
|
2
|
-
import { createSignal, onCleanup, getOwner, runWithOwner, createMemo, createContext, useContext, untrack, createRenderEffect,
|
|
2
|
+
import { createSignal, onCleanup, getOwner, runWithOwner, createMemo, createContext, useContext, untrack, createRenderEffect, on, startTransition, createComponent, resetErrorBoundaries, children, createRoot, Show, mergeProps, splitProps, createResource, sharedConfig, $TRACK } from 'solid-js';
|
|
3
|
+
import { createStore, reconcile } from 'solid-js/store';
|
|
3
4
|
|
|
4
5
|
function bindEvent(target, type, handler) {
|
|
5
6
|
target.addEventListener(type, handler);
|
|
@@ -201,6 +202,7 @@ function createBeforeLeave() {
|
|
|
201
202
|
|
|
202
203
|
const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
|
|
203
204
|
const trimPathRegex = /^\/+|(\/)\/+$/g;
|
|
205
|
+
const redirectStatusCodes = new Set([204, 301, 302, 303, 307, 308]);
|
|
204
206
|
function normalizePath(path, omitSlash = false) {
|
|
205
207
|
const s = path.replace(trimPathRegex, "$1");
|
|
206
208
|
return s ? omitSlash || /^[?#]/.test(s) ? s : "/" + s : "";
|
|
@@ -353,8 +355,7 @@ const MAX_REDIRECTS = 100;
|
|
|
353
355
|
const RouterContextObj = createContext();
|
|
354
356
|
const RouteContextObj = createContext();
|
|
355
357
|
const useRouter = () => invariant(useContext(RouterContextObj), "Make sure your app is wrapped in a <Router />");
|
|
356
|
-
|
|
357
|
-
const useRoute = () => TempRoute || useContext(RouteContextObj) || useRouter().base;
|
|
358
|
+
const useRoute = () => useContext(RouteContextObj) || useRouter().base;
|
|
358
359
|
const useResolvedPath = path => {
|
|
359
360
|
const route = useRoute();
|
|
360
361
|
return createMemo(() => route.resolvePath(path()));
|
|
@@ -380,7 +381,6 @@ const useMatch = (path, matchFilters) => {
|
|
|
380
381
|
});
|
|
381
382
|
};
|
|
382
383
|
const useParams = () => useRoute().params;
|
|
383
|
-
const useRouteData = () => useRoute().data;
|
|
384
384
|
const useSearchParams = () => {
|
|
385
385
|
const location = useLocation();
|
|
386
386
|
const navigate = useNavigate();
|
|
@@ -402,23 +402,17 @@ const useBeforeLeave = listener => {
|
|
|
402
402
|
});
|
|
403
403
|
onCleanup(s);
|
|
404
404
|
};
|
|
405
|
-
function createRoutes(routeDef, base = ""
|
|
405
|
+
function createRoutes(routeDef, base = "") {
|
|
406
406
|
const {
|
|
407
407
|
component,
|
|
408
|
-
|
|
408
|
+
load,
|
|
409
409
|
children
|
|
410
410
|
} = routeDef;
|
|
411
411
|
const isLeaf = !children || Array.isArray(children) && !children.length;
|
|
412
412
|
const shared = {
|
|
413
413
|
key: routeDef,
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
element
|
|
417
|
-
} = routeDef;
|
|
418
|
-
return element === undefined && fallback ? createComponent(fallback, {}) : element;
|
|
419
|
-
},
|
|
420
|
-
preload: routeDef.component ? component.preload : routeDef.preload,
|
|
421
|
-
data
|
|
414
|
+
component,
|
|
415
|
+
load
|
|
422
416
|
};
|
|
423
417
|
return asArray(routeDef.path).reduce((acc, path) => {
|
|
424
418
|
for (const originalPath of expandOptionals(path)) {
|
|
@@ -458,17 +452,18 @@ function createBranch(routes, index = 0) {
|
|
|
458
452
|
function asArray(value) {
|
|
459
453
|
return Array.isArray(value) ? value : [value];
|
|
460
454
|
}
|
|
461
|
-
function createBranches(routeDef, base = "",
|
|
455
|
+
function createBranches(routeDef, base = "", stack = [], branches = []) {
|
|
462
456
|
const routeDefs = asArray(routeDef);
|
|
463
457
|
for (let i = 0, len = routeDefs.length; i < len; i++) {
|
|
464
458
|
const def = routeDefs[i];
|
|
465
|
-
if (def && typeof def === "object"
|
|
466
|
-
|
|
459
|
+
if (def && typeof def === "object") {
|
|
460
|
+
if (!def.hasOwnProperty("path")) def.path = "";
|
|
461
|
+
const routes = createRoutes(def, base);
|
|
467
462
|
for (const route of routes) {
|
|
468
463
|
stack.push(route);
|
|
469
464
|
const isEmptyArray = Array.isArray(def.children) && def.children.length === 0;
|
|
470
465
|
if (def.children && !isEmptyArray) {
|
|
471
|
-
createBranches(def.children, route.pattern,
|
|
466
|
+
createBranches(def.children, route.pattern, stack, branches);
|
|
472
467
|
} else {
|
|
473
468
|
const branch = createBranch([...stack], branches.length);
|
|
474
469
|
branches.push(branch);
|
|
@@ -506,7 +501,7 @@ function createLocation(path, state) {
|
|
|
506
501
|
const pathname = createMemo(() => url().pathname);
|
|
507
502
|
const search = createMemo(() => url().search, true);
|
|
508
503
|
const hash = createMemo(() => url().hash);
|
|
509
|
-
const key =
|
|
504
|
+
const key = () => "";
|
|
510
505
|
return {
|
|
511
506
|
get pathname() {
|
|
512
507
|
return pathname();
|
|
@@ -526,7 +521,15 @@ function createLocation(path, state) {
|
|
|
526
521
|
query: createMemoObject(on(search, () => extractSearchParams(url())))
|
|
527
522
|
};
|
|
528
523
|
}
|
|
529
|
-
|
|
524
|
+
const actions = new Map();
|
|
525
|
+
function registerAction(url, fn) {
|
|
526
|
+
actions.set(url, fn);
|
|
527
|
+
}
|
|
528
|
+
let intent;
|
|
529
|
+
function getIntent() {
|
|
530
|
+
return intent;
|
|
531
|
+
}
|
|
532
|
+
function createRouterContext(integration, getBranches, base = "") {
|
|
530
533
|
const {
|
|
531
534
|
signal: [source, setSource],
|
|
532
535
|
utils = {}
|
|
@@ -534,11 +537,8 @@ function createRouterContext(integration, base = "", data, out) {
|
|
|
534
537
|
const parsePath = utils.parsePath || (p => p);
|
|
535
538
|
const renderPath = utils.renderPath || (p => p);
|
|
536
539
|
const beforeLeave = utils.beforeLeave || createBeforeLeave();
|
|
540
|
+
let submissions = [];
|
|
537
541
|
const basePath = resolvePath("", base);
|
|
538
|
-
const output = isServer && out ? Object.assign(out, {
|
|
539
|
-
matches: [],
|
|
540
|
-
url: undefined
|
|
541
|
-
}) : undefined;
|
|
542
542
|
if (basePath === undefined) {
|
|
543
543
|
throw new Error(`${basePath} is not a valid base path`);
|
|
544
544
|
} else if (basePath && !source().value) {
|
|
@@ -570,19 +570,6 @@ function createRouterContext(integration, base = "", data, out) {
|
|
|
570
570
|
return resolvePath(basePath, to);
|
|
571
571
|
}
|
|
572
572
|
};
|
|
573
|
-
if (data) {
|
|
574
|
-
try {
|
|
575
|
-
TempRoute = baseRoute;
|
|
576
|
-
baseRoute.data = data({
|
|
577
|
-
data: undefined,
|
|
578
|
-
params: {},
|
|
579
|
-
location,
|
|
580
|
-
navigate: navigatorFactory(baseRoute)
|
|
581
|
-
});
|
|
582
|
-
} finally {
|
|
583
|
-
TempRoute = undefined;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
573
|
function navigateFromRoute(route, to, options) {
|
|
587
574
|
// Untrack in case someone navigates in an effect - don't want to track `reference` or route paths
|
|
588
575
|
untrack(() => {
|
|
@@ -614,9 +601,6 @@ function createRouterContext(integration, base = "", data, out) {
|
|
|
614
601
|
const current = reference();
|
|
615
602
|
if (resolvedTo !== current || nextState !== state()) {
|
|
616
603
|
if (isServer) {
|
|
617
|
-
if (output) {
|
|
618
|
-
output.url = resolvedTo;
|
|
619
|
-
}
|
|
620
604
|
const e = getRequestEvent();
|
|
621
605
|
e && (e.response = Response.redirect(resolvedTo, 302));
|
|
622
606
|
setSource({
|
|
@@ -633,11 +617,13 @@ function createRouterContext(integration, base = "", data, out) {
|
|
|
633
617
|
state: state()
|
|
634
618
|
});
|
|
635
619
|
start(() => {
|
|
620
|
+
intent = "navigate";
|
|
636
621
|
setReference(resolvedTo);
|
|
637
622
|
setState(nextState);
|
|
638
623
|
resetErrorBoundaries();
|
|
639
624
|
}).then(() => {
|
|
640
625
|
if (referrers.length === len) {
|
|
626
|
+
intent = undefined;
|
|
641
627
|
navigateEnd({
|
|
642
628
|
value: resolvedTo,
|
|
643
629
|
state: nextState
|
|
@@ -675,23 +661,38 @@ function createRouterContext(integration, base = "", data, out) {
|
|
|
675
661
|
untrack(() => {
|
|
676
662
|
if (value !== reference()) {
|
|
677
663
|
start(() => {
|
|
664
|
+
intent = "native";
|
|
678
665
|
setReference(value);
|
|
679
666
|
setState(state);
|
|
667
|
+
}).then(() => {
|
|
668
|
+
intent = undefined;
|
|
680
669
|
});
|
|
681
670
|
}
|
|
682
671
|
});
|
|
683
672
|
});
|
|
684
673
|
if (!isServer) {
|
|
685
|
-
|
|
674
|
+
let preloadTimeout = {};
|
|
675
|
+
function isSvg(el) {
|
|
676
|
+
return el.namespaceURI === "http://www.w3.org/2000/svg";
|
|
677
|
+
}
|
|
678
|
+
function handleAnchor(evt) {
|
|
686
679
|
if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
|
|
687
680
|
const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
|
|
688
|
-
if (!a
|
|
689
|
-
const
|
|
690
|
-
|
|
681
|
+
if (!a) return;
|
|
682
|
+
const svg = isSvg(a);
|
|
683
|
+
const href = svg ? a.href.baseVal : a.href;
|
|
684
|
+
const target = svg ? a.target.baseVal : a.target;
|
|
685
|
+
if (target || !href && !a.hasAttribute("state")) return;
|
|
691
686
|
const rel = (a.getAttribute("rel") || "").split(/\s+/);
|
|
692
687
|
if (a.hasAttribute("download") || rel && rel.includes("external")) return;
|
|
693
|
-
const url = new URL(href);
|
|
688
|
+
const url = svg ? new URL(href, document.baseURI) : new URL(href);
|
|
694
689
|
if (url.origin !== window.location.origin || basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())) return;
|
|
690
|
+
return [a, url];
|
|
691
|
+
}
|
|
692
|
+
function handleAnchorClick(evt) {
|
|
693
|
+
const res = handleAnchor(evt);
|
|
694
|
+
if (!res) return;
|
|
695
|
+
const [a, url] = res;
|
|
695
696
|
const to = parsePath(url.pathname + url.search + url.hash);
|
|
696
697
|
const state = a.getAttribute("state");
|
|
697
698
|
evt.preventDefault();
|
|
@@ -702,94 +703,175 @@ function createRouterContext(integration, base = "", data, out) {
|
|
|
702
703
|
state: state && JSON.parse(state)
|
|
703
704
|
});
|
|
704
705
|
}
|
|
706
|
+
function doPreload(a, url) {
|
|
707
|
+
const preload = a.getAttribute("preload") !== "false";
|
|
708
|
+
const matches = getRouteMatches(getBranches(), url.pathname);
|
|
709
|
+
const prevIntent = intent;
|
|
710
|
+
intent = "preload";
|
|
711
|
+
for (let match in matches) {
|
|
712
|
+
const {
|
|
713
|
+
route,
|
|
714
|
+
params
|
|
715
|
+
} = matches[match];
|
|
716
|
+
route.component && route.component.preload && route.component.preload();
|
|
717
|
+
preload && route.load && route.load({
|
|
718
|
+
params,
|
|
719
|
+
location: {
|
|
720
|
+
pathname: url.pathname,
|
|
721
|
+
search: url.search,
|
|
722
|
+
hash: url.hash,
|
|
723
|
+
query: extractSearchParams(url),
|
|
724
|
+
state: null,
|
|
725
|
+
key: ""
|
|
726
|
+
},
|
|
727
|
+
intent
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
intent = prevIntent;
|
|
731
|
+
}
|
|
732
|
+
function handleAnchorPreload(evt) {
|
|
733
|
+
const res = handleAnchor(evt);
|
|
734
|
+
if (!res) return;
|
|
735
|
+
const [a, url] = res;
|
|
736
|
+
if (!preloadTimeout[url.pathname]) doPreload(a, url);
|
|
737
|
+
}
|
|
738
|
+
function handleAnchorIn(evt) {
|
|
739
|
+
const res = handleAnchor(evt);
|
|
740
|
+
if (!res) return;
|
|
741
|
+
const [a, url] = res;
|
|
742
|
+
if (preloadTimeout[url.pathname]) return;
|
|
743
|
+
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
744
|
+
doPreload(a, url);
|
|
745
|
+
delete preloadTimeout[url.pathname];
|
|
746
|
+
}, 200);
|
|
747
|
+
}
|
|
748
|
+
function handleAnchorOut(evt) {
|
|
749
|
+
const res = handleAnchor(evt);
|
|
750
|
+
if (!res) return;
|
|
751
|
+
const [, url] = res;
|
|
752
|
+
if (preloadTimeout[url.pathname]) {
|
|
753
|
+
clearTimeout(preloadTimeout[url.pathname]);
|
|
754
|
+
delete preloadTimeout[url.pathname];
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function handleFormSubmit(evt) {
|
|
758
|
+
let actionRef = evt.submitter && evt.submitter.getAttribute("formaction") || evt.target.action;
|
|
759
|
+
if (actionRef && actionRef.startsWith("action:")) {
|
|
760
|
+
const data = new FormData(evt.target);
|
|
761
|
+
actions.get(actionRef.slice(7))(data);
|
|
762
|
+
evt.preventDefault();
|
|
763
|
+
}
|
|
764
|
+
}
|
|
705
765
|
|
|
706
|
-
// ensure delegated
|
|
707
|
-
delegateEvents(["click"]);
|
|
766
|
+
// ensure delegated event run first
|
|
767
|
+
delegateEvents(["click", "submit"]);
|
|
708
768
|
document.addEventListener("click", handleAnchorClick);
|
|
709
|
-
|
|
769
|
+
document.addEventListener("mouseover", handleAnchorIn);
|
|
770
|
+
document.addEventListener("mouseout", handleAnchorOut);
|
|
771
|
+
document.addEventListener("focusin", handleAnchorPreload);
|
|
772
|
+
document.addEventListener("touchstart", handleAnchorPreload);
|
|
773
|
+
document.addEventListener("submit", handleFormSubmit);
|
|
774
|
+
onCleanup(() => {
|
|
775
|
+
document.removeEventListener("click", handleAnchorClick);
|
|
776
|
+
document.removeEventListener("mouseover", handleAnchorIn);
|
|
777
|
+
document.removeEventListener("mouseout", handleAnchorOut);
|
|
778
|
+
document.removeEventListener("focusin", handleAnchorPreload);
|
|
779
|
+
document.removeEventListener("touchstart", handleAnchorPreload);
|
|
780
|
+
document.removeEventListener("submit", handleFormSubmit);
|
|
781
|
+
});
|
|
782
|
+
} else {
|
|
783
|
+
function initFromFlash(params) {
|
|
784
|
+
let param = params.form ? JSON.parse(params.form) : null;
|
|
785
|
+
if (!param || !param.result) {
|
|
786
|
+
return [];
|
|
787
|
+
}
|
|
788
|
+
const input = new Map(param.entries);
|
|
789
|
+
return [{
|
|
790
|
+
url: param.url,
|
|
791
|
+
result: param.error ? new Error(param.result.message) : param.result,
|
|
792
|
+
input: input
|
|
793
|
+
}];
|
|
794
|
+
}
|
|
795
|
+
submissions = initFromFlash(location.query);
|
|
710
796
|
}
|
|
711
797
|
return {
|
|
712
798
|
base: baseRoute,
|
|
713
|
-
out: output,
|
|
714
799
|
location,
|
|
715
800
|
isRouting,
|
|
716
801
|
renderPath,
|
|
717
802
|
parsePath,
|
|
718
803
|
navigatorFactory,
|
|
719
|
-
beforeLeave
|
|
804
|
+
beforeLeave,
|
|
805
|
+
submissions: createSignal(submissions)
|
|
720
806
|
};
|
|
721
807
|
}
|
|
722
|
-
function createRouteContext(router, parent,
|
|
808
|
+
function createRouteContext(router, parent, outlet, match, params) {
|
|
723
809
|
const {
|
|
724
810
|
base,
|
|
725
|
-
location
|
|
726
|
-
navigatorFactory
|
|
811
|
+
location
|
|
727
812
|
} = router;
|
|
728
813
|
const {
|
|
729
814
|
pattern,
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
data
|
|
815
|
+
component,
|
|
816
|
+
load
|
|
733
817
|
} = match().route;
|
|
734
818
|
const path = createMemo(() => match().path);
|
|
735
|
-
preload && preload();
|
|
736
819
|
const route = {
|
|
737
820
|
parent,
|
|
738
821
|
pattern,
|
|
739
|
-
get child() {
|
|
740
|
-
return child();
|
|
741
|
-
},
|
|
742
822
|
path,
|
|
743
823
|
params,
|
|
744
|
-
|
|
745
|
-
|
|
824
|
+
outlet: () => component ? createComponent(component, {
|
|
825
|
+
params,
|
|
826
|
+
location,
|
|
827
|
+
get children() {
|
|
828
|
+
return outlet();
|
|
829
|
+
}
|
|
830
|
+
}) : outlet(),
|
|
746
831
|
resolvePath(to) {
|
|
747
832
|
return resolvePath(base.path(), to, path());
|
|
748
833
|
}
|
|
749
834
|
};
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
location,
|
|
757
|
-
navigate: navigatorFactory(route)
|
|
758
|
-
});
|
|
759
|
-
} finally {
|
|
760
|
-
TempRoute = undefined;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
835
|
+
component && component.preload && component.preload();
|
|
836
|
+
load && load({
|
|
837
|
+
params,
|
|
838
|
+
location,
|
|
839
|
+
intent: intent || "navigate"
|
|
840
|
+
});
|
|
763
841
|
return route;
|
|
764
842
|
}
|
|
765
843
|
|
|
766
|
-
const _tmpl$ = /*#__PURE__*/template(`<a
|
|
844
|
+
const _tmpl$ = /*#__PURE__*/template(`<a></a>`, 2);
|
|
767
845
|
const Router = props => {
|
|
768
846
|
let e;
|
|
769
847
|
const {
|
|
770
848
|
source,
|
|
771
849
|
url,
|
|
772
|
-
base
|
|
773
|
-
data,
|
|
774
|
-
out
|
|
850
|
+
base
|
|
775
851
|
} = props;
|
|
776
852
|
const integration = source || (isServer ? staticIntegration({
|
|
777
853
|
value: url || (e = getRequestEvent()) && e.request.url || ""
|
|
778
854
|
}) : pathIntegration());
|
|
779
|
-
const
|
|
855
|
+
const routeDefs = children(() => props.children);
|
|
856
|
+
const branches = createMemo(() => createBranches(props.root ? {
|
|
857
|
+
component: props.root,
|
|
858
|
+
children: routeDefs()
|
|
859
|
+
} : routeDefs(), props.base || ""));
|
|
860
|
+
const routerState = createRouterContext(integration, branches, base);
|
|
780
861
|
return createComponent$1(RouterContextObj.Provider, {
|
|
781
862
|
value: routerState,
|
|
782
863
|
get children() {
|
|
783
|
-
return
|
|
864
|
+
return createComponent$1(Routes, {
|
|
865
|
+
routerState: routerState,
|
|
866
|
+
get branches() {
|
|
867
|
+
return branches();
|
|
868
|
+
}
|
|
869
|
+
});
|
|
784
870
|
}
|
|
785
871
|
});
|
|
786
872
|
};
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
const parentRoute = useRoute();
|
|
790
|
-
const routeDefs = children(() => props.children);
|
|
791
|
-
const branches = createMemo(() => createBranches(routeDefs(), joinPaths(parentRoute.pattern, props.base || ""), Outlet));
|
|
792
|
-
const matches = createMemo(() => getRouteMatches(branches(), router.location.pathname));
|
|
873
|
+
function Routes(props) {
|
|
874
|
+
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
793
875
|
const params = createMemoObject(() => {
|
|
794
876
|
const m = matches();
|
|
795
877
|
const params = {};
|
|
@@ -798,18 +880,6 @@ const Routes = props => {
|
|
|
798
880
|
}
|
|
799
881
|
return params;
|
|
800
882
|
});
|
|
801
|
-
if (router.out) {
|
|
802
|
-
router.out.matches.push(matches().map(({
|
|
803
|
-
route,
|
|
804
|
-
path,
|
|
805
|
-
params
|
|
806
|
-
}) => ({
|
|
807
|
-
originalPath: route.originalPath,
|
|
808
|
-
pattern: route.pattern,
|
|
809
|
-
path,
|
|
810
|
-
params
|
|
811
|
-
})));
|
|
812
|
-
}
|
|
813
883
|
const disposers = [];
|
|
814
884
|
let root;
|
|
815
885
|
const routeStates = createMemo(on(matches, (nextMatches, prevMatches, prev) => {
|
|
@@ -827,7 +897,7 @@ const Routes = props => {
|
|
|
827
897
|
}
|
|
828
898
|
createRoot(dispose => {
|
|
829
899
|
disposers[i] = dispose;
|
|
830
|
-
next[i] = createRouteContext(
|
|
900
|
+
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => matches()[i], params);
|
|
831
901
|
});
|
|
832
902
|
}
|
|
833
903
|
}
|
|
@@ -850,26 +920,11 @@ const Routes = props => {
|
|
|
850
920
|
}
|
|
851
921
|
})
|
|
852
922
|
});
|
|
853
|
-
}
|
|
854
|
-
const
|
|
855
|
-
return () => createComponent$1(
|
|
856
|
-
base: base,
|
|
857
|
-
children: routes
|
|
858
|
-
});
|
|
859
|
-
};
|
|
860
|
-
const Route = props => {
|
|
861
|
-
const childRoutes = children(() => props.children);
|
|
862
|
-
return mergeProps(props, {
|
|
863
|
-
get children() {
|
|
864
|
-
return childRoutes();
|
|
865
|
-
}
|
|
866
|
-
});
|
|
867
|
-
};
|
|
868
|
-
const Outlet = () => {
|
|
869
|
-
const route = useRoute();
|
|
870
|
-
return createComponent$1(Show, {
|
|
923
|
+
}
|
|
924
|
+
const createOutlet = child => {
|
|
925
|
+
return () => createComponent$1(Show, {
|
|
871
926
|
get when() {
|
|
872
|
-
return
|
|
927
|
+
return child();
|
|
873
928
|
},
|
|
874
929
|
keyed: true,
|
|
875
930
|
children: child => createComponent$1(RouteContextObj.Provider, {
|
|
@@ -880,6 +935,14 @@ const Outlet = () => {
|
|
|
880
935
|
})
|
|
881
936
|
});
|
|
882
937
|
};
|
|
938
|
+
const Route = props => {
|
|
939
|
+
const childRoutes = children(() => props.children);
|
|
940
|
+
return mergeProps(props, {
|
|
941
|
+
get children() {
|
|
942
|
+
return childRoutes();
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
};
|
|
883
946
|
function A(props) {
|
|
884
947
|
props = mergeProps({
|
|
885
948
|
inactiveClass: "inactive",
|
|
@@ -897,7 +960,7 @@ function A(props) {
|
|
|
897
960
|
return props.end ? path === loc : loc.startsWith(path);
|
|
898
961
|
});
|
|
899
962
|
return (() => {
|
|
900
|
-
const _el$ = _tmpl
|
|
963
|
+
const _el$ = _tmpl$.cloneNode(true);
|
|
901
964
|
spread(_el$, mergeProps$1(rest, {
|
|
902
965
|
get href() {
|
|
903
966
|
return href() || props.href;
|
|
@@ -940,4 +1003,261 @@ function Navigate(props) {
|
|
|
940
1003
|
return null;
|
|
941
1004
|
}
|
|
942
1005
|
|
|
943
|
-
|
|
1006
|
+
/**
|
|
1007
|
+
* This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
|
|
1008
|
+
*/
|
|
1009
|
+
function createAsync(fn) {
|
|
1010
|
+
const [resource] = createResource(() => subFetch(fn), v => v);
|
|
1011
|
+
return () => resource();
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// mock promise while hydrating to prevent fetching
|
|
1015
|
+
class MockPromise {
|
|
1016
|
+
static all() {
|
|
1017
|
+
return new MockPromise();
|
|
1018
|
+
}
|
|
1019
|
+
static allSettled() {
|
|
1020
|
+
return new MockPromise();
|
|
1021
|
+
}
|
|
1022
|
+
static any() {
|
|
1023
|
+
return new MockPromise();
|
|
1024
|
+
}
|
|
1025
|
+
static race() {
|
|
1026
|
+
return new MockPromise();
|
|
1027
|
+
}
|
|
1028
|
+
static reject() {
|
|
1029
|
+
return new MockPromise();
|
|
1030
|
+
}
|
|
1031
|
+
static resolve() {
|
|
1032
|
+
return new MockPromise();
|
|
1033
|
+
}
|
|
1034
|
+
catch() {
|
|
1035
|
+
return new MockPromise();
|
|
1036
|
+
}
|
|
1037
|
+
then() {
|
|
1038
|
+
return new MockPromise();
|
|
1039
|
+
}
|
|
1040
|
+
finally() {
|
|
1041
|
+
return new MockPromise();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
function subFetch(fn) {
|
|
1045
|
+
if (isServer || !sharedConfig.context) return fn();
|
|
1046
|
+
const ogFetch = fetch;
|
|
1047
|
+
const ogPromise = Promise;
|
|
1048
|
+
try {
|
|
1049
|
+
window.fetch = () => new MockPromise();
|
|
1050
|
+
Promise = MockPromise;
|
|
1051
|
+
return fn();
|
|
1052
|
+
} finally {
|
|
1053
|
+
window.fetch = ogFetch;
|
|
1054
|
+
Promise = ogPromise;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const LocationHeader = "Location";
|
|
1059
|
+
const PRELOAD_TIMEOUT = 5000;
|
|
1060
|
+
let cacheMap = new Map();
|
|
1061
|
+
function getCache() {
|
|
1062
|
+
if (!isServer) return cacheMap;
|
|
1063
|
+
const req = getRequestEvent() || sharedConfig.context;
|
|
1064
|
+
return req.routerCache || (req.routerCache = new Map());
|
|
1065
|
+
}
|
|
1066
|
+
function revalidate(key) {
|
|
1067
|
+
return startTransition(() => {
|
|
1068
|
+
const now = Date.now();
|
|
1069
|
+
for (let k of cacheMap.keys()) {
|
|
1070
|
+
if (key === undefined || k === key) {
|
|
1071
|
+
const set = cacheMap.get(k)[3];
|
|
1072
|
+
revalidateSignals(set, now);
|
|
1073
|
+
cacheMap.delete(k);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
function revalidateSignals(set, time) {
|
|
1079
|
+
for (let s of set) s[1](time);
|
|
1080
|
+
}
|
|
1081
|
+
function cache(fn, name, options) {
|
|
1082
|
+
const [store, setStore] = createStore({});
|
|
1083
|
+
return (...args) => {
|
|
1084
|
+
const cache = getCache();
|
|
1085
|
+
const intent = getIntent();
|
|
1086
|
+
const owner = getOwner();
|
|
1087
|
+
const navigate = owner ? useNavigate() : undefined;
|
|
1088
|
+
const now = Date.now();
|
|
1089
|
+
const key = name + (args.length ? ":" + args.join(":") : "");
|
|
1090
|
+
let cached = cache.get(key);
|
|
1091
|
+
let version;
|
|
1092
|
+
if (owner) {
|
|
1093
|
+
version = createSignal(now, {
|
|
1094
|
+
equals: (p, v) => v - p < 50 // margin of error
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
onCleanup(() => cached[3].delete(version));
|
|
1098
|
+
version[0](); // track it;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (cached && (isServer || intent === "native" || Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
|
|
1102
|
+
version && cached[3].add(version);
|
|
1103
|
+
if (cached[2] === "preload" && intent !== "preload") {
|
|
1104
|
+
cached[0] = now;
|
|
1105
|
+
cached[1] = "then" in cached[1] ? cached[1].then(handleResponse) : handleResponse(cached[1]);
|
|
1106
|
+
cached[2] = intent;
|
|
1107
|
+
}
|
|
1108
|
+
if (!isServer && intent === "navigate") {
|
|
1109
|
+
startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return cached[1];
|
|
1113
|
+
}
|
|
1114
|
+
let res = !isServer && sharedConfig.context && sharedConfig.load ? sharedConfig.load(key) // hydrating
|
|
1115
|
+
: fn(...args);
|
|
1116
|
+
|
|
1117
|
+
// serialize on server
|
|
1118
|
+
if (isServer && sharedConfig.context && !sharedConfig.context.noHydrate) {
|
|
1119
|
+
sharedConfig.context && sharedConfig.context.serialize(key, res);
|
|
1120
|
+
}
|
|
1121
|
+
if (intent !== "preload") {
|
|
1122
|
+
res = "then" in res ? res.then(handleResponse) : handleResponse(res);
|
|
1123
|
+
}
|
|
1124
|
+
if (cached) {
|
|
1125
|
+
cached[0] = now;
|
|
1126
|
+
cached[1] = res;
|
|
1127
|
+
cached[2] = intent;
|
|
1128
|
+
version && cached[3].add(version);
|
|
1129
|
+
if (!isServer && intent === "navigate") {
|
|
1130
|
+
startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
|
|
1131
|
+
}
|
|
1132
|
+
} else cache.set(key, cached = [now, res, intent, new Set(version ? [version] : [])]);
|
|
1133
|
+
return res;
|
|
1134
|
+
function handleRedirect(response) {
|
|
1135
|
+
startTransition(() => {
|
|
1136
|
+
let url = response.headers.get(LocationHeader);
|
|
1137
|
+
if (url && url.startsWith("/")) {
|
|
1138
|
+
navigate(url, {
|
|
1139
|
+
replace: true
|
|
1140
|
+
});
|
|
1141
|
+
} else if (!isServer && url) {
|
|
1142
|
+
window.location.href = url;
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
function handleResponse(v) {
|
|
1147
|
+
if (v instanceof Response && redirectStatusCodes.has(v.status)) {
|
|
1148
|
+
if (navigate) isServer ? handleRedirect(v) : setTimeout(() => handleRedirect(v), 0);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (isServer) return v;
|
|
1152
|
+
setStore(key, reconcile(v, options));
|
|
1153
|
+
return store[key];
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function useSubmissions(fn, filter) {
|
|
1159
|
+
const router = useRouter();
|
|
1160
|
+
const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.toString() && (!filter || filter(s.input))));
|
|
1161
|
+
return new Proxy([], {
|
|
1162
|
+
get(_, property) {
|
|
1163
|
+
if (property === $TRACK) return subs();
|
|
1164
|
+
if (property === "pending") return subs().some(sub => !sub.result);
|
|
1165
|
+
return subs()[property];
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
function useSubmission(fn, filter) {
|
|
1170
|
+
const submissions = useSubmissions(fn, filter);
|
|
1171
|
+
return {
|
|
1172
|
+
get clear() {
|
|
1173
|
+
return submissions[0]?.clear;
|
|
1174
|
+
},
|
|
1175
|
+
get retry() {
|
|
1176
|
+
return submissions[0]?.retry;
|
|
1177
|
+
},
|
|
1178
|
+
get url() {
|
|
1179
|
+
return submissions[0]?.url;
|
|
1180
|
+
},
|
|
1181
|
+
get input() {
|
|
1182
|
+
return submissions[0]?.input;
|
|
1183
|
+
},
|
|
1184
|
+
get result() {
|
|
1185
|
+
return submissions[0]?.result;
|
|
1186
|
+
},
|
|
1187
|
+
get pending() {
|
|
1188
|
+
return submissions[0]?.pending;
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
function action(fn, name) {
|
|
1193
|
+
function mutate(variables) {
|
|
1194
|
+
const p = fn(variables);
|
|
1195
|
+
const [result, setResult] = createSignal();
|
|
1196
|
+
let submission;
|
|
1197
|
+
const router = this;
|
|
1198
|
+
router.submissions[1](s => [...s, submission = {
|
|
1199
|
+
input: variables,
|
|
1200
|
+
url,
|
|
1201
|
+
get result() {
|
|
1202
|
+
return result()?.data;
|
|
1203
|
+
},
|
|
1204
|
+
get pending() {
|
|
1205
|
+
return !result();
|
|
1206
|
+
},
|
|
1207
|
+
clear() {
|
|
1208
|
+
router.submissions[1](v => v.filter(i => i.input !== variables));
|
|
1209
|
+
},
|
|
1210
|
+
retry() {
|
|
1211
|
+
setResult(undefined);
|
|
1212
|
+
const p = fn(variables);
|
|
1213
|
+
p.then(async data => {
|
|
1214
|
+
const keys = handleResponse(data, router.navigatorFactory());
|
|
1215
|
+
await revalidate(keys);
|
|
1216
|
+
data ? setResult({
|
|
1217
|
+
data
|
|
1218
|
+
}) : submission.clear();
|
|
1219
|
+
return data;
|
|
1220
|
+
}).catch(error => {
|
|
1221
|
+
setResult({
|
|
1222
|
+
data: error
|
|
1223
|
+
});
|
|
1224
|
+
});
|
|
1225
|
+
return p;
|
|
1226
|
+
}
|
|
1227
|
+
}]);
|
|
1228
|
+
p.then(async data => {
|
|
1229
|
+
const keys = handleResponse(data, router.navigatorFactory());
|
|
1230
|
+
await revalidate(keys);
|
|
1231
|
+
data ? setResult({
|
|
1232
|
+
data
|
|
1233
|
+
}) : submission.clear();
|
|
1234
|
+
return data;
|
|
1235
|
+
}).catch(error => {
|
|
1236
|
+
setResult({
|
|
1237
|
+
data: error
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1240
|
+
return p;
|
|
1241
|
+
}
|
|
1242
|
+
const url = fn.url || `action:${name}` || !isServer ? `action:${fn.name}` : "";
|
|
1243
|
+
mutate.toString = () => {
|
|
1244
|
+
if (!url) throw new Error("Client Actions need explicit names if server rendered");
|
|
1245
|
+
return url;
|
|
1246
|
+
};
|
|
1247
|
+
if (!isServer) registerAction(url, mutate);
|
|
1248
|
+
return mutate;
|
|
1249
|
+
}
|
|
1250
|
+
function handleResponse(response, navigate) {
|
|
1251
|
+
if (response instanceof Response && redirectStatusCodes.has(response.status)) {
|
|
1252
|
+
const locationUrl = response.headers.get("Location") || "/";
|
|
1253
|
+
if (locationUrl.startsWith("http")) {
|
|
1254
|
+
window.location.href = locationUrl;
|
|
1255
|
+
} else {
|
|
1256
|
+
navigate(locationUrl);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// return keys
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
export { A, A as Link, A as NavLink, Navigate, Route, Router, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createIntegration, createMemoryHistory, hashIntegration, memoryIntegration, normalizeIntegration, pathIntegration, revalidate, staticIntegration, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|