@solidjs/router 0.12.5 → 0.13.0
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 +80 -48
- package/dist/routers/components.jsx +25 -19
- package/dist/routing.js +3 -1
- package/dist/types.d.ts +6 -1
- 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() {
|
|
@@ -642,35 +642,70 @@ const createRouterComponent = router => props => {
|
|
|
642
642
|
base
|
|
643
643
|
} = props;
|
|
644
644
|
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 || ""));
|
|
645
|
+
const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
|
|
650
646
|
let context;
|
|
651
647
|
const routerState = createRouterContext(router, () => context, branches, {
|
|
652
648
|
base,
|
|
653
649
|
singleFlight: props.singleFlight
|
|
654
650
|
});
|
|
651
|
+
const location = routerState.location;
|
|
655
652
|
router.create && router.create(routerState);
|
|
656
653
|
return createComponent$1(RouterContextObj.Provider, {
|
|
657
654
|
value: routerState,
|
|
658
655
|
get children() {
|
|
659
|
-
return
|
|
660
|
-
|
|
661
|
-
get
|
|
662
|
-
return
|
|
656
|
+
return createComponent$1(Root, {
|
|
657
|
+
location: location,
|
|
658
|
+
get root() {
|
|
659
|
+
return props.root;
|
|
660
|
+
},
|
|
661
|
+
get load() {
|
|
662
|
+
return props.rootLoad;
|
|
663
|
+
},
|
|
664
|
+
get children() {
|
|
665
|
+
return [memo(() => (context = getOwner()) && null), createComponent$1(Routes, {
|
|
666
|
+
routerState: routerState,
|
|
667
|
+
get branches() {
|
|
668
|
+
return branches();
|
|
669
|
+
}
|
|
670
|
+
})];
|
|
663
671
|
}
|
|
664
|
-
})
|
|
672
|
+
});
|
|
665
673
|
}
|
|
666
674
|
});
|
|
667
675
|
};
|
|
676
|
+
function Root(props) {
|
|
677
|
+
const location = props.location;
|
|
678
|
+
const data = createMemo(() => props.load && untrack(() => props.load({
|
|
679
|
+
params: {},
|
|
680
|
+
location,
|
|
681
|
+
intent: "preload"
|
|
682
|
+
})));
|
|
683
|
+
return createComponent$1(Show, {
|
|
684
|
+
get when() {
|
|
685
|
+
return props.root;
|
|
686
|
+
},
|
|
687
|
+
keyed: true,
|
|
688
|
+
get fallback() {
|
|
689
|
+
return props.children;
|
|
690
|
+
},
|
|
691
|
+
children: Root => createComponent$1(Root, {
|
|
692
|
+
params: {},
|
|
693
|
+
location: location,
|
|
694
|
+
get data() {
|
|
695
|
+
return data();
|
|
696
|
+
},
|
|
697
|
+
get children() {
|
|
698
|
+
return props.children;
|
|
699
|
+
}
|
|
700
|
+
})
|
|
701
|
+
});
|
|
702
|
+
}
|
|
668
703
|
function Routes(props) {
|
|
669
704
|
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
670
705
|
if (isServer) {
|
|
671
706
|
const e = getRequestEvent();
|
|
672
707
|
if (e && e.router && e.router.dataOnly) {
|
|
673
|
-
dataOnly(e, props.branches);
|
|
708
|
+
dataOnly(e, props.routerState, props.branches);
|
|
674
709
|
return;
|
|
675
710
|
}
|
|
676
711
|
e && ((e.router || (e.router = {})).matches || (e.router.matches = matches().map(({
|
|
@@ -721,18 +756,7 @@ function Routes(props) {
|
|
|
721
756
|
root = next[0];
|
|
722
757
|
return next;
|
|
723
758
|
}));
|
|
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
|
-
});
|
|
759
|
+
return createOutlet(() => routeStates() && root)();
|
|
736
760
|
}
|
|
737
761
|
const createOutlet = child => {
|
|
738
762
|
return () => createComponent$1(Show, {
|
|
@@ -758,7 +782,7 @@ const Route = props => {
|
|
|
758
782
|
};
|
|
759
783
|
|
|
760
784
|
// for data only mode with single flight mutations
|
|
761
|
-
function dataOnly(event, branches) {
|
|
785
|
+
function dataOnly(event, routerState, branches) {
|
|
762
786
|
const url = new URL(event.request.url);
|
|
763
787
|
const prevMatches = getRouteMatches(branches, new URL(event.router.previousUrl || event.request.url).pathname);
|
|
764
788
|
const matches = getRouteMatches(branches, url.pathname);
|
|
@@ -770,14 +794,7 @@ function dataOnly(event, branches) {
|
|
|
770
794
|
} = matches[match];
|
|
771
795
|
route.load && route.load({
|
|
772
796
|
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
|
-
},
|
|
797
|
+
location: routerState.location,
|
|
781
798
|
intent: "preload"
|
|
782
799
|
});
|
|
783
800
|
}
|
|
@@ -1040,12 +1057,15 @@ function useSubmission(fn, filter) {
|
|
|
1040
1057
|
});
|
|
1041
1058
|
}
|
|
1042
1059
|
function useAction(action) {
|
|
1043
|
-
const
|
|
1044
|
-
return (...args) => action.apply(
|
|
1060
|
+
const r = useRouter();
|
|
1061
|
+
return (...args) => action.apply({
|
|
1062
|
+
r
|
|
1063
|
+
}, args);
|
|
1045
1064
|
}
|
|
1046
1065
|
function action(fn, name) {
|
|
1047
1066
|
function mutate(...variables) {
|
|
1048
|
-
const router = this;
|
|
1067
|
+
const router = this.r;
|
|
1068
|
+
const form = this.f;
|
|
1049
1069
|
const p = (router.singleFlight && fn.withOptions ? fn.withOptions({
|
|
1050
1070
|
headers: {
|
|
1051
1071
|
"X-Single-Flight": "true"
|
|
@@ -1053,12 +1073,14 @@ function action(fn, name) {
|
|
|
1053
1073
|
}) : fn)(...variables);
|
|
1054
1074
|
const [result, setResult] = createSignal();
|
|
1055
1075
|
let submission;
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1076
|
+
function handler(error) {
|
|
1077
|
+
return async res => {
|
|
1078
|
+
const result = await handleResponse(res, error, router.navigatorFactory());
|
|
1079
|
+
if (!result) return submission.clear();
|
|
1080
|
+
setResult(result);
|
|
1081
|
+
if (result.error && !form) throw result.error;
|
|
1082
|
+
return result.data;
|
|
1083
|
+
};
|
|
1062
1084
|
}
|
|
1063
1085
|
router.submissions[1](s => [...s, submission = {
|
|
1064
1086
|
input: variables,
|
|
@@ -1066,6 +1088,9 @@ function action(fn, name) {
|
|
|
1066
1088
|
get result() {
|
|
1067
1089
|
return result()?.data;
|
|
1068
1090
|
},
|
|
1091
|
+
get error() {
|
|
1092
|
+
return result()?.error;
|
|
1093
|
+
},
|
|
1069
1094
|
get pending() {
|
|
1070
1095
|
return !result();
|
|
1071
1096
|
},
|
|
@@ -1075,10 +1100,10 @@ function action(fn, name) {
|
|
|
1075
1100
|
retry() {
|
|
1076
1101
|
setResult(undefined);
|
|
1077
1102
|
const p = fn(...variables);
|
|
1078
|
-
return p.then(handler, handler);
|
|
1103
|
+
return p.then(handler(), handler(true));
|
|
1079
1104
|
}
|
|
1080
1105
|
}]);
|
|
1081
|
-
return p.then(handler, handler);
|
|
1106
|
+
return p.then(handler(), handler(true));
|
|
1082
1107
|
}
|
|
1083
1108
|
const url = fn.url || name && `https://action/${name}` || (!isServer ? `https://action/${hashString(fn.toString())}` : "");
|
|
1084
1109
|
return toAction(mutate, url);
|
|
@@ -1104,7 +1129,7 @@ function toAction(fn, url) {
|
|
|
1104
1129
|
return fn;
|
|
1105
1130
|
}
|
|
1106
1131
|
const hashString = s => s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
|
|
1107
|
-
async function handleResponse(response, navigate) {
|
|
1132
|
+
async function handleResponse(response, error, navigate) {
|
|
1108
1133
|
let data;
|
|
1109
1134
|
let keys;
|
|
1110
1135
|
let invalidateKeys;
|
|
@@ -1131,12 +1156,16 @@ async function handleResponse(response, navigate) {
|
|
|
1131
1156
|
navigate(locationUrl);
|
|
1132
1157
|
}
|
|
1133
1158
|
}
|
|
1134
|
-
} else
|
|
1159
|
+
} else if (error) return {
|
|
1160
|
+
error: response
|
|
1161
|
+
};else data = response;
|
|
1135
1162
|
// invalidate
|
|
1136
1163
|
cacheKeyOp(invalidateKeys, entry => entry[0] = 0);
|
|
1137
1164
|
// trigger revalidation
|
|
1138
1165
|
await revalidate(keys, false);
|
|
1139
|
-
return data
|
|
1166
|
+
return data != null ? {
|
|
1167
|
+
data
|
|
1168
|
+
} : undefined;
|
|
1140
1169
|
}
|
|
1141
1170
|
|
|
1142
1171
|
function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
|
|
@@ -1215,7 +1244,10 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1215
1244
|
evt.preventDefault();
|
|
1216
1245
|
const data = new FormData(evt.target);
|
|
1217
1246
|
if (evt.submitter && evt.submitter.name) data.append(evt.submitter.name, evt.submitter.value);
|
|
1218
|
-
handler.call(
|
|
1247
|
+
handler.call({
|
|
1248
|
+
r: router,
|
|
1249
|
+
f: evt.target
|
|
1250
|
+
}, data);
|
|
1219
1251
|
}
|
|
1220
1252
|
}
|
|
1221
1253
|
|
|
@@ -1,26 +1,41 @@
|
|
|
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
|
|
5
|
+
import { createMemoObject } from "../utils.js";
|
|
6
6
|
export const createRouterComponent = (router) => (props) => {
|
|
7
7
|
const { base } = props;
|
|
8
8
|
const routeDefs = children(() => props.children);
|
|
9
|
-
const branches = createMemo(() => createBranches(
|
|
9
|
+
const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
|
|
10
10
|
let context;
|
|
11
|
-
const routerState = createRouterContext(router, () => context, branches, {
|
|
11
|
+
const routerState = createRouterContext(router, () => context, branches, {
|
|
12
|
+
base,
|
|
13
|
+
singleFlight: props.singleFlight
|
|
14
|
+
});
|
|
15
|
+
const location = routerState.location;
|
|
12
16
|
router.create && router.create(routerState);
|
|
13
17
|
return (<RouterContextObj.Provider value={routerState}>
|
|
14
|
-
{
|
|
15
|
-
|
|
18
|
+
<Root location={location} root={props.root} load={props.rootLoad}>
|
|
19
|
+
{(context = getOwner()) && null}
|
|
20
|
+
<Routes routerState={routerState} branches={branches()}/>
|
|
21
|
+
</Root>
|
|
16
22
|
</RouterContextObj.Provider>);
|
|
17
23
|
};
|
|
24
|
+
function Root(props) {
|
|
25
|
+
const location = props.location;
|
|
26
|
+
const data = createMemo(() => props.load && untrack(() => props.load({ params: {}, location, intent: "preload" })));
|
|
27
|
+
return (<Show when={props.root} keyed fallback={props.children}>
|
|
28
|
+
{Root => (<Root params={{}} location={location} data={data()}>
|
|
29
|
+
{props.children}
|
|
30
|
+
</Root>)}
|
|
31
|
+
</Show>);
|
|
32
|
+
}
|
|
18
33
|
function Routes(props) {
|
|
19
34
|
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
20
35
|
if (isServer) {
|
|
21
36
|
const e = getRequestEvent();
|
|
22
37
|
if (e && e.router && e.router.dataOnly) {
|
|
23
|
-
dataOnly(e, props.branches);
|
|
38
|
+
dataOnly(e, props.routerState, props.branches);
|
|
24
39
|
return;
|
|
25
40
|
}
|
|
26
41
|
e &&
|
|
@@ -70,9 +85,7 @@ function Routes(props) {
|
|
|
70
85
|
root = next[0];
|
|
71
86
|
return next;
|
|
72
87
|
}));
|
|
73
|
-
return (
|
|
74
|
-
{route => <RouteContextObj.Provider value={route}>{route.outlet()}</RouteContextObj.Provider>}
|
|
75
|
-
</Show>);
|
|
88
|
+
return createOutlet(() => routeStates() && root)();
|
|
76
89
|
}
|
|
77
90
|
const createOutlet = (child) => {
|
|
78
91
|
return () => (<Show when={child()} keyed>
|
|
@@ -88,7 +101,7 @@ export const Route = (props) => {
|
|
|
88
101
|
});
|
|
89
102
|
};
|
|
90
103
|
// for data only mode with single flight mutations
|
|
91
|
-
function dataOnly(event, branches) {
|
|
104
|
+
function dataOnly(event, routerState, branches) {
|
|
92
105
|
const url = new URL(event.request.url);
|
|
93
106
|
const prevMatches = getRouteMatches(branches, new URL(event.router.previousUrl || event.request.url).pathname);
|
|
94
107
|
const matches = getRouteMatches(branches, url.pathname);
|
|
@@ -99,14 +112,7 @@ function dataOnly(event, branches) {
|
|
|
99
112
|
route.load &&
|
|
100
113
|
route.load({
|
|
101
114
|
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
|
-
},
|
|
115
|
+
location: routerState.location,
|
|
110
116
|
intent: "preload"
|
|
111
117
|
});
|
|
112
118
|
}
|
package/dist/routing.js
CHANGED
|
@@ -352,7 +352,9 @@ export function createRouterContext(integration, getContext, getBranches, option
|
|
|
352
352
|
}
|
|
353
353
|
function initFromFlash() {
|
|
354
354
|
const e = getRequestEvent();
|
|
355
|
-
return e && e.router && e.router.submission
|
|
355
|
+
return (e && e.router && e.router.submission
|
|
356
|
+
? [e.router.submission]
|
|
357
|
+
: []);
|
|
356
358
|
}
|
|
357
359
|
}
|
|
358
360
|
export function createRouteContext(router, parent, outlet, match, 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;
|
|
@@ -153,6 +157,7 @@ export interface BeforeLeaveLifecycle {
|
|
|
153
157
|
export type Submission<T, U> = {
|
|
154
158
|
readonly input: T;
|
|
155
159
|
readonly result?: U;
|
|
160
|
+
readonly error: any;
|
|
156
161
|
readonly pending: boolean;
|
|
157
162
|
readonly url: string;
|
|
158
163
|
clear: () => void;
|