@solidjs/router 0.12.5 → 0.13.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 +8 -0
- package/dist/data/action.js +23 -11
- package/dist/data/events.js +1 -1
- package/dist/index.js +100 -67
- package/dist/routers/components.jsx +27 -31
- package/dist/routing.d.ts +2 -2
- package/dist/routing.js +19 -8
- package/dist/types.d.ts +8 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -494,6 +494,14 @@ const user = createAsync((currentValue) => getUser(params.id))
|
|
|
494
494
|
|
|
495
495
|
Using `cache` in `createResource` directly won't work properly as the fetcher is not reactive and it won't invalidate properly.
|
|
496
496
|
|
|
497
|
+
### `createAsyncStore`
|
|
498
|
+
|
|
499
|
+
Similar to `createAsync` except it uses a deeply reactive store. Perfect for applying fine-grained changes to large model data that updates.
|
|
500
|
+
|
|
501
|
+
```jsx
|
|
502
|
+
const todos = createAsyncStore(() => getTodos());
|
|
503
|
+
```
|
|
504
|
+
|
|
497
505
|
### `action`
|
|
498
506
|
|
|
499
507
|
Actions are data mutations that can trigger invalidations and further routing. A list of prebuilt response helpers can be found below.
|
package/dist/data/action.js
CHANGED
|
@@ -26,21 +26,28 @@ export function useSubmission(fn, filter) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
export function useAction(action) {
|
|
29
|
-
const
|
|
30
|
-
return (...args) => action.apply(
|
|
29
|
+
const r = useRouter();
|
|
30
|
+
return (...args) => action.apply({ r }, args);
|
|
31
31
|
}
|
|
32
32
|
export function action(fn, name) {
|
|
33
33
|
function mutate(...variables) {
|
|
34
|
-
const router = this;
|
|
34
|
+
const router = this.r;
|
|
35
|
+
const form = this.f;
|
|
35
36
|
const p = (router.singleFlight && fn.withOptions
|
|
36
37
|
? fn.withOptions({ headers: { "X-Single-Flight": "true" } })
|
|
37
38
|
: fn)(...variables);
|
|
38
39
|
const [result, setResult] = createSignal();
|
|
39
40
|
let submission;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
function handler(error) {
|
|
42
|
+
return async (res) => {
|
|
43
|
+
const result = await handleResponse(res, error, router.navigatorFactory());
|
|
44
|
+
if (!result)
|
|
45
|
+
return submission.clear();
|
|
46
|
+
setResult(result);
|
|
47
|
+
if (result.error && !form)
|
|
48
|
+
throw result.error;
|
|
49
|
+
return result.data;
|
|
50
|
+
};
|
|
44
51
|
}
|
|
45
52
|
router.submissions[1](s => [
|
|
46
53
|
...s,
|
|
@@ -50,6 +57,9 @@ export function action(fn, name) {
|
|
|
50
57
|
get result() {
|
|
51
58
|
return result()?.data;
|
|
52
59
|
},
|
|
60
|
+
get error() {
|
|
61
|
+
return result()?.error;
|
|
62
|
+
},
|
|
53
63
|
get pending() {
|
|
54
64
|
return !result();
|
|
55
65
|
},
|
|
@@ -59,11 +69,11 @@ export function action(fn, name) {
|
|
|
59
69
|
retry() {
|
|
60
70
|
setResult(undefined);
|
|
61
71
|
const p = fn(...variables);
|
|
62
|
-
return p.then(handler, handler);
|
|
72
|
+
return p.then(handler(), handler(true));
|
|
63
73
|
}
|
|
64
74
|
})
|
|
65
75
|
]);
|
|
66
|
-
return p.then(handler, handler);
|
|
76
|
+
return p.then(handler(), handler(true));
|
|
67
77
|
}
|
|
68
78
|
const url = fn.url ||
|
|
69
79
|
(name && `https://action/${name}`) ||
|
|
@@ -92,7 +102,7 @@ function toAction(fn, url) {
|
|
|
92
102
|
return fn;
|
|
93
103
|
}
|
|
94
104
|
const hashString = (s) => s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
|
|
95
|
-
async function handleResponse(response, navigate) {
|
|
105
|
+
async function handleResponse(response, error, navigate) {
|
|
96
106
|
let data;
|
|
97
107
|
let keys;
|
|
98
108
|
let invalidateKeys;
|
|
@@ -123,11 +133,13 @@ async function handleResponse(response, navigate) {
|
|
|
123
133
|
}
|
|
124
134
|
}
|
|
125
135
|
}
|
|
136
|
+
else if (error)
|
|
137
|
+
return { error: response };
|
|
126
138
|
else
|
|
127
139
|
data = response;
|
|
128
140
|
// invalidate
|
|
129
141
|
cacheKeyOp(invalidateKeys, entry => (entry[0] = 0));
|
|
130
142
|
// trigger revalidation
|
|
131
143
|
await revalidate(keys, false);
|
|
132
|
-
return data;
|
|
144
|
+
return data != null ? { data } : undefined;
|
|
133
145
|
}
|
package/dist/data/events.js
CHANGED
|
@@ -103,7 +103,7 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
|
|
|
103
103
|
const data = new FormData(evt.target);
|
|
104
104
|
if (evt.submitter && evt.submitter.name)
|
|
105
105
|
data.append(evt.submitter.name, evt.submitter.value);
|
|
106
|
-
handler.call(router, data);
|
|
106
|
+
handler.call({ r: router, f: evt.target }, data);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
// ensure delegated event run first
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isServer, getRequestEvent, createComponent as createComponent$1, memo, 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,
|
|
2
|
+
import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, Show, createRoot, getListener, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
|
|
3
3
|
import { createStore, reconcile, unwrap } from 'solid-js/store';
|
|
4
4
|
|
|
5
5
|
function createBeforeLeave() {
|
|
@@ -256,7 +256,7 @@ const useMatch = (path, matchFilters) => {
|
|
|
256
256
|
}
|
|
257
257
|
});
|
|
258
258
|
};
|
|
259
|
-
const useParams = () =>
|
|
259
|
+
const useParams = () => useRouter().params;
|
|
260
260
|
const useSearchParams = () => {
|
|
261
261
|
const location = useLocation();
|
|
262
262
|
const navigate = useNavigate();
|
|
@@ -406,7 +406,7 @@ let intent;
|
|
|
406
406
|
function getIntent() {
|
|
407
407
|
return intent;
|
|
408
408
|
}
|
|
409
|
-
function createRouterContext(integration,
|
|
409
|
+
function createRouterContext(integration, branches, getContext, options = {}) {
|
|
410
410
|
const {
|
|
411
411
|
signal: [source, setSource],
|
|
412
412
|
utils = {}
|
|
@@ -438,9 +438,17 @@ function createRouterContext(integration, getContext, getBranches, options = {})
|
|
|
438
438
|
const location = createLocation(reference, state);
|
|
439
439
|
const referrers = [];
|
|
440
440
|
const submissions = createSignal(isServer ? initFromFlash() : []);
|
|
441
|
+
const matches = createMemo(() => getRouteMatches(branches(), location.pathname));
|
|
442
|
+
const params = createMemoObject(() => {
|
|
443
|
+
const m = matches();
|
|
444
|
+
const params = {};
|
|
445
|
+
for (let i = 0; i < m.length; i++) {
|
|
446
|
+
Object.assign(params, m[i].params);
|
|
447
|
+
}
|
|
448
|
+
return params;
|
|
449
|
+
});
|
|
441
450
|
const baseRoute = {
|
|
442
451
|
pattern: basePath,
|
|
443
|
-
params: {},
|
|
444
452
|
path: () => basePath,
|
|
445
453
|
outlet: () => null,
|
|
446
454
|
resolvePath(to) {
|
|
@@ -470,10 +478,12 @@ function createRouterContext(integration, getContext, getBranches, options = {})
|
|
|
470
478
|
return {
|
|
471
479
|
base: baseRoute,
|
|
472
480
|
location,
|
|
481
|
+
params,
|
|
473
482
|
isRouting,
|
|
474
483
|
renderPath,
|
|
475
484
|
parsePath,
|
|
476
485
|
navigatorFactory,
|
|
486
|
+
matches,
|
|
477
487
|
beforeLeave,
|
|
478
488
|
preloadRoute,
|
|
479
489
|
singleFlight: options.singleFlight === undefined ? true : options.singleFlight,
|
|
@@ -568,7 +578,7 @@ function createRouterContext(integration, getContext, getBranches, options = {})
|
|
|
568
578
|
}
|
|
569
579
|
}
|
|
570
580
|
function preloadRoute(url, preloadData) {
|
|
571
|
-
const matches = getRouteMatches(
|
|
581
|
+
const matches = getRouteMatches(branches(), url.pathname);
|
|
572
582
|
const prevIntent = intent;
|
|
573
583
|
intent = "preload";
|
|
574
584
|
for (let match in matches) {
|
|
@@ -600,10 +610,11 @@ function createRouterContext(integration, getContext, getBranches, options = {})
|
|
|
600
610
|
return e && e.router && e.router.submission ? [e.router.submission] : [];
|
|
601
611
|
}
|
|
602
612
|
}
|
|
603
|
-
function createRouteContext(router, parent, outlet, match
|
|
613
|
+
function createRouteContext(router, parent, outlet, match) {
|
|
604
614
|
const {
|
|
605
615
|
base,
|
|
606
|
-
location
|
|
616
|
+
location,
|
|
617
|
+
params
|
|
607
618
|
} = router;
|
|
608
619
|
const {
|
|
609
620
|
pattern,
|
|
@@ -621,7 +632,6 @@ function createRouteContext(router, parent, outlet, match, params) {
|
|
|
621
632
|
parent,
|
|
622
633
|
pattern,
|
|
623
634
|
path,
|
|
624
|
-
params,
|
|
625
635
|
outlet: () => component ? createComponent(component, {
|
|
626
636
|
params,
|
|
627
637
|
location,
|
|
@@ -642,13 +652,9 @@ const createRouterComponent = router => props => {
|
|
|
642
652
|
base
|
|
643
653
|
} = props;
|
|
644
654
|
const routeDefs = children(() => props.children);
|
|
645
|
-
const branches = createMemo(() => createBranches(props.
|
|
646
|
-
component: props.root,
|
|
647
|
-
load: props.rootLoad,
|
|
648
|
-
children: routeDefs()
|
|
649
|
-
} : routeDefs(), props.base || ""));
|
|
655
|
+
const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
|
|
650
656
|
let context;
|
|
651
|
-
const routerState = createRouterContext(router, () => context,
|
|
657
|
+
const routerState = createRouterContext(router, branches, () => context, {
|
|
652
658
|
base,
|
|
653
659
|
singleFlight: props.singleFlight
|
|
654
660
|
});
|
|
@@ -656,24 +662,62 @@ const createRouterComponent = router => props => {
|
|
|
656
662
|
return createComponent$1(RouterContextObj.Provider, {
|
|
657
663
|
value: routerState,
|
|
658
664
|
get children() {
|
|
659
|
-
return
|
|
665
|
+
return createComponent$1(Root, {
|
|
660
666
|
routerState: routerState,
|
|
661
|
-
get
|
|
662
|
-
return
|
|
667
|
+
get root() {
|
|
668
|
+
return props.root;
|
|
669
|
+
},
|
|
670
|
+
get load() {
|
|
671
|
+
return props.rootLoad;
|
|
672
|
+
},
|
|
673
|
+
get children() {
|
|
674
|
+
return [memo(() => (context = getOwner()) && null), createComponent$1(Routes, {
|
|
675
|
+
routerState: routerState,
|
|
676
|
+
get branches() {
|
|
677
|
+
return branches();
|
|
678
|
+
}
|
|
679
|
+
})];
|
|
663
680
|
}
|
|
664
|
-
})
|
|
681
|
+
});
|
|
665
682
|
}
|
|
666
683
|
});
|
|
667
684
|
};
|
|
685
|
+
function Root(props) {
|
|
686
|
+
const location = props.routerState.location;
|
|
687
|
+
const params = props.routerState.params;
|
|
688
|
+
const data = createMemo(() => props.load && untrack(() => props.load({
|
|
689
|
+
params,
|
|
690
|
+
location,
|
|
691
|
+
intent: "preload"
|
|
692
|
+
})));
|
|
693
|
+
return createComponent$1(Show, {
|
|
694
|
+
get when() {
|
|
695
|
+
return props.root;
|
|
696
|
+
},
|
|
697
|
+
keyed: true,
|
|
698
|
+
get fallback() {
|
|
699
|
+
return props.children;
|
|
700
|
+
},
|
|
701
|
+
children: Root => createComponent$1(Root, {
|
|
702
|
+
params: params,
|
|
703
|
+
location: location,
|
|
704
|
+
get data() {
|
|
705
|
+
return data();
|
|
706
|
+
},
|
|
707
|
+
get children() {
|
|
708
|
+
return props.children;
|
|
709
|
+
}
|
|
710
|
+
})
|
|
711
|
+
});
|
|
712
|
+
}
|
|
668
713
|
function Routes(props) {
|
|
669
|
-
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
670
714
|
if (isServer) {
|
|
671
715
|
const e = getRequestEvent();
|
|
672
716
|
if (e && e.router && e.router.dataOnly) {
|
|
673
|
-
dataOnly(e, props.branches);
|
|
717
|
+
dataOnly(e, props.routerState, props.branches);
|
|
674
718
|
return;
|
|
675
719
|
}
|
|
676
|
-
e && ((e.router || (e.router = {})).matches || (e.router.matches = matches().map(({
|
|
720
|
+
e && ((e.router || (e.router = {})).matches || (e.router.matches = props.routerState.matches().map(({
|
|
677
721
|
route,
|
|
678
722
|
path,
|
|
679
723
|
params
|
|
@@ -685,17 +729,9 @@ function Routes(props) {
|
|
|
685
729
|
info: route.info
|
|
686
730
|
}))));
|
|
687
731
|
}
|
|
688
|
-
const params = createMemoObject(() => {
|
|
689
|
-
const m = matches();
|
|
690
|
-
const params = {};
|
|
691
|
-
for (let i = 0; i < m.length; i++) {
|
|
692
|
-
Object.assign(params, m[i].params);
|
|
693
|
-
}
|
|
694
|
-
return params;
|
|
695
|
-
});
|
|
696
732
|
const disposers = [];
|
|
697
733
|
let root;
|
|
698
|
-
const routeStates = createMemo(on(matches, (nextMatches, prevMatches, prev) => {
|
|
734
|
+
const routeStates = createMemo(on(props.routerState.matches, (nextMatches, prevMatches, prev) => {
|
|
699
735
|
let equal = prevMatches && nextMatches.length === prevMatches.length;
|
|
700
736
|
const next = [];
|
|
701
737
|
for (let i = 0, len = nextMatches.length; i < len; i++) {
|
|
@@ -710,7 +746,7 @@ function Routes(props) {
|
|
|
710
746
|
}
|
|
711
747
|
createRoot(dispose => {
|
|
712
748
|
disposers[i] = dispose;
|
|
713
|
-
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => matches()[i]
|
|
749
|
+
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => props.routerState.matches()[i]);
|
|
714
750
|
});
|
|
715
751
|
}
|
|
716
752
|
}
|
|
@@ -721,18 +757,7 @@ function Routes(props) {
|
|
|
721
757
|
root = next[0];
|
|
722
758
|
return next;
|
|
723
759
|
}));
|
|
724
|
-
return
|
|
725
|
-
get when() {
|
|
726
|
-
return routeStates() && root;
|
|
727
|
-
},
|
|
728
|
-
keyed: true,
|
|
729
|
-
children: route => createComponent$1(RouteContextObj.Provider, {
|
|
730
|
-
value: route,
|
|
731
|
-
get children() {
|
|
732
|
-
return route.outlet();
|
|
733
|
-
}
|
|
734
|
-
})
|
|
735
|
-
});
|
|
760
|
+
return createOutlet(() => routeStates() && root)();
|
|
736
761
|
}
|
|
737
762
|
const createOutlet = child => {
|
|
738
763
|
return () => createComponent$1(Show, {
|
|
@@ -758,7 +783,7 @@ const Route = props => {
|
|
|
758
783
|
};
|
|
759
784
|
|
|
760
785
|
// for data only mode with single flight mutations
|
|
761
|
-
function dataOnly(event, branches) {
|
|
786
|
+
function dataOnly(event, routerState, branches) {
|
|
762
787
|
const url = new URL(event.request.url);
|
|
763
788
|
const prevMatches = getRouteMatches(branches, new URL(event.router.previousUrl || event.request.url).pathname);
|
|
764
789
|
const matches = getRouteMatches(branches, url.pathname);
|
|
@@ -770,14 +795,7 @@ function dataOnly(event, branches) {
|
|
|
770
795
|
} = matches[match];
|
|
771
796
|
route.load && route.load({
|
|
772
797
|
params,
|
|
773
|
-
location:
|
|
774
|
-
pathname: url.pathname,
|
|
775
|
-
search: url.search,
|
|
776
|
-
hash: url.hash,
|
|
777
|
-
query: extractSearchParams(url),
|
|
778
|
-
state: null,
|
|
779
|
-
key: ""
|
|
780
|
-
},
|
|
798
|
+
location: routerState.location,
|
|
781
799
|
intent: "preload"
|
|
782
800
|
});
|
|
783
801
|
}
|
|
@@ -1040,12 +1058,15 @@ function useSubmission(fn, filter) {
|
|
|
1040
1058
|
});
|
|
1041
1059
|
}
|
|
1042
1060
|
function useAction(action) {
|
|
1043
|
-
const
|
|
1044
|
-
return (...args) => action.apply(
|
|
1061
|
+
const r = useRouter();
|
|
1062
|
+
return (...args) => action.apply({
|
|
1063
|
+
r
|
|
1064
|
+
}, args);
|
|
1045
1065
|
}
|
|
1046
1066
|
function action(fn, name) {
|
|
1047
1067
|
function mutate(...variables) {
|
|
1048
|
-
const router = this;
|
|
1068
|
+
const router = this.r;
|
|
1069
|
+
const form = this.f;
|
|
1049
1070
|
const p = (router.singleFlight && fn.withOptions ? fn.withOptions({
|
|
1050
1071
|
headers: {
|
|
1051
1072
|
"X-Single-Flight": "true"
|
|
@@ -1053,12 +1074,14 @@ function action(fn, name) {
|
|
|
1053
1074
|
}) : fn)(...variables);
|
|
1054
1075
|
const [result, setResult] = createSignal();
|
|
1055
1076
|
let submission;
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1077
|
+
function handler(error) {
|
|
1078
|
+
return async res => {
|
|
1079
|
+
const result = await handleResponse(res, error, router.navigatorFactory());
|
|
1080
|
+
if (!result) return submission.clear();
|
|
1081
|
+
setResult(result);
|
|
1082
|
+
if (result.error && !form) throw result.error;
|
|
1083
|
+
return result.data;
|
|
1084
|
+
};
|
|
1062
1085
|
}
|
|
1063
1086
|
router.submissions[1](s => [...s, submission = {
|
|
1064
1087
|
input: variables,
|
|
@@ -1066,6 +1089,9 @@ function action(fn, name) {
|
|
|
1066
1089
|
get result() {
|
|
1067
1090
|
return result()?.data;
|
|
1068
1091
|
},
|
|
1092
|
+
get error() {
|
|
1093
|
+
return result()?.error;
|
|
1094
|
+
},
|
|
1069
1095
|
get pending() {
|
|
1070
1096
|
return !result();
|
|
1071
1097
|
},
|
|
@@ -1075,10 +1101,10 @@ function action(fn, name) {
|
|
|
1075
1101
|
retry() {
|
|
1076
1102
|
setResult(undefined);
|
|
1077
1103
|
const p = fn(...variables);
|
|
1078
|
-
return p.then(handler, handler);
|
|
1104
|
+
return p.then(handler(), handler(true));
|
|
1079
1105
|
}
|
|
1080
1106
|
}]);
|
|
1081
|
-
return p.then(handler, handler);
|
|
1107
|
+
return p.then(handler(), handler(true));
|
|
1082
1108
|
}
|
|
1083
1109
|
const url = fn.url || name && `https://action/${name}` || (!isServer ? `https://action/${hashString(fn.toString())}` : "");
|
|
1084
1110
|
return toAction(mutate, url);
|
|
@@ -1104,7 +1130,7 @@ function toAction(fn, url) {
|
|
|
1104
1130
|
return fn;
|
|
1105
1131
|
}
|
|
1106
1132
|
const hashString = s => s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
|
|
1107
|
-
async function handleResponse(response, navigate) {
|
|
1133
|
+
async function handleResponse(response, error, navigate) {
|
|
1108
1134
|
let data;
|
|
1109
1135
|
let keys;
|
|
1110
1136
|
let invalidateKeys;
|
|
@@ -1131,12 +1157,16 @@ async function handleResponse(response, navigate) {
|
|
|
1131
1157
|
navigate(locationUrl);
|
|
1132
1158
|
}
|
|
1133
1159
|
}
|
|
1134
|
-
} else
|
|
1160
|
+
} else if (error) return {
|
|
1161
|
+
error: response
|
|
1162
|
+
};else data = response;
|
|
1135
1163
|
// invalidate
|
|
1136
1164
|
cacheKeyOp(invalidateKeys, entry => entry[0] = 0);
|
|
1137
1165
|
// trigger revalidation
|
|
1138
1166
|
await revalidate(keys, false);
|
|
1139
|
-
return data
|
|
1167
|
+
return data != null ? {
|
|
1168
|
+
data
|
|
1169
|
+
} : undefined;
|
|
1140
1170
|
}
|
|
1141
1171
|
|
|
1142
1172
|
function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
|
|
@@ -1215,7 +1245,10 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1215
1245
|
evt.preventDefault();
|
|
1216
1246
|
const data = new FormData(evt.target);
|
|
1217
1247
|
if (evt.submitter && evt.submitter.name) data.append(evt.submitter.name, evt.submitter.value);
|
|
1218
|
-
handler.call(
|
|
1248
|
+
handler.call({
|
|
1249
|
+
r: router,
|
|
1250
|
+
f: evt.target
|
|
1251
|
+
}, data);
|
|
1219
1252
|
}
|
|
1220
1253
|
}
|
|
1221
1254
|
|
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
/*@refresh skip*/
|
|
2
2
|
import { getRequestEvent, isServer } from "solid-js/web";
|
|
3
|
-
import { children, createMemo, createRoot, getOwner, mergeProps, on, Show } from "solid-js";
|
|
3
|
+
import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
|
|
4
4
|
import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj } from "../routing.js";
|
|
5
|
-
import { createMemoObject, extractSearchParams } from "../utils.js";
|
|
6
5
|
export const createRouterComponent = (router) => (props) => {
|
|
7
6
|
const { base } = props;
|
|
8
7
|
const routeDefs = children(() => props.children);
|
|
9
|
-
const branches = createMemo(() => createBranches(
|
|
8
|
+
const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
|
|
10
9
|
let context;
|
|
11
|
-
const routerState = createRouterContext(router, () => context,
|
|
10
|
+
const routerState = createRouterContext(router, branches, () => context, {
|
|
11
|
+
base,
|
|
12
|
+
singleFlight: props.singleFlight
|
|
13
|
+
});
|
|
12
14
|
router.create && router.create(routerState);
|
|
13
15
|
return (<RouterContextObj.Provider value={routerState}>
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
+
<Root routerState={routerState} root={props.root} load={props.rootLoad}>
|
|
17
|
+
{(context = getOwner()) && null}
|
|
18
|
+
<Routes routerState={routerState} branches={branches()}/>
|
|
19
|
+
</Root>
|
|
16
20
|
</RouterContextObj.Provider>);
|
|
17
21
|
};
|
|
22
|
+
function Root(props) {
|
|
23
|
+
const location = props.routerState.location;
|
|
24
|
+
const params = props.routerState.params;
|
|
25
|
+
const data = createMemo(() => props.load && untrack(() => props.load({ params, location, intent: "preload" })));
|
|
26
|
+
return (<Show when={props.root} keyed fallback={props.children}>
|
|
27
|
+
{Root => (<Root params={params} location={location} data={data()}>
|
|
28
|
+
{props.children}
|
|
29
|
+
</Root>)}
|
|
30
|
+
</Show>);
|
|
31
|
+
}
|
|
18
32
|
function Routes(props) {
|
|
19
|
-
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
20
33
|
if (isServer) {
|
|
21
34
|
const e = getRequestEvent();
|
|
22
35
|
if (e && e.router && e.router.dataOnly) {
|
|
23
|
-
dataOnly(e, props.branches);
|
|
36
|
+
dataOnly(e, props.routerState, props.branches);
|
|
24
37
|
return;
|
|
25
38
|
}
|
|
26
39
|
e &&
|
|
27
40
|
((e.router || (e.router = {})).matches ||
|
|
28
|
-
(e.router.matches = matches().map(({ route, path, params }) => ({
|
|
41
|
+
(e.router.matches = props.routerState.matches().map(({ route, path, params }) => ({
|
|
29
42
|
path: route.originalPath,
|
|
30
43
|
pattern: route.pattern,
|
|
31
44
|
match: path,
|
|
@@ -33,17 +46,9 @@ function Routes(props) {
|
|
|
33
46
|
info: route.info
|
|
34
47
|
}))));
|
|
35
48
|
}
|
|
36
|
-
const params = createMemoObject(() => {
|
|
37
|
-
const m = matches();
|
|
38
|
-
const params = {};
|
|
39
|
-
for (let i = 0; i < m.length; i++) {
|
|
40
|
-
Object.assign(params, m[i].params);
|
|
41
|
-
}
|
|
42
|
-
return params;
|
|
43
|
-
});
|
|
44
49
|
const disposers = [];
|
|
45
50
|
let root;
|
|
46
|
-
const routeStates = createMemo(on(matches, (nextMatches, prevMatches, prev) => {
|
|
51
|
+
const routeStates = createMemo(on(props.routerState.matches, (nextMatches, prevMatches, prev) => {
|
|
47
52
|
let equal = prevMatches && nextMatches.length === prevMatches.length;
|
|
48
53
|
const next = [];
|
|
49
54
|
for (let i = 0, len = nextMatches.length; i < len; i++) {
|
|
@@ -59,7 +64,7 @@ function Routes(props) {
|
|
|
59
64
|
}
|
|
60
65
|
createRoot(dispose => {
|
|
61
66
|
disposers[i] = dispose;
|
|
62
|
-
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => matches()[i]
|
|
67
|
+
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => props.routerState.matches()[i]);
|
|
63
68
|
});
|
|
64
69
|
}
|
|
65
70
|
}
|
|
@@ -70,9 +75,7 @@ function Routes(props) {
|
|
|
70
75
|
root = next[0];
|
|
71
76
|
return next;
|
|
72
77
|
}));
|
|
73
|
-
return (
|
|
74
|
-
{route => <RouteContextObj.Provider value={route}>{route.outlet()}</RouteContextObj.Provider>}
|
|
75
|
-
</Show>);
|
|
78
|
+
return createOutlet(() => routeStates() && root)();
|
|
76
79
|
}
|
|
77
80
|
const createOutlet = (child) => {
|
|
78
81
|
return () => (<Show when={child()} keyed>
|
|
@@ -88,7 +91,7 @@ export const Route = (props) => {
|
|
|
88
91
|
});
|
|
89
92
|
};
|
|
90
93
|
// for data only mode with single flight mutations
|
|
91
|
-
function dataOnly(event, branches) {
|
|
94
|
+
function dataOnly(event, routerState, branches) {
|
|
92
95
|
const url = new URL(event.request.url);
|
|
93
96
|
const prevMatches = getRouteMatches(branches, new URL(event.router.previousUrl || event.request.url).pathname);
|
|
94
97
|
const matches = getRouteMatches(branches, url.pathname);
|
|
@@ -99,14 +102,7 @@ function dataOnly(event, branches) {
|
|
|
99
102
|
route.load &&
|
|
100
103
|
route.load({
|
|
101
104
|
params,
|
|
102
|
-
location:
|
|
103
|
-
pathname: url.pathname,
|
|
104
|
-
search: url.search,
|
|
105
|
-
hash: url.hash,
|
|
106
|
-
query: extractSearchParams(url),
|
|
107
|
-
state: null,
|
|
108
|
-
key: ""
|
|
109
|
-
},
|
|
105
|
+
location: routerState.location,
|
|
110
106
|
intent: "preload"
|
|
111
107
|
});
|
|
112
108
|
}
|
package/dist/routing.d.ts
CHANGED
|
@@ -19,8 +19,8 @@ export declare function createBranches(routeDef: RouteDefinition | RouteDefiniti
|
|
|
19
19
|
export declare function getRouteMatches(branches: Branch[], location: string): RouteMatch[];
|
|
20
20
|
export declare function createLocation(path: Accessor<string>, state: Accessor<any>): Location;
|
|
21
21
|
export declare function getIntent(): Intent | undefined;
|
|
22
|
-
export declare function createRouterContext(integration: RouterIntegration,
|
|
22
|
+
export declare function createRouterContext(integration: RouterIntegration, branches: () => Branch[], getContext?: () => any, options?: {
|
|
23
23
|
base?: string;
|
|
24
24
|
singleFlight?: boolean;
|
|
25
25
|
}): RouterContext;
|
|
26
|
-
export declare function createRouteContext(router: RouterContext, parent: RouteContext, outlet: () => JSX.Element, match: () => RouteMatch
|
|
26
|
+
export declare function createRouteContext(router: RouterContext, parent: RouteContext, outlet: () => JSX.Element, match: () => RouteMatch): RouteContext;
|
package/dist/routing.js
CHANGED
|
@@ -34,7 +34,7 @@ export const useMatch = (path, matchFilters) => {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
};
|
|
37
|
-
export const useParams = () =>
|
|
37
|
+
export const useParams = () => useRouter().params;
|
|
38
38
|
export const useSearchParams = () => {
|
|
39
39
|
const location = useLocation();
|
|
40
40
|
const navigate = useNavigate();
|
|
@@ -184,7 +184,7 @@ let intent;
|
|
|
184
184
|
export function getIntent() {
|
|
185
185
|
return intent;
|
|
186
186
|
}
|
|
187
|
-
export function createRouterContext(integration,
|
|
187
|
+
export function createRouterContext(integration, branches, getContext, options = {}) {
|
|
188
188
|
const { signal: [source, setSource], utils = {} } = integration;
|
|
189
189
|
const parsePath = utils.parsePath || (p => p);
|
|
190
190
|
const renderPath = utils.renderPath || (p => p);
|
|
@@ -211,9 +211,17 @@ export function createRouterContext(integration, getContext, getBranches, option
|
|
|
211
211
|
const location = createLocation(reference, state);
|
|
212
212
|
const referrers = [];
|
|
213
213
|
const submissions = createSignal(isServer ? initFromFlash() : []);
|
|
214
|
+
const matches = createMemo(() => getRouteMatches(branches(), location.pathname));
|
|
215
|
+
const params = createMemoObject(() => {
|
|
216
|
+
const m = matches();
|
|
217
|
+
const params = {};
|
|
218
|
+
for (let i = 0; i < m.length; i++) {
|
|
219
|
+
Object.assign(params, m[i].params);
|
|
220
|
+
}
|
|
221
|
+
return params;
|
|
222
|
+
});
|
|
214
223
|
const baseRoute = {
|
|
215
224
|
pattern: basePath,
|
|
216
|
-
params: {},
|
|
217
225
|
path: () => basePath,
|
|
218
226
|
outlet: () => null,
|
|
219
227
|
resolvePath(to) {
|
|
@@ -240,10 +248,12 @@ export function createRouterContext(integration, getContext, getBranches, option
|
|
|
240
248
|
return {
|
|
241
249
|
base: baseRoute,
|
|
242
250
|
location,
|
|
251
|
+
params,
|
|
243
252
|
isRouting,
|
|
244
253
|
renderPath,
|
|
245
254
|
parsePath,
|
|
246
255
|
navigatorFactory,
|
|
256
|
+
matches,
|
|
247
257
|
beforeLeave,
|
|
248
258
|
preloadRoute,
|
|
249
259
|
singleFlight: options.singleFlight === undefined ? true : options.singleFlight,
|
|
@@ -324,7 +334,7 @@ export function createRouterContext(integration, getContext, getBranches, option
|
|
|
324
334
|
}
|
|
325
335
|
}
|
|
326
336
|
function preloadRoute(url, preloadData) {
|
|
327
|
-
const matches = getRouteMatches(
|
|
337
|
+
const matches = getRouteMatches(branches(), url.pathname);
|
|
328
338
|
const prevIntent = intent;
|
|
329
339
|
intent = "preload";
|
|
330
340
|
for (let match in matches) {
|
|
@@ -352,11 +362,13 @@ export function createRouterContext(integration, getContext, getBranches, option
|
|
|
352
362
|
}
|
|
353
363
|
function initFromFlash() {
|
|
354
364
|
const e = getRequestEvent();
|
|
355
|
-
return e && e.router && e.router.submission
|
|
365
|
+
return (e && e.router && e.router.submission
|
|
366
|
+
? [e.router.submission]
|
|
367
|
+
: []);
|
|
356
368
|
}
|
|
357
369
|
}
|
|
358
|
-
export function createRouteContext(router, parent, outlet, match
|
|
359
|
-
const { base, location } = router;
|
|
370
|
+
export function createRouteContext(router, parent, outlet, match) {
|
|
371
|
+
const { base, location, params } = router;
|
|
360
372
|
const { pattern, component, load } = match().route;
|
|
361
373
|
const path = createMemo(() => match().path);
|
|
362
374
|
component &&
|
|
@@ -367,7 +379,6 @@ export function createRouteContext(router, parent, outlet, match, params) {
|
|
|
367
379
|
parent,
|
|
368
380
|
pattern,
|
|
369
381
|
path,
|
|
370
|
-
params,
|
|
371
382
|
outlet: () => component
|
|
372
383
|
? createComponent(component, {
|
|
373
384
|
params,
|
package/dist/types.d.ts
CHANGED
|
@@ -9,7 +9,11 @@ declare module "solid-js/web" {
|
|
|
9
9
|
router?: {
|
|
10
10
|
matches?: OutputMatch[];
|
|
11
11
|
cache?: Map<string, CacheEntry>;
|
|
12
|
-
submission?:
|
|
12
|
+
submission?: {
|
|
13
|
+
input: any;
|
|
14
|
+
result: any;
|
|
15
|
+
url: string;
|
|
16
|
+
};
|
|
13
17
|
dataOnly?: boolean | string[];
|
|
14
18
|
data?: Record<string, any>;
|
|
15
19
|
previousUrl?: string;
|
|
@@ -110,7 +114,6 @@ export interface RouteContext {
|
|
|
110
114
|
parent?: RouteContext;
|
|
111
115
|
child?: RouteContext;
|
|
112
116
|
pattern: string;
|
|
113
|
-
params: Params;
|
|
114
117
|
path: () => string;
|
|
115
118
|
outlet: () => JSX.Element;
|
|
116
119
|
resolvePath(to: string): string | undefined;
|
|
@@ -124,8 +127,10 @@ export interface RouterUtils {
|
|
|
124
127
|
export interface RouterContext {
|
|
125
128
|
base: RouteContext;
|
|
126
129
|
location: Location;
|
|
130
|
+
params: Params;
|
|
127
131
|
navigatorFactory: NavigatorFactory;
|
|
128
132
|
isRouting: () => boolean;
|
|
133
|
+
matches: () => RouteMatch[];
|
|
129
134
|
renderPath(path: string): string;
|
|
130
135
|
parsePath(str: string): string;
|
|
131
136
|
beforeLeave: BeforeLeaveLifecycle;
|
|
@@ -153,6 +158,7 @@ export interface BeforeLeaveLifecycle {
|
|
|
153
158
|
export type Submission<T, U> = {
|
|
154
159
|
readonly input: T;
|
|
155
160
|
readonly result?: U;
|
|
161
|
+
readonly error: any;
|
|
156
162
|
readonly pending: boolean;
|
|
157
163
|
readonly url: string;
|
|
158
164
|
clear: () => void;
|