@solidjs/router 0.10.0-beta.4 → 0.10.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -9
- package/dist/components.d.ts +2 -24
- package/dist/components.jsx +3 -84
- package/dist/data/action.d.ts +1 -0
- package/dist/data/action.js +22 -29
- package/dist/data/cache.d.ts +6 -2
- package/dist/data/cache.js +55 -12
- package/dist/data/events.d.ts +2 -0
- package/dist/data/events.js +116 -0
- package/dist/data/index.d.ts +2 -2
- package/dist/data/response.d.ts +5 -1
- package/dist/data/response.js +17 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +521 -472
- package/dist/index.jsx +1 -1
- package/dist/routers/HashRouter.d.ts +5 -0
- package/dist/routers/HashRouter.js +36 -0
- package/dist/routers/MemoryRouter.d.ts +21 -0
- package/dist/routers/MemoryRouter.js +52 -0
- package/dist/routers/Router.d.ts +6 -0
- package/dist/routers/Router.js +28 -0
- package/dist/routers/StaticRouter.d.ts +6 -0
- package/dist/routers/StaticRouter.js +15 -0
- package/dist/routers/components.d.ts +17 -0
- package/dist/routers/components.jsx +70 -0
- package/dist/routers/createIntegration.d.ts +4 -0
- package/dist/routers/createIntegration.js +49 -0
- package/dist/routers/createIntegration.jsx +48 -0
- package/dist/routers/createRouter.d.ts +10 -0
- package/dist/routers/createRouter.js +49 -0
- package/dist/routers/index.d.ts +11 -0
- package/dist/routers/index.js +6 -0
- package/dist/routing.d.ts +2 -3
- package/dist/routing.js +53 -166
- package/dist/types.d.ts +9 -4
- package/package.json +1 -1
- package/dist/integration.d.ts +0 -15
- package/dist/integration.js +0 -154
package/README.md
CHANGED
|
@@ -382,12 +382,12 @@ const user = createAsync(() => getUser(params.id))
|
|
|
382
382
|
|
|
383
383
|
Actions are data mutations that can trigger invalidations and further routing. A list of prebuilt response builders can be found below(TODO).
|
|
384
384
|
```jsx
|
|
385
|
+
import { action, revalidate, redirect } from "@solidjs/router"
|
|
386
|
+
|
|
385
387
|
// anywhere
|
|
386
388
|
const myAction = action(async (data) => {
|
|
387
389
|
await doMutation(data);
|
|
388
|
-
|
|
389
|
-
invalidate: [getUser, data.id]
|
|
390
|
-
}) // returns a response
|
|
390
|
+
throw redirect("/", { revalidate: getUser.keyFor(data.id) }); // throw a response to do a redirect
|
|
391
391
|
});
|
|
392
392
|
|
|
393
393
|
// in component
|
|
@@ -456,12 +456,12 @@ function loadUser({params, location}) {
|
|
|
456
456
|
<Route path="/users/:id" component={User} load={loadUser} />;
|
|
457
457
|
```
|
|
458
458
|
|
|
459
|
-
The load function is called when the Route is loaded or eagerly when links are hovered. Inside your page component you
|
|
459
|
+
The load function is called when the Route is loaded or eagerly when links are hovered. Inside your page component you:
|
|
460
460
|
|
|
461
461
|
```jsx
|
|
462
462
|
// pages/users/[id].js
|
|
463
463
|
import { getUser } from ... // the cache function
|
|
464
|
-
|
|
464
|
+
|
|
465
465
|
export default function User(props) {
|
|
466
466
|
const user = createAsync(() => getUser(props.params.id));
|
|
467
467
|
return <h1>{user().name}</h1>;
|
|
@@ -542,9 +542,9 @@ render(() =>
|
|
|
542
542
|
By default, Solid Router uses `location.pathname` as route path. You can simply switch to hash mode through the `source` property on `<Router>` component.
|
|
543
543
|
|
|
544
544
|
```jsx
|
|
545
|
-
import {
|
|
545
|
+
import { HashRouter } from "@solidjs/router";
|
|
546
546
|
|
|
547
|
-
<
|
|
547
|
+
<HashRouter />;
|
|
548
548
|
```
|
|
549
549
|
|
|
550
550
|
### Memory Mode Router
|
|
@@ -552,11 +552,23 @@ import { Router, hashIntegration } from "@solidjs/router";
|
|
|
552
552
|
You can also use memory mode router for testing purpose.
|
|
553
553
|
|
|
554
554
|
```jsx
|
|
555
|
-
import {
|
|
555
|
+
import { MemoryRouter } from "@solidjs/router";
|
|
556
|
+
|
|
557
|
+
<MemoryRouter />;
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### SSR Routing
|
|
561
|
+
|
|
562
|
+
For SSR you can use the static router directly or the browser Router defaults to it on the server, just pass in the url.
|
|
563
|
+
|
|
564
|
+
```jsx
|
|
565
|
+
import { isServer } from "solid-js/web";
|
|
566
|
+
import { Router } from "@solidjs/router";
|
|
556
567
|
|
|
557
|
-
<Router
|
|
568
|
+
<Router url={isServer ? req.url : ""} />;
|
|
558
569
|
```
|
|
559
570
|
|
|
571
|
+
|
|
560
572
|
## Components
|
|
561
573
|
|
|
562
574
|
### `<A>`
|
package/dist/components.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Location,
|
|
1
|
+
import type { JSX } from "solid-js";
|
|
2
|
+
import type { Location, Navigator } from "./types";
|
|
3
3
|
declare module "solid-js" {
|
|
4
4
|
namespace JSX {
|
|
5
5
|
interface AnchorHTMLAttributes<T> {
|
|
@@ -10,27 +10,6 @@ declare module "solid-js" {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
export type RouterProps = {
|
|
14
|
-
base?: string;
|
|
15
|
-
actionBase?: string;
|
|
16
|
-
root?: Component<RouteSectionProps>;
|
|
17
|
-
children: JSX.Element;
|
|
18
|
-
} & ({
|
|
19
|
-
url?: never;
|
|
20
|
-
source?: RouterIntegration | LocationChangeSignal;
|
|
21
|
-
} | {
|
|
22
|
-
source?: never;
|
|
23
|
-
url: string;
|
|
24
|
-
});
|
|
25
|
-
export declare const Router: (props: RouterProps) => JSX.Element;
|
|
26
|
-
export type RouteProps<S extends string> = {
|
|
27
|
-
path?: S | S[];
|
|
28
|
-
children?: JSX.Element;
|
|
29
|
-
load?: RouteLoadFunc;
|
|
30
|
-
matchFilters?: MatchFilters<S>;
|
|
31
|
-
component?: Component;
|
|
32
|
-
};
|
|
33
|
-
export declare const Route: <S extends string>(props: RouteProps<S>) => JSX.Element;
|
|
34
13
|
export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, "state"> {
|
|
35
14
|
href: string;
|
|
36
15
|
replace?: boolean | undefined;
|
|
@@ -41,7 +20,6 @@ export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorEle
|
|
|
41
20
|
end?: boolean | undefined;
|
|
42
21
|
}
|
|
43
22
|
export declare function A(props: AnchorProps): JSX.Element;
|
|
44
|
-
export { A as Link, A as NavLink, AnchorProps as LinkProps, AnchorProps as NavLinkProps };
|
|
45
23
|
export interface NavigateProps {
|
|
46
24
|
href: ((args: {
|
|
47
25
|
navigate: Navigator;
|
package/dist/components.jsx
CHANGED
|
@@ -1,85 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { pathIntegration, staticIntegration } from "./integration";
|
|
5
|
-
import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj, useHref, useLocation, useNavigate, useResolvedPath } from "./routing";
|
|
6
|
-
import { normalizePath, createMemoObject } from "./utils";
|
|
7
|
-
export const Router = (props) => {
|
|
8
|
-
let e;
|
|
9
|
-
const { source, url, base, actionBase } = props;
|
|
10
|
-
const integration = source ||
|
|
11
|
-
(isServer
|
|
12
|
-
? staticIntegration({
|
|
13
|
-
value: url || ((e = getRequestEvent()) && getPath(e.request.url)) || ""
|
|
14
|
-
})
|
|
15
|
-
: pathIntegration());
|
|
16
|
-
const routeDefs = children(() => props.children);
|
|
17
|
-
const branches = createMemo(() => createBranches(props.root ? { component: props.root, children: routeDefs() } : routeDefs(), props.base || ""));
|
|
18
|
-
const routerState = createRouterContext(integration, branches, { base, actionBase });
|
|
19
|
-
return (<RouterContextObj.Provider value={routerState}>
|
|
20
|
-
<Routes routerState={routerState} branches={branches()}/>
|
|
21
|
-
</RouterContextObj.Provider>);
|
|
22
|
-
};
|
|
23
|
-
function getPath(url) {
|
|
24
|
-
const u = new URL(url);
|
|
25
|
-
return u.pathname + u.search;
|
|
26
|
-
}
|
|
27
|
-
function Routes(props) {
|
|
28
|
-
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
29
|
-
const params = createMemoObject(() => {
|
|
30
|
-
const m = matches();
|
|
31
|
-
const params = {};
|
|
32
|
-
for (let i = 0; i < m.length; i++) {
|
|
33
|
-
Object.assign(params, m[i].params);
|
|
34
|
-
}
|
|
35
|
-
return params;
|
|
36
|
-
});
|
|
37
|
-
const disposers = [];
|
|
38
|
-
let root;
|
|
39
|
-
const routeStates = createMemo(on(matches, (nextMatches, prevMatches, prev) => {
|
|
40
|
-
let equal = prevMatches && nextMatches.length === prevMatches.length;
|
|
41
|
-
const next = [];
|
|
42
|
-
for (let i = 0, len = nextMatches.length; i < len; i++) {
|
|
43
|
-
const prevMatch = prevMatches && prevMatches[i];
|
|
44
|
-
const nextMatch = nextMatches[i];
|
|
45
|
-
if (prev && prevMatch && nextMatch.route.key === prevMatch.route.key) {
|
|
46
|
-
next[i] = prev[i];
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
equal = false;
|
|
50
|
-
if (disposers[i]) {
|
|
51
|
-
disposers[i]();
|
|
52
|
-
}
|
|
53
|
-
createRoot(dispose => {
|
|
54
|
-
disposers[i] = dispose;
|
|
55
|
-
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => matches()[i], params);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
disposers.splice(nextMatches.length).forEach(dispose => dispose());
|
|
60
|
-
if (prev && equal) {
|
|
61
|
-
return prev;
|
|
62
|
-
}
|
|
63
|
-
root = next[0];
|
|
64
|
-
return next;
|
|
65
|
-
}));
|
|
66
|
-
return (<Show when={routeStates() && root} keyed>
|
|
67
|
-
{route => <RouteContextObj.Provider value={route}>{route.outlet()}</RouteContextObj.Provider>}
|
|
68
|
-
</Show>);
|
|
69
|
-
}
|
|
70
|
-
const createOutlet = (child) => {
|
|
71
|
-
return () => (<Show when={child()} keyed>
|
|
72
|
-
{child => <RouteContextObj.Provider value={child}>{child.outlet()}</RouteContextObj.Provider>}
|
|
73
|
-
</Show>);
|
|
74
|
-
};
|
|
75
|
-
export const Route = (props) => {
|
|
76
|
-
const childRoutes = children(() => props.children);
|
|
77
|
-
return mergeProps(props, {
|
|
78
|
-
get children() {
|
|
79
|
-
return childRoutes();
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
};
|
|
1
|
+
import { createMemo, mergeProps, splitProps } from "solid-js";
|
|
2
|
+
import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing";
|
|
3
|
+
import { normalizePath } from "./utils";
|
|
83
4
|
export function A(props) {
|
|
84
5
|
props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
|
|
85
6
|
const [, rest] = splitProps(props, [
|
|
@@ -108,8 +29,6 @@ export function A(props) {
|
|
|
108
29
|
...rest.classList
|
|
109
30
|
}} aria-current={isActive() ? "page" : undefined}/>);
|
|
110
31
|
}
|
|
111
|
-
// deprecated alias exports
|
|
112
|
-
export { A as Link, A as NavLink };
|
|
113
32
|
export function Navigate(props) {
|
|
114
33
|
const navigate = useNavigate();
|
|
115
34
|
const location = useLocation();
|
package/dist/data/action.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JSX } from "solid-js";
|
|
2
2
|
import { Submission } from "../types";
|
|
3
3
|
export type Action<T, U> = ((vars: T) => Promise<U>) & JSX.SerializableAttributeValue;
|
|
4
|
+
export declare const actions: Map<string, Function>;
|
|
4
5
|
export declare function useSubmissions<T, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U>[] & {
|
|
5
6
|
pending: boolean;
|
|
6
7
|
};
|
package/dist/data/action.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { $TRACK, createMemo, createSignal } from "solid-js";
|
|
2
2
|
import { isServer } from "solid-js/web";
|
|
3
|
-
import {
|
|
3
|
+
import { useRouter } from "../routing";
|
|
4
4
|
import { redirectStatusCodes } from "../utils";
|
|
5
5
|
import { revalidate } from "./cache";
|
|
6
|
+
export const actions = /* #__PURE__ */ new Map();
|
|
6
7
|
export function useSubmissions(fn, filter) {
|
|
7
8
|
const router = useRouter();
|
|
8
9
|
const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.toString() && (!filter || filter(s.input))));
|
|
@@ -18,26 +19,11 @@ export function useSubmissions(fn, filter) {
|
|
|
18
19
|
}
|
|
19
20
|
export function useSubmission(fn, filter) {
|
|
20
21
|
const submissions = useSubmissions(fn, filter);
|
|
21
|
-
return {
|
|
22
|
-
get
|
|
23
|
-
return submissions[submissions.length - 1]?.
|
|
24
|
-
},
|
|
25
|
-
get retry() {
|
|
26
|
-
return submissions[submissions.length - 1]?.retry;
|
|
27
|
-
},
|
|
28
|
-
get url() {
|
|
29
|
-
return submissions[submissions.length - 1]?.url;
|
|
30
|
-
},
|
|
31
|
-
get input() {
|
|
32
|
-
return submissions[submissions.length - 1]?.input;
|
|
33
|
-
},
|
|
34
|
-
get result() {
|
|
35
|
-
return submissions[submissions.length - 1]?.result;
|
|
36
|
-
},
|
|
37
|
-
get pending() {
|
|
38
|
-
return submissions[submissions.length - 1]?.pending;
|
|
22
|
+
return new Proxy({}, {
|
|
23
|
+
get(_, property) {
|
|
24
|
+
return submissions[submissions.length - 1]?.[property];
|
|
39
25
|
}
|
|
40
|
-
};
|
|
26
|
+
});
|
|
41
27
|
}
|
|
42
28
|
export function useAction(action) {
|
|
43
29
|
const router = useRouter();
|
|
@@ -86,23 +72,30 @@ export function action(fn, name) {
|
|
|
86
72
|
return url;
|
|
87
73
|
};
|
|
88
74
|
if (!isServer)
|
|
89
|
-
|
|
75
|
+
actions.set(url, mutate);
|
|
90
76
|
return mutate;
|
|
91
77
|
}
|
|
92
78
|
async function handleResponse(response, navigate) {
|
|
93
79
|
let data;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
80
|
+
let keys;
|
|
81
|
+
if (response instanceof Response) {
|
|
82
|
+
if (response.headers.has("X-Revalidate")) {
|
|
83
|
+
keys = response.headers.get("X-Revalidate").split(",");
|
|
98
84
|
}
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
if (response.customBody)
|
|
86
|
+
data = await response.customBody();
|
|
87
|
+
if (redirectStatusCodes.has(response.status)) {
|
|
88
|
+
const locationUrl = response.headers.get("Location") || "/";
|
|
89
|
+
if (locationUrl.startsWith("http")) {
|
|
90
|
+
window.location.href = locationUrl;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
navigate(locationUrl);
|
|
94
|
+
}
|
|
101
95
|
}
|
|
102
96
|
}
|
|
103
97
|
else
|
|
104
98
|
data = response;
|
|
105
|
-
|
|
106
|
-
await revalidate();
|
|
99
|
+
await revalidate(keys);
|
|
107
100
|
return data;
|
|
108
101
|
}
|
package/dist/data/cache.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { type ReconcileOptions } from "solid-js/store";
|
|
2
|
-
export declare function revalidate(key?: string |
|
|
3
|
-
export
|
|
2
|
+
export declare function revalidate(key?: string | string[] | void): Promise<void>;
|
|
3
|
+
export type CachedFunction<T extends (...args: any) => U | Response, U> = T & {
|
|
4
|
+
keyFor: (...args: Parameters<T>) => string;
|
|
5
|
+
key: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): CachedFunction<T, U>;
|
package/dist/data/cache.js
CHANGED
|
@@ -5,7 +5,19 @@ import { useNavigate, getIntent } from "../routing";
|
|
|
5
5
|
import { redirectStatusCodes } from "../utils";
|
|
6
6
|
const LocationHeader = "Location";
|
|
7
7
|
const PRELOAD_TIMEOUT = 5000;
|
|
8
|
+
const CACHE_TIMEOUT = 180000;
|
|
8
9
|
let cacheMap = new Map();
|
|
10
|
+
// cleanup forward/back cache
|
|
11
|
+
if (!isServer) {
|
|
12
|
+
setInterval(() => {
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
for (let [k, v] of cacheMap.entries()) {
|
|
15
|
+
if (!v[3].size && now - v[0] > CACHE_TIMEOUT) {
|
|
16
|
+
cacheMap.delete(k);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}, 300000);
|
|
20
|
+
}
|
|
9
21
|
function getCache() {
|
|
10
22
|
if (!isServer)
|
|
11
23
|
return cacheMap;
|
|
@@ -13,13 +25,14 @@ function getCache() {
|
|
|
13
25
|
return req.routerCache || (req.routerCache = new Map());
|
|
14
26
|
}
|
|
15
27
|
export function revalidate(key) {
|
|
28
|
+
key && !Array.isArray(key) && (key = [key]);
|
|
16
29
|
return startTransition(() => {
|
|
17
30
|
const now = Date.now();
|
|
18
31
|
for (let k of cacheMap.keys()) {
|
|
19
|
-
if (key === undefined || k
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
if (key === undefined || matchKey(k, key)) {
|
|
33
|
+
const entry = cacheMap.get(k);
|
|
34
|
+
entry[0] = 0; //force cache miss
|
|
35
|
+
revalidateSignals(entry[3], now); // retrigger live signals
|
|
23
36
|
}
|
|
24
37
|
}
|
|
25
38
|
});
|
|
@@ -30,13 +43,13 @@ function revalidateSignals(set, time) {
|
|
|
30
43
|
}
|
|
31
44
|
export function cache(fn, name, options) {
|
|
32
45
|
const [store, setStore] = createStore({});
|
|
33
|
-
|
|
46
|
+
const cachedFn = ((...args) => {
|
|
34
47
|
const cache = getCache();
|
|
35
48
|
const intent = getIntent();
|
|
36
49
|
const owner = getOwner();
|
|
37
50
|
const navigate = owner ? useNavigate() : undefined;
|
|
38
51
|
const now = Date.now();
|
|
39
|
-
const key = name + (args
|
|
52
|
+
const key = name + hashKey(args);
|
|
40
53
|
let cached = cache.get(key);
|
|
41
54
|
let version;
|
|
42
55
|
if (owner) {
|
|
@@ -53,9 +66,10 @@ export function cache(fn, name, options) {
|
|
|
53
66
|
}
|
|
54
67
|
let res = cached[1];
|
|
55
68
|
if (!isServer && intent === "navigate") {
|
|
56
|
-
res =
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
res =
|
|
70
|
+
"then" in cached[1]
|
|
71
|
+
? cached[1].then(handleResponse(false), handleResponse(true))
|
|
72
|
+
: handleResponse(false)(cached[1]);
|
|
59
73
|
startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
|
|
60
74
|
}
|
|
61
75
|
return res;
|
|
@@ -79,9 +93,10 @@ export function cache(fn, name, options) {
|
|
|
79
93
|
else
|
|
80
94
|
cache.set(key, (cached = [now, res, intent, new Set(version ? [version] : [])]));
|
|
81
95
|
if (intent !== "preload") {
|
|
82
|
-
res =
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
res =
|
|
97
|
+
"then" in res
|
|
98
|
+
? res.then(handleResponse(false), handleResponse(true))
|
|
99
|
+
: handleResponse(false)(res);
|
|
85
100
|
}
|
|
86
101
|
return res;
|
|
87
102
|
function handleResponse(error) {
|
|
@@ -111,4 +126,32 @@ export function cache(fn, name, options) {
|
|
|
111
126
|
};
|
|
112
127
|
}
|
|
113
128
|
});
|
|
129
|
+
cachedFn.keyFor = (...args) => name + hashKey(args);
|
|
130
|
+
cachedFn.key = name;
|
|
131
|
+
return cachedFn;
|
|
132
|
+
}
|
|
133
|
+
function matchKey(key, keys) {
|
|
134
|
+
for (let k of keys) {
|
|
135
|
+
if (key.startsWith(k))
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
// Modified from the amazing Tanstack Query library (MIT)
|
|
141
|
+
// https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168
|
|
142
|
+
function hashKey(args) {
|
|
143
|
+
return JSON.stringify(args, (_, val) => isPlainObject(val)
|
|
144
|
+
? Object.keys(val)
|
|
145
|
+
.sort()
|
|
146
|
+
.reduce((result, key) => {
|
|
147
|
+
result[key] = val[key];
|
|
148
|
+
return result;
|
|
149
|
+
}, {})
|
|
150
|
+
: val);
|
|
151
|
+
}
|
|
152
|
+
function isPlainObject(obj) {
|
|
153
|
+
let proto;
|
|
154
|
+
return (obj != null &&
|
|
155
|
+
typeof obj === "object" &&
|
|
156
|
+
(!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype));
|
|
114
157
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { delegateEvents } from "solid-js/web";
|
|
2
|
+
import { onCleanup } from "solid-js";
|
|
3
|
+
import { actions } from "./action";
|
|
4
|
+
export function setupNativeEvents(router) {
|
|
5
|
+
const basePath = router.base.path();
|
|
6
|
+
const navigateFromRoute = router.navigatorFactory(router.base);
|
|
7
|
+
let preloadTimeout = {};
|
|
8
|
+
function isSvg(el) {
|
|
9
|
+
return el.namespaceURI === "http://www.w3.org/2000/svg";
|
|
10
|
+
}
|
|
11
|
+
function handleAnchor(evt) {
|
|
12
|
+
if (evt.defaultPrevented ||
|
|
13
|
+
evt.button !== 0 ||
|
|
14
|
+
evt.metaKey ||
|
|
15
|
+
evt.altKey ||
|
|
16
|
+
evt.ctrlKey ||
|
|
17
|
+
evt.shiftKey)
|
|
18
|
+
return;
|
|
19
|
+
const a = evt
|
|
20
|
+
.composedPath()
|
|
21
|
+
.find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
|
|
22
|
+
if (!a)
|
|
23
|
+
return;
|
|
24
|
+
const svg = isSvg(a);
|
|
25
|
+
const href = svg ? a.href.baseVal : a.href;
|
|
26
|
+
const target = svg ? a.target.baseVal : a.target;
|
|
27
|
+
if (target || (!href && !a.hasAttribute("state")))
|
|
28
|
+
return;
|
|
29
|
+
const rel = (a.getAttribute("rel") || "").split(/\s+/);
|
|
30
|
+
if (a.hasAttribute("download") || (rel && rel.includes("external")))
|
|
31
|
+
return;
|
|
32
|
+
const url = svg ? new URL(href, document.baseURI) : new URL(href);
|
|
33
|
+
if (url.origin !== window.location.origin ||
|
|
34
|
+
(basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())))
|
|
35
|
+
return;
|
|
36
|
+
return [a, url];
|
|
37
|
+
}
|
|
38
|
+
function handleAnchorClick(evt) {
|
|
39
|
+
const res = handleAnchor(evt);
|
|
40
|
+
if (!res)
|
|
41
|
+
return;
|
|
42
|
+
const [a, url] = res;
|
|
43
|
+
const to = router.parsePath(url.pathname + url.search + url.hash);
|
|
44
|
+
const state = a.getAttribute("state");
|
|
45
|
+
evt.preventDefault();
|
|
46
|
+
navigateFromRoute(to, {
|
|
47
|
+
resolve: false,
|
|
48
|
+
replace: a.hasAttribute("replace"),
|
|
49
|
+
scroll: !a.hasAttribute("noscroll"),
|
|
50
|
+
state: state && JSON.parse(state)
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function handleAnchorPreload(evt) {
|
|
54
|
+
const res = handleAnchor(evt);
|
|
55
|
+
if (!res)
|
|
56
|
+
return;
|
|
57
|
+
const [a, url] = res;
|
|
58
|
+
if (!preloadTimeout[url.pathname])
|
|
59
|
+
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
60
|
+
}
|
|
61
|
+
function handleAnchorIn(evt) {
|
|
62
|
+
const res = handleAnchor(evt);
|
|
63
|
+
if (!res)
|
|
64
|
+
return;
|
|
65
|
+
const [a, url] = res;
|
|
66
|
+
if (preloadTimeout[url.pathname])
|
|
67
|
+
return;
|
|
68
|
+
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
69
|
+
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
70
|
+
delete preloadTimeout[url.pathname];
|
|
71
|
+
}, 200);
|
|
72
|
+
}
|
|
73
|
+
function handleAnchorOut(evt) {
|
|
74
|
+
const res = handleAnchor(evt);
|
|
75
|
+
if (!res)
|
|
76
|
+
return;
|
|
77
|
+
const [, url] = res;
|
|
78
|
+
if (preloadTimeout[url.pathname]) {
|
|
79
|
+
clearTimeout(preloadTimeout[url.pathname]);
|
|
80
|
+
delete preloadTimeout[url.pathname];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function handleFormSubmit(evt) {
|
|
84
|
+
let actionRef = (evt.submitter && evt.submitter.getAttribute("formaction")) || evt.target.action;
|
|
85
|
+
if (!actionRef)
|
|
86
|
+
return;
|
|
87
|
+
if (!actionRef.startsWith("action:")) {
|
|
88
|
+
const url = new URL(actionRef);
|
|
89
|
+
actionRef = router.parsePath(url.pathname + url.search);
|
|
90
|
+
if (!actionRef.startsWith(router.actionBase))
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const handler = actions.get(actionRef);
|
|
94
|
+
if (handler) {
|
|
95
|
+
evt.preventDefault();
|
|
96
|
+
const data = new FormData(evt.target);
|
|
97
|
+
handler.call(router, data);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ensure delegated event run first
|
|
101
|
+
delegateEvents(["click", "submit"]);
|
|
102
|
+
document.addEventListener("click", handleAnchorClick);
|
|
103
|
+
document.addEventListener("mouseover", handleAnchorIn);
|
|
104
|
+
document.addEventListener("mouseout", handleAnchorOut);
|
|
105
|
+
document.addEventListener("focusin", handleAnchorPreload);
|
|
106
|
+
document.addEventListener("touchstart", handleAnchorPreload);
|
|
107
|
+
document.addEventListener("submit", handleFormSubmit);
|
|
108
|
+
onCleanup(() => {
|
|
109
|
+
document.removeEventListener("click", handleAnchorClick);
|
|
110
|
+
document.removeEventListener("mouseover", handleAnchorIn);
|
|
111
|
+
document.removeEventListener("mouseout", handleAnchorOut);
|
|
112
|
+
document.removeEventListener("focusin", handleAnchorPreload);
|
|
113
|
+
document.removeEventListener("touchstart", handleAnchorPreload);
|
|
114
|
+
document.removeEventListener("submit", handleFormSubmit);
|
|
115
|
+
});
|
|
116
|
+
}
|
package/dist/data/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { createAsync } from "./createAsync";
|
|
2
|
-
export { action, useSubmission, useSubmissions, useAction } from "./action";
|
|
3
|
-
export { cache, revalidate } from "./cache";
|
|
2
|
+
export { action, useSubmission, useSubmissions, useAction, type Action } from "./action";
|
|
3
|
+
export { cache, revalidate, type CachedFunction } from "./cache";
|
|
4
4
|
export { redirect } from "./response";
|
package/dist/data/response.d.ts
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type RouterResponseInit = ResponseInit & {
|
|
2
|
+
revalidate?: string | string[];
|
|
3
|
+
};
|
|
4
|
+
export declare function redirect(url: string, init?: number | RouterResponseInit): Response;
|
|
5
|
+
export declare function reload(init: RouterResponseInit): Response;
|
package/dist/data/response.js
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
export function redirect(url, init = 302) {
|
|
2
|
-
let responseInit
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
let responseInit;
|
|
3
|
+
let revalidate;
|
|
4
|
+
if (typeof init === "number") {
|
|
5
|
+
responseInit = { status: init };
|
|
5
6
|
}
|
|
6
|
-
else
|
|
7
|
-
responseInit
|
|
7
|
+
else {
|
|
8
|
+
({ revalidate, ...responseInit } = init);
|
|
9
|
+
if (typeof responseInit.status === "undefined") {
|
|
10
|
+
responseInit.status = 302;
|
|
11
|
+
}
|
|
8
12
|
}
|
|
9
13
|
const headers = new Headers(responseInit.headers);
|
|
10
14
|
headers.set("Location", url);
|
|
15
|
+
revalidate && headers.set("X-Revalidate", revalidate.toString());
|
|
11
16
|
const response = new Response(null, {
|
|
12
17
|
...responseInit,
|
|
13
18
|
headers: headers
|
|
14
19
|
});
|
|
15
20
|
return response;
|
|
16
21
|
}
|
|
22
|
+
export function reload(init) {
|
|
23
|
+
const { revalidate, ...responseInit } = init;
|
|
24
|
+
return new Response(null, {
|
|
25
|
+
...responseInit,
|
|
26
|
+
...(revalidate ? { headers: new Headers(responseInit.headers).set("X-Revalidate", revalidate.toString()) } : {})
|
|
27
|
+
});
|
|
28
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
export * from "./routers";
|
|
1
2
|
export * from "./components";
|
|
2
|
-
export * from "./integration";
|
|
3
3
|
export * from "./lifecycle";
|
|
4
4
|
export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing";
|
|
5
5
|
export { mergeSearchString as _mergeSearchString } from "./utils";
|
|
6
6
|
export * from "./data";
|
|
7
|
-
export type { Location, LocationChange,
|
|
7
|
+
export type { Location, LocationChange, NavigateOptions, Navigator, OutputMatch, Params, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouterIntegration, RouterOutput, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types";
|