@solidjs/router 0.13.2 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/components.jsx +1 -1
- package/dist/data/action.js +9 -12
- package/dist/data/cache.js +8 -3
- package/dist/data/events.d.ts +1 -1
- package/dist/data/events.js +10 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +76 -44
- package/dist/index.jsx +1 -1
- package/dist/routers/HashRouter.js +1 -1
- package/dist/routers/Router.js +8 -5
- package/dist/routers/StaticRouter.js +2 -1
- package/dist/routers/components.d.ts +1 -0
- package/dist/routers/components.jsx +9 -3
- package/dist/routers/createRouter.js +3 -1
- package/dist/routing.d.ts +4 -0
- package/dist/routing.js +27 -11
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ It supports all of Solid's SSR methods and has Solid's transitions baked in, so
|
|
|
30
30
|
- [useSearchParams](#usesearchparams)
|
|
31
31
|
- [useIsRouting](#useisrouting)
|
|
32
32
|
- [useMatch](#usematch)
|
|
33
|
+
- [useCurrentMatches](#useCurrentMatches)
|
|
33
34
|
- [useBeforeLeave](#usebeforeleave)
|
|
34
35
|
- [SPAs in Deployed Environments](#spas-in-deployed-environments)
|
|
35
36
|
|
|
@@ -429,7 +430,7 @@ Keep in mind these are completely optional. To use but showcase the power of our
|
|
|
429
430
|
To prevent duplicate fetching and to trigger handle refetching we provide a cache api. That takes a function and returns the same function.
|
|
430
431
|
|
|
431
432
|
```jsx
|
|
432
|
-
const getUser = cache((id) => {
|
|
433
|
+
const getUser = cache(async (id) => {
|
|
433
434
|
return (await fetch(`/api/users${id}`)).json()
|
|
434
435
|
}, "users") // used as cache key + serialized arguments
|
|
435
436
|
```
|
|
@@ -860,6 +861,16 @@ const match = useMatch(() => props.href);
|
|
|
860
861
|
return <div classList={{ active: Boolean(match()) }} />;
|
|
861
862
|
```
|
|
862
863
|
|
|
864
|
+
### useCurrentMatches
|
|
865
|
+
|
|
866
|
+
`useCurrentMatches` returns all the matches for the current matched route. Useful for getting all the route information.
|
|
867
|
+
|
|
868
|
+
For example if you stored breadcrumbs on your route definition you could retrieve them like so:
|
|
869
|
+
```js
|
|
870
|
+
const matches = useCurrentMatches();
|
|
871
|
+
const breadcrumbs = createMemo(() => matches().map(m => m.route.info.breadcrumb))
|
|
872
|
+
```
|
|
873
|
+
|
|
863
874
|
### useBeforeLeave
|
|
864
875
|
|
|
865
876
|
`useBeforeLeave` takes a function that will be called prior to leaving a route. The function will be called with:
|
package/dist/components.jsx
CHANGED
|
@@ -20,7 +20,7 @@ export function A(props) {
|
|
|
20
20
|
return [false, false];
|
|
21
21
|
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
|
|
22
22
|
const loc = normalizePath(location.pathname).toLowerCase();
|
|
23
|
-
return [props.end ? path === loc : loc.startsWith(path), path === loc];
|
|
23
|
+
return [props.end ? path === loc : loc.startsWith(path + "/") || loc === path, path === loc];
|
|
24
24
|
});
|
|
25
25
|
return (<a {...rest} href={href() || props.href} state={JSON.stringify(props.state)} classList={{
|
|
26
26
|
...(props.class && { [props.class]: true }),
|
package/dist/data/action.js
CHANGED
|
@@ -104,23 +104,18 @@ function toAction(fn, url) {
|
|
|
104
104
|
const hashString = (s) => s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
|
|
105
105
|
async function handleResponse(response, error, navigate) {
|
|
106
106
|
let data;
|
|
107
|
+
let custom;
|
|
107
108
|
let keys;
|
|
108
|
-
let
|
|
109
|
+
let flightKeys;
|
|
109
110
|
if (response instanceof Response) {
|
|
110
111
|
if (response.headers.has("X-Revalidate"))
|
|
111
|
-
keys =
|
|
112
|
+
keys = response.headers.get("X-Revalidate").split(",");
|
|
112
113
|
if (response.customBody) {
|
|
113
|
-
data = await response.customBody();
|
|
114
|
+
data = custom = await response.customBody();
|
|
114
115
|
if (response.headers.has("X-Single-Flight")) {
|
|
115
|
-
keys || (keys = []);
|
|
116
|
-
invalidateKeys || (invalidateKeys = []);
|
|
117
|
-
Object.keys(data).forEach(key => {
|
|
118
|
-
if (key === "_$value")
|
|
119
|
-
return;
|
|
120
|
-
keys.push(key);
|
|
121
|
-
cache.set(key, data[key]);
|
|
122
|
-
});
|
|
123
116
|
data = data._$value;
|
|
117
|
+
delete custom._$value;
|
|
118
|
+
flightKeys = Object.keys(custom);
|
|
124
119
|
}
|
|
125
120
|
}
|
|
126
121
|
if (response.headers.has("Location")) {
|
|
@@ -138,7 +133,9 @@ async function handleResponse(response, error, navigate) {
|
|
|
138
133
|
else
|
|
139
134
|
data = response;
|
|
140
135
|
// invalidate
|
|
141
|
-
cacheKeyOp(
|
|
136
|
+
cacheKeyOp(keys, entry => (entry[0] = 0));
|
|
137
|
+
// set cache
|
|
138
|
+
flightKeys && flightKeys.forEach(k => cache.set(k, custom[k]));
|
|
142
139
|
// trigger revalidation
|
|
143
140
|
await revalidate(keys, false);
|
|
144
141
|
return data != null ? { data } : undefined;
|
package/dist/data/cache.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSignal, getListener, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
|
|
2
2
|
import { getRequestEvent, isServer } from "solid-js/web";
|
|
3
|
-
import { useNavigate, getIntent } from "../routing.js";
|
|
3
|
+
import { useNavigate, getIntent, getInLoadFn } from "../routing.js";
|
|
4
4
|
const LocationHeader = "Location";
|
|
5
5
|
const PRELOAD_TIMEOUT = 5000;
|
|
6
6
|
const CACHE_TIMEOUT = 180000;
|
|
@@ -47,6 +47,7 @@ export function cache(fn, name) {
|
|
|
47
47
|
const cachedFn = ((...args) => {
|
|
48
48
|
const cache = getCache();
|
|
49
49
|
const intent = getIntent();
|
|
50
|
+
const inLoadFn = getInLoadFn();
|
|
50
51
|
const owner = getOwner();
|
|
51
52
|
const navigate = owner ? useNavigate() : undefined;
|
|
52
53
|
const now = Date.now();
|
|
@@ -86,13 +87,15 @@ export function cache(fn, name) {
|
|
|
86
87
|
cached[0] = now;
|
|
87
88
|
}
|
|
88
89
|
let res = cached[1];
|
|
89
|
-
if (
|
|
90
|
+
if (!inLoadFn) {
|
|
90
91
|
res =
|
|
91
92
|
"then" in cached[1]
|
|
92
93
|
? cached[1].then(handleResponse(false), handleResponse(true))
|
|
93
94
|
: handleResponse(false)(cached[1]);
|
|
94
95
|
!isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
|
|
95
96
|
}
|
|
97
|
+
else
|
|
98
|
+
"then" in res && res.catch(() => { });
|
|
96
99
|
return res;
|
|
97
100
|
}
|
|
98
101
|
let res = !isServer && sharedConfig.context && sharedConfig.has(key)
|
|
@@ -117,12 +120,14 @@ export function cache(fn, name) {
|
|
|
117
120
|
if (e && e.router.dataOnly)
|
|
118
121
|
return (e.router.data[key] = res);
|
|
119
122
|
}
|
|
120
|
-
if (
|
|
123
|
+
if (!inLoadFn) {
|
|
121
124
|
res =
|
|
122
125
|
"then" in res
|
|
123
126
|
? res.then(handleResponse(false), handleResponse(true))
|
|
124
127
|
: handleResponse(false)(res);
|
|
125
128
|
}
|
|
129
|
+
else
|
|
130
|
+
"then" in res && res.catch(() => { });
|
|
126
131
|
// serialize on server
|
|
127
132
|
if (isServer &&
|
|
128
133
|
sharedConfig.context &&
|
package/dist/data/events.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { RouterContext } from "../types.js";
|
|
2
|
-
export declare function setupNativeEvents(preload?: boolean, explicitLinks?: boolean, actionBase?: string): (router: RouterContext) => void;
|
|
2
|
+
export declare function setupNativeEvents(preload?: boolean, explicitLinks?: boolean, actionBase?: string, transformUrl?: (url: string) => string): (router: RouterContext) => void;
|
package/dist/data/events.js
CHANGED
|
@@ -2,7 +2,7 @@ import { delegateEvents } from "solid-js/web";
|
|
|
2
2
|
import { onCleanup } from "solid-js";
|
|
3
3
|
import { actions } from "./action.js";
|
|
4
4
|
import { mockBase } from "../utils.js";
|
|
5
|
-
export function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
|
|
5
|
+
export function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server", transformUrl) {
|
|
6
6
|
return (router) => {
|
|
7
7
|
const basePath = router.base.path();
|
|
8
8
|
const navigateFromRoute = router.navigatorFactory(router.base);
|
|
@@ -57,6 +57,9 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
|
|
|
57
57
|
if (!res)
|
|
58
58
|
return;
|
|
59
59
|
const [a, url] = res;
|
|
60
|
+
if (typeof transformUrl === "function") {
|
|
61
|
+
url.pathname = transformUrl(url.pathname);
|
|
62
|
+
}
|
|
60
63
|
if (!preloadTimeout[url.pathname])
|
|
61
64
|
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
62
65
|
}
|
|
@@ -65,6 +68,9 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
|
|
|
65
68
|
if (!res)
|
|
66
69
|
return;
|
|
67
70
|
const [a, url] = res;
|
|
71
|
+
if (typeof transformUrl === "function") {
|
|
72
|
+
url.pathname = transformUrl(url.pathname);
|
|
73
|
+
}
|
|
68
74
|
if (preloadTimeout[url.pathname])
|
|
69
75
|
return;
|
|
70
76
|
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
@@ -77,6 +83,9 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
|
|
|
77
83
|
if (!res)
|
|
78
84
|
return;
|
|
79
85
|
const [, url] = res;
|
|
86
|
+
if (typeof transformUrl === "function") {
|
|
87
|
+
url.pathname = transformUrl(url.pathname);
|
|
88
|
+
}
|
|
80
89
|
if (preloadTimeout[url.pathname]) {
|
|
81
90
|
clearTimeout(preloadTimeout[url.pathname]);
|
|
82
91
|
delete preloadTimeout[url.pathname];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./routers/index.js";
|
|
2
2
|
export * from "./components.jsx";
|
|
3
3
|
export * from "./lifecycle.js";
|
|
4
|
-
export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
|
|
4
|
+
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
|
|
5
5
|
export { mergeSearchString as _mergeSearchString } from "./utils.js";
|
|
6
6
|
export * from "./data/index.js";
|
|
7
|
-
export type { Location, LocationChange, NavigateOptions, Navigator, OutputMatch, Params, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouterIntegration, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types.js";
|
|
7
|
+
export type { Location, LocationChange, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, Route, RouteMatch, RouterIntegration, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -256,6 +256,7 @@ const useMatch = (path, matchFilters) => {
|
|
|
256
256
|
}
|
|
257
257
|
});
|
|
258
258
|
};
|
|
259
|
+
const useCurrentMatches = () => useRouter().matches;
|
|
259
260
|
const useParams = () => useRouter().params;
|
|
260
261
|
const useSearchParams = () => {
|
|
261
262
|
const location = useLocation();
|
|
@@ -406,6 +407,13 @@ let intent;
|
|
|
406
407
|
function getIntent() {
|
|
407
408
|
return intent;
|
|
408
409
|
}
|
|
410
|
+
let inLoadFn = false;
|
|
411
|
+
function getInLoadFn() {
|
|
412
|
+
return inLoadFn;
|
|
413
|
+
}
|
|
414
|
+
function setInLoadFn(value) {
|
|
415
|
+
inLoadFn = value;
|
|
416
|
+
}
|
|
409
417
|
function createRouterContext(integration, branches, getContext, options = {}) {
|
|
410
418
|
const {
|
|
411
419
|
signal: [source, setSource],
|
|
@@ -438,7 +446,12 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
438
446
|
const location = createLocation(reference, state);
|
|
439
447
|
const referrers = [];
|
|
440
448
|
const submissions = createSignal(isServer ? initFromFlash() : []);
|
|
441
|
-
const matches = createMemo(() =>
|
|
449
|
+
const matches = createMemo(() => {
|
|
450
|
+
if (typeof options.transformUrl === "function") {
|
|
451
|
+
return getRouteMatches(branches(), options.transformUrl(location.pathname));
|
|
452
|
+
}
|
|
453
|
+
return getRouteMatches(branches(), location.pathname);
|
|
454
|
+
});
|
|
442
455
|
const params = createMemoObject(() => {
|
|
443
456
|
const m = matches();
|
|
444
457
|
const params = {};
|
|
@@ -462,17 +475,15 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
462
475
|
} = source();
|
|
463
476
|
// Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
|
|
464
477
|
untrack(() => {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
});
|
|
475
|
-
}
|
|
478
|
+
start(() => {
|
|
479
|
+
intent = "native";
|
|
480
|
+
if (value !== reference()) setReference(value);
|
|
481
|
+
setState(state);
|
|
482
|
+
resetErrorBoundaries();
|
|
483
|
+
submissions[1]([]);
|
|
484
|
+
}).then(() => {
|
|
485
|
+
intent = undefined;
|
|
486
|
+
});
|
|
476
487
|
});
|
|
477
488
|
});
|
|
478
489
|
return {
|
|
@@ -590,6 +601,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
590
601
|
const {
|
|
591
602
|
load
|
|
592
603
|
} = route;
|
|
604
|
+
inLoadFn = true;
|
|
593
605
|
preloadData && load && runWithOwner(getContext(), () => load({
|
|
594
606
|
params,
|
|
595
607
|
location: {
|
|
@@ -602,6 +614,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
602
614
|
},
|
|
603
615
|
intent: "preload"
|
|
604
616
|
}));
|
|
617
|
+
inLoadFn = false;
|
|
605
618
|
}
|
|
606
619
|
intent = prevIntent;
|
|
607
620
|
}
|
|
@@ -623,11 +636,13 @@ function createRouteContext(router, parent, outlet, match) {
|
|
|
623
636
|
} = match().route;
|
|
624
637
|
const path = createMemo(() => match().path);
|
|
625
638
|
component && component.preload && component.preload();
|
|
639
|
+
inLoadFn = true;
|
|
626
640
|
const data = load ? load({
|
|
627
641
|
params,
|
|
628
642
|
location,
|
|
629
643
|
intent: intent || "initial"
|
|
630
644
|
}) : undefined;
|
|
645
|
+
inLoadFn = false;
|
|
631
646
|
const route = {
|
|
632
647
|
parent,
|
|
633
648
|
pattern,
|
|
@@ -656,7 +671,8 @@ const createRouterComponent = router => props => {
|
|
|
656
671
|
let context;
|
|
657
672
|
const routerState = createRouterContext(router, branches, () => context, {
|
|
658
673
|
base,
|
|
659
|
-
singleFlight: props.singleFlight
|
|
674
|
+
singleFlight: props.singleFlight,
|
|
675
|
+
transformUrl: props.transformUrl
|
|
660
676
|
});
|
|
661
677
|
router.create && router.create(routerState);
|
|
662
678
|
return createComponent$1(RouterContextObj.Provider, {
|
|
@@ -685,11 +701,15 @@ const createRouterComponent = router => props => {
|
|
|
685
701
|
function Root(props) {
|
|
686
702
|
const location = props.routerState.location;
|
|
687
703
|
const params = props.routerState.params;
|
|
688
|
-
const data = createMemo(() => props.load && untrack(() =>
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
704
|
+
const data = createMemo(() => props.load && untrack(() => {
|
|
705
|
+
setInLoadFn(true);
|
|
706
|
+
props.load({
|
|
707
|
+
params,
|
|
708
|
+
location,
|
|
709
|
+
intent: getIntent() || "initial"
|
|
710
|
+
});
|
|
711
|
+
setInLoadFn(false);
|
|
712
|
+
}));
|
|
693
713
|
return createComponent$1(Show, {
|
|
694
714
|
get when() {
|
|
695
715
|
return props.root;
|
|
@@ -821,7 +841,7 @@ function createRouter(config) {
|
|
|
821
841
|
value
|
|
822
842
|
} : value;
|
|
823
843
|
const signal = intercept(createSignal(wrap(config.get()), {
|
|
824
|
-
equals: (a, b) => a.value === b.value
|
|
844
|
+
equals: (a, b) => a.value === b.value && a.state === b.state
|
|
825
845
|
}), undefined, next => {
|
|
826
846
|
!ignore && config.set(next);
|
|
827
847
|
return next;
|
|
@@ -856,8 +876,9 @@ function getPath(url) {
|
|
|
856
876
|
}
|
|
857
877
|
function StaticRouter(props) {
|
|
858
878
|
let e;
|
|
879
|
+
const url = props.url || (e = getRequestEvent()) && getPath(e.request.url) || "";
|
|
859
880
|
const obj = {
|
|
860
|
-
value: props.
|
|
881
|
+
value: props.transformUrl ? props.transformUrl(url) : url
|
|
861
882
|
};
|
|
862
883
|
return createRouterComponent({
|
|
863
884
|
signal: [() => obj, next => Object.assign(obj, next)]
|
|
@@ -907,6 +928,7 @@ function cache(fn, name) {
|
|
|
907
928
|
const cachedFn = (...args) => {
|
|
908
929
|
const cache = getCache();
|
|
909
930
|
const intent = getIntent();
|
|
931
|
+
const inLoadFn = getInLoadFn();
|
|
910
932
|
const owner = getOwner();
|
|
911
933
|
const navigate = owner ? useNavigate() : undefined;
|
|
912
934
|
const now = Date.now();
|
|
@@ -940,10 +962,10 @@ function cache(fn, name) {
|
|
|
940
962
|
cached[0] = now;
|
|
941
963
|
}
|
|
942
964
|
let res = cached[1];
|
|
943
|
-
if (
|
|
965
|
+
if (!inLoadFn) {
|
|
944
966
|
res = "then" in cached[1] ? cached[1].then(handleResponse(false), handleResponse(true)) : handleResponse(false)(cached[1]);
|
|
945
967
|
!isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
|
|
946
|
-
}
|
|
968
|
+
} else "then" in res && res.catch(() => {});
|
|
947
969
|
return res;
|
|
948
970
|
}
|
|
949
971
|
let res = !isServer && sharedConfig.context && sharedConfig.has(key) ? sharedConfig.load(key) // hydrating
|
|
@@ -965,9 +987,9 @@ function cache(fn, name) {
|
|
|
965
987
|
const e = getRequestEvent();
|
|
966
988
|
if (e && e.router.dataOnly) return e.router.data[key] = res;
|
|
967
989
|
}
|
|
968
|
-
if (
|
|
990
|
+
if (!inLoadFn) {
|
|
969
991
|
res = "then" in res ? res.then(handleResponse(false), handleResponse(true)) : handleResponse(false)(res);
|
|
970
|
-
}
|
|
992
|
+
} else "then" in res && res.catch(() => {});
|
|
971
993
|
// serialize on server
|
|
972
994
|
if (isServer && sharedConfig.context && sharedConfig.context.async && !sharedConfig.context.noHydrate) {
|
|
973
995
|
const e = getRequestEvent();
|
|
@@ -1132,21 +1154,17 @@ function toAction(fn, url) {
|
|
|
1132
1154
|
const hashString = s => s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
|
|
1133
1155
|
async function handleResponse(response, error, navigate) {
|
|
1134
1156
|
let data;
|
|
1157
|
+
let custom;
|
|
1135
1158
|
let keys;
|
|
1136
|
-
let
|
|
1159
|
+
let flightKeys;
|
|
1137
1160
|
if (response instanceof Response) {
|
|
1138
|
-
if (response.headers.has("X-Revalidate")) keys =
|
|
1161
|
+
if (response.headers.has("X-Revalidate")) keys = response.headers.get("X-Revalidate").split(",");
|
|
1139
1162
|
if (response.customBody) {
|
|
1140
|
-
data = await response.customBody();
|
|
1163
|
+
data = custom = await response.customBody();
|
|
1141
1164
|
if (response.headers.has("X-Single-Flight")) {
|
|
1142
|
-
keys || (keys = []);
|
|
1143
|
-
invalidateKeys || (invalidateKeys = []);
|
|
1144
|
-
Object.keys(data).forEach(key => {
|
|
1145
|
-
if (key === "_$value") return;
|
|
1146
|
-
keys.push(key);
|
|
1147
|
-
cache.set(key, data[key]);
|
|
1148
|
-
});
|
|
1149
1165
|
data = data._$value;
|
|
1166
|
+
delete custom._$value;
|
|
1167
|
+
flightKeys = Object.keys(custom);
|
|
1150
1168
|
}
|
|
1151
1169
|
}
|
|
1152
1170
|
if (response.headers.has("Location")) {
|
|
@@ -1161,7 +1179,9 @@ async function handleResponse(response, error, navigate) {
|
|
|
1161
1179
|
error: response
|
|
1162
1180
|
};else data = response;
|
|
1163
1181
|
// invalidate
|
|
1164
|
-
cacheKeyOp(
|
|
1182
|
+
cacheKeyOp(keys, entry => entry[0] = 0);
|
|
1183
|
+
// set cache
|
|
1184
|
+
flightKeys && flightKeys.forEach(k => cache.set(k, custom[k]));
|
|
1165
1185
|
// trigger revalidation
|
|
1166
1186
|
await revalidate(keys, false);
|
|
1167
1187
|
return data != null ? {
|
|
@@ -1169,7 +1189,7 @@ async function handleResponse(response, error, navigate) {
|
|
|
1169
1189
|
} : undefined;
|
|
1170
1190
|
}
|
|
1171
1191
|
|
|
1172
|
-
function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
|
|
1192
|
+
function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server", transformUrl) {
|
|
1173
1193
|
return router => {
|
|
1174
1194
|
const basePath = router.base.path();
|
|
1175
1195
|
const navigateFromRoute = router.navigatorFactory(router.base);
|
|
@@ -1209,12 +1229,18 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1209
1229
|
const res = handleAnchor(evt);
|
|
1210
1230
|
if (!res) return;
|
|
1211
1231
|
const [a, url] = res;
|
|
1232
|
+
if (typeof transformUrl === "function") {
|
|
1233
|
+
url.pathname = transformUrl(url.pathname);
|
|
1234
|
+
}
|
|
1212
1235
|
if (!preloadTimeout[url.pathname]) router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
1213
1236
|
}
|
|
1214
1237
|
function handleAnchorIn(evt) {
|
|
1215
1238
|
const res = handleAnchor(evt);
|
|
1216
1239
|
if (!res) return;
|
|
1217
1240
|
const [a, url] = res;
|
|
1241
|
+
if (typeof transformUrl === "function") {
|
|
1242
|
+
url.pathname = transformUrl(url.pathname);
|
|
1243
|
+
}
|
|
1218
1244
|
if (preloadTimeout[url.pathname]) return;
|
|
1219
1245
|
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
1220
1246
|
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
@@ -1225,6 +1251,9 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1225
1251
|
const res = handleAnchor(evt);
|
|
1226
1252
|
if (!res) return;
|
|
1227
1253
|
const [, url] = res;
|
|
1254
|
+
if (typeof transformUrl === "function") {
|
|
1255
|
+
url.pathname = transformUrl(url.pathname);
|
|
1256
|
+
}
|
|
1228
1257
|
if (preloadTimeout[url.pathname]) {
|
|
1229
1258
|
clearTimeout(preloadTimeout[url.pathname]);
|
|
1230
1259
|
delete preloadTimeout[url.pathname];
|
|
@@ -1278,10 +1307,13 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1278
1307
|
|
|
1279
1308
|
function Router(props) {
|
|
1280
1309
|
if (isServer) return StaticRouter(props);
|
|
1281
|
-
const getSource = () =>
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1310
|
+
const getSource = () => {
|
|
1311
|
+
const url = window.location.pathname + window.location.search;
|
|
1312
|
+
return {
|
|
1313
|
+
value: props.transformUrl ? props.transformUrl(url) + window.location.hash : url + window.location.hash,
|
|
1314
|
+
state: window.history.state
|
|
1315
|
+
};
|
|
1316
|
+
};
|
|
1285
1317
|
const beforeLeave = createBeforeLeave();
|
|
1286
1318
|
return createRouter({
|
|
1287
1319
|
get: getSource,
|
|
@@ -1309,7 +1341,7 @@ function Router(props) {
|
|
|
1309
1341
|
});
|
|
1310
1342
|
}
|
|
1311
1343
|
})),
|
|
1312
|
-
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
|
|
1344
|
+
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase, props.transformUrl),
|
|
1313
1345
|
utils: {
|
|
1314
1346
|
go: delta => window.history.go(delta),
|
|
1315
1347
|
beforeLeave
|
|
@@ -1342,7 +1374,7 @@ function HashRouter(props) {
|
|
|
1342
1374
|
if (replace) {
|
|
1343
1375
|
window.history.replaceState(keepDepth(state), "", "#" + value);
|
|
1344
1376
|
} else {
|
|
1345
|
-
window.
|
|
1377
|
+
window.history.pushState(state, "", "#" + value);
|
|
1346
1378
|
}
|
|
1347
1379
|
const hashIndex = value.indexOf("#");
|
|
1348
1380
|
const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
|
|
@@ -1434,7 +1466,7 @@ function A(props) {
|
|
|
1434
1466
|
if (to_ === undefined) return [false, false];
|
|
1435
1467
|
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
|
|
1436
1468
|
const loc = normalizePath(location.pathname).toLowerCase();
|
|
1437
|
-
return [props.end ? path === loc : loc.startsWith(path), path === loc];
|
|
1469
|
+
return [props.end ? path === loc : loc.startsWith(path + "/") || loc === path, path === loc];
|
|
1438
1470
|
});
|
|
1439
1471
|
return (() => {
|
|
1440
1472
|
const _el$ = _tmpl$();
|
|
@@ -1607,4 +1639,4 @@ function json(data, init = {}) {
|
|
|
1607
1639
|
return response;
|
|
1608
1640
|
}
|
|
1609
1641
|
|
|
1610
|
-
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|
|
1642
|
+
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|
package/dist/index.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./routers/index.js";
|
|
2
2
|
export * from "./components.jsx";
|
|
3
3
|
export * from "./lifecycle.js";
|
|
4
|
-
export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
|
|
4
|
+
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
|
|
5
5
|
export { mergeSearchString as _mergeSearchString } from "./utils.js";
|
|
6
6
|
export * from "./data/index.js";
|
|
@@ -22,7 +22,7 @@ export function HashRouter(props) {
|
|
|
22
22
|
window.history.replaceState(keepDepth(state), "", "#" + value);
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
|
-
window.
|
|
25
|
+
window.history.pushState(state, "", "#" + value);
|
|
26
26
|
}
|
|
27
27
|
const hashIndex = value.indexOf("#");
|
|
28
28
|
const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
|
package/dist/routers/Router.js
CHANGED
|
@@ -6,10 +6,13 @@ import { createBeforeLeave, keepDepth, notifyIfNotBlocked, saveCurrentDepth } fr
|
|
|
6
6
|
export function Router(props) {
|
|
7
7
|
if (isServer)
|
|
8
8
|
return StaticRouter(props);
|
|
9
|
-
const getSource = () =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const getSource = () => {
|
|
10
|
+
const url = window.location.pathname + window.location.search;
|
|
11
|
+
return {
|
|
12
|
+
value: props.transformUrl ? props.transformUrl(url) + window.location.hash : url + window.location.hash,
|
|
13
|
+
state: window.history.state
|
|
14
|
+
};
|
|
15
|
+
};
|
|
13
16
|
const beforeLeave = createBeforeLeave();
|
|
14
17
|
return createRouter({
|
|
15
18
|
get: getSource,
|
|
@@ -32,7 +35,7 @@ export function Router(props) {
|
|
|
32
35
|
return !beforeLeave.confirm(s.value, { state: s.state });
|
|
33
36
|
}
|
|
34
37
|
})),
|
|
35
|
-
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
|
|
38
|
+
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase, props.transformUrl),
|
|
36
39
|
utils: {
|
|
37
40
|
go: delta => window.history.go(delta),
|
|
38
41
|
beforeLeave
|
|
@@ -6,8 +6,9 @@ function getPath(url) {
|
|
|
6
6
|
}
|
|
7
7
|
export function StaticRouter(props) {
|
|
8
8
|
let e;
|
|
9
|
+
const url = props.url || ((e = getRequestEvent()) && getPath(e.request.url)) || "";
|
|
9
10
|
const obj = {
|
|
10
|
-
value: props.
|
|
11
|
+
value: props.transformUrl ? props.transformUrl(url) : url,
|
|
11
12
|
};
|
|
12
13
|
return createRouterComponent({
|
|
13
14
|
signal: [() => obj, next => Object.assign(obj, next)]
|
|
@@ -9,6 +9,7 @@ export type BaseRouterProps = {
|
|
|
9
9
|
rootLoad?: RouteLoadFunc;
|
|
10
10
|
singleFlight?: boolean;
|
|
11
11
|
children?: JSX.Element | RouteDefinition | RouteDefinition[];
|
|
12
|
+
transformUrl?: (url: string) => string;
|
|
12
13
|
};
|
|
13
14
|
export declare const createRouterComponent: (router: RouterIntegration) => (props: BaseRouterProps) => JSX.Element;
|
|
14
15
|
export type RouteProps<S extends string, T = unknown> = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*@refresh skip*/
|
|
2
2
|
import { getRequestEvent, isServer } from "solid-js/web";
|
|
3
3
|
import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
|
|
4
|
-
import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj } from "../routing.js";
|
|
4
|
+
import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInLoadFn } from "../routing.js";
|
|
5
5
|
export const createRouterComponent = (router) => (props) => {
|
|
6
6
|
const { base } = props;
|
|
7
7
|
const routeDefs = children(() => props.children);
|
|
@@ -9,7 +9,8 @@ export const createRouterComponent = (router) => (props) => {
|
|
|
9
9
|
let context;
|
|
10
10
|
const routerState = createRouterContext(router, branches, () => context, {
|
|
11
11
|
base,
|
|
12
|
-
singleFlight: props.singleFlight
|
|
12
|
+
singleFlight: props.singleFlight,
|
|
13
|
+
transformUrl: props.transformUrl,
|
|
13
14
|
});
|
|
14
15
|
router.create && router.create(routerState);
|
|
15
16
|
return (<RouterContextObj.Provider value={routerState}>
|
|
@@ -22,7 +23,12 @@ export const createRouterComponent = (router) => (props) => {
|
|
|
22
23
|
function Root(props) {
|
|
23
24
|
const location = props.routerState.location;
|
|
24
25
|
const params = props.routerState.params;
|
|
25
|
-
const data = createMemo(() => props.load &&
|
|
26
|
+
const data = createMemo(() => props.load &&
|
|
27
|
+
untrack(() => {
|
|
28
|
+
setInLoadFn(true);
|
|
29
|
+
props.load({ params, location, intent: getIntent() || "initial" });
|
|
30
|
+
setInLoadFn(false);
|
|
31
|
+
}));
|
|
26
32
|
return (<Show when={props.root} keyed fallback={props.children}>
|
|
27
33
|
{Root => (<Root params={params} location={location} data={data()}>
|
|
28
34
|
{props.children}
|
|
@@ -18,7 +18,9 @@ function querySelector(selector) {
|
|
|
18
18
|
export function createRouter(config) {
|
|
19
19
|
let ignore = false;
|
|
20
20
|
const wrap = (value) => (typeof value === "string" ? { value } : value);
|
|
21
|
-
const signal = intercept(createSignal(wrap(config.get()), {
|
|
21
|
+
const signal = intercept(createSignal(wrap(config.get()), {
|
|
22
|
+
equals: (a, b) => a.value === b.value && a.state === b.state
|
|
23
|
+
}), undefined, next => {
|
|
22
24
|
!ignore && config.set(next);
|
|
23
25
|
return next;
|
|
24
26
|
});
|
package/dist/routing.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare const useNavigate: () => Navigator;
|
|
|
10
10
|
export declare const useLocation: <S = unknown>() => Location<S>;
|
|
11
11
|
export declare const useIsRouting: () => () => boolean;
|
|
12
12
|
export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types.js").PathMatch | undefined>;
|
|
13
|
+
export declare const useCurrentMatches: () => () => RouteMatch[];
|
|
13
14
|
export declare const useParams: <T extends Params>() => T;
|
|
14
15
|
export declare const useSearchParams: <T extends Params>() => [Partial<T>, (params: SetParams, options?: Partial<NavigateOptions>) => void];
|
|
15
16
|
export declare const useBeforeLeave: (listener: (e: BeforeLeaveEventArgs) => void) => void;
|
|
@@ -19,8 +20,11 @@ export declare function createBranches(routeDef: RouteDefinition | RouteDefiniti
|
|
|
19
20
|
export declare function getRouteMatches(branches: Branch[], location: string): RouteMatch[];
|
|
20
21
|
export declare function createLocation(path: Accessor<string>, state: Accessor<any>): Location;
|
|
21
22
|
export declare function getIntent(): Intent | undefined;
|
|
23
|
+
export declare function getInLoadFn(): boolean;
|
|
24
|
+
export declare function setInLoadFn(value: boolean): void;
|
|
22
25
|
export declare function createRouterContext(integration: RouterIntegration, branches: () => Branch[], getContext?: () => any, options?: {
|
|
23
26
|
base?: string;
|
|
24
27
|
singleFlight?: boolean;
|
|
28
|
+
transformUrl?: (url: string) => string;
|
|
25
29
|
}): RouterContext;
|
|
26
30
|
export declare function createRouteContext(router: RouterContext, parent: RouteContext, outlet: () => JSX.Element, match: () => RouteMatch): RouteContext;
|
package/dist/routing.js
CHANGED
|
@@ -34,6 +34,7 @@ export const useMatch = (path, matchFilters) => {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
};
|
|
37
|
+
export const useCurrentMatches = () => useRouter().matches;
|
|
37
38
|
export const useParams = () => useRouter().params;
|
|
38
39
|
export const useSearchParams = () => {
|
|
39
40
|
const location = useLocation();
|
|
@@ -184,6 +185,13 @@ let intent;
|
|
|
184
185
|
export function getIntent() {
|
|
185
186
|
return intent;
|
|
186
187
|
}
|
|
188
|
+
let inLoadFn = false;
|
|
189
|
+
export function getInLoadFn() {
|
|
190
|
+
return inLoadFn;
|
|
191
|
+
}
|
|
192
|
+
export function setInLoadFn(value) {
|
|
193
|
+
inLoadFn = value;
|
|
194
|
+
}
|
|
187
195
|
export function createRouterContext(integration, branches, getContext, options = {}) {
|
|
188
196
|
const { signal: [source, setSource], utils = {} } = integration;
|
|
189
197
|
const parsePath = utils.parsePath || (p => p);
|
|
@@ -211,7 +219,12 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
211
219
|
const location = createLocation(reference, state);
|
|
212
220
|
const referrers = [];
|
|
213
221
|
const submissions = createSignal(isServer ? initFromFlash() : []);
|
|
214
|
-
const matches = createMemo(() =>
|
|
222
|
+
const matches = createMemo(() => {
|
|
223
|
+
if (typeof options.transformUrl === "function") {
|
|
224
|
+
return getRouteMatches(branches(), options.transformUrl(location.pathname));
|
|
225
|
+
}
|
|
226
|
+
return getRouteMatches(branches(), location.pathname);
|
|
227
|
+
});
|
|
215
228
|
const params = createMemoObject(() => {
|
|
216
229
|
const m = matches();
|
|
217
230
|
const params = {};
|
|
@@ -232,17 +245,16 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
232
245
|
const { value, state } = source();
|
|
233
246
|
// Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
|
|
234
247
|
untrack(() => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
start(() => {
|
|
249
|
+
intent = "native";
|
|
250
|
+
if (value !== reference())
|
|
238
251
|
setReference(value);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
252
|
+
setState(state);
|
|
253
|
+
resetErrorBoundaries();
|
|
254
|
+
submissions[1]([]);
|
|
255
|
+
}).then(() => {
|
|
256
|
+
intent = undefined;
|
|
257
|
+
});
|
|
246
258
|
});
|
|
247
259
|
});
|
|
248
260
|
return {
|
|
@@ -343,6 +355,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
343
355
|
route.component.preload &&
|
|
344
356
|
route.component.preload();
|
|
345
357
|
const { load } = route;
|
|
358
|
+
inLoadFn = true;
|
|
346
359
|
preloadData &&
|
|
347
360
|
load &&
|
|
348
361
|
runWithOwner(getContext(), () => load({
|
|
@@ -357,6 +370,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
357
370
|
},
|
|
358
371
|
intent: "preload"
|
|
359
372
|
}));
|
|
373
|
+
inLoadFn = false;
|
|
360
374
|
}
|
|
361
375
|
intent = prevIntent;
|
|
362
376
|
}
|
|
@@ -374,7 +388,9 @@ export function createRouteContext(router, parent, outlet, match) {
|
|
|
374
388
|
component &&
|
|
375
389
|
component.preload &&
|
|
376
390
|
component.preload();
|
|
391
|
+
inLoadFn = true;
|
|
377
392
|
const data = load ? load({ params, location, intent: intent || "initial" }) : undefined;
|
|
393
|
+
inLoadFn = false;
|
|
378
394
|
const route = {
|
|
379
395
|
parent,
|
|
380
396
|
pattern,
|
package/dist/types.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export type RouteLoadFunc<T = unknown> = (args: RouteLoadFuncArgs) => T;
|
|
|
65
65
|
export interface RouteSectionProps<T = unknown> {
|
|
66
66
|
params: Params;
|
|
67
67
|
location: Location;
|
|
68
|
-
data
|
|
68
|
+
data: T;
|
|
69
69
|
children?: JSX.Element;
|
|
70
70
|
}
|
|
71
71
|
export type RouteDefinition<S extends string | string[] = any, T = unknown> = {
|