@solidjs/router 0.15.3 → 0.16.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 +178 -141
- package/dist/data/action.d.ts +1 -1
- package/dist/data/createAsync.js +8 -6
- package/dist/data/query.js +11 -5
- package/dist/data/response.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +57 -22
- package/dist/routers/HashRouter.d.ts +1 -1
- package/dist/routers/Router.js +1 -1
- package/dist/routers/components.d.ts +1 -1
- package/dist/routers/components.jsx +5 -2
- package/dist/routers/createRouter.d.ts +1 -1
- package/dist/src/components.d.ts +31 -0
- package/dist/src/components.jsx +39 -0
- package/dist/src/data/action.d.ts +17 -0
- package/dist/src/data/action.js +163 -0
- package/dist/src/data/action.spec.d.ts +1 -0
- package/dist/src/data/action.spec.js +297 -0
- package/dist/src/data/createAsync.d.ts +32 -0
- package/dist/src/data/createAsync.js +96 -0
- package/dist/src/data/createAsync.spec.d.ts +1 -0
- package/dist/src/data/createAsync.spec.js +196 -0
- package/dist/src/data/events.d.ts +9 -0
- package/dist/src/data/events.js +123 -0
- package/dist/src/data/events.spec.d.ts +1 -0
- package/dist/src/data/events.spec.js +567 -0
- package/dist/src/data/index.d.ts +4 -0
- package/dist/src/data/index.js +4 -0
- package/dist/src/data/query.d.ts +23 -0
- package/dist/src/data/query.js +232 -0
- package/dist/src/data/query.spec.d.ts +1 -0
- package/dist/src/data/query.spec.js +354 -0
- package/dist/src/data/response.d.ts +4 -0
- package/dist/src/data/response.js +42 -0
- package/dist/src/data/response.spec.d.ts +1 -0
- package/dist/src/data/response.spec.js +165 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.jsx +6 -0
- package/dist/src/lifecycle.d.ts +5 -0
- package/dist/src/lifecycle.js +69 -0
- package/dist/src/routers/HashRouter.d.ts +9 -0
- package/dist/src/routers/HashRouter.js +41 -0
- package/dist/src/routers/MemoryRouter.d.ts +24 -0
- package/dist/src/routers/MemoryRouter.js +57 -0
- package/dist/src/routers/Router.d.ts +9 -0
- package/dist/src/routers/Router.js +45 -0
- package/dist/src/routers/StaticRouter.d.ts +6 -0
- package/dist/src/routers/StaticRouter.js +15 -0
- package/dist/src/routers/components.d.ts +27 -0
- package/dist/src/routers/components.jsx +118 -0
- package/dist/src/routers/createRouter.d.ts +10 -0
- package/dist/src/routers/createRouter.js +41 -0
- package/dist/src/routers/index.d.ts +11 -0
- package/dist/src/routers/index.js +6 -0
- package/dist/src/routing.d.ts +175 -0
- package/dist/src/routing.js +560 -0
- package/dist/src/types.d.ts +200 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +13 -0
- package/dist/src/utils.js +185 -0
- package/dist/test/helpers.d.ts +6 -0
- package/dist/test/helpers.js +50 -0
- package/dist/types.d.ts +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -0
- package/package.json +3 -2
package/dist/data/createAsync.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
|
|
3
3
|
*/
|
|
4
|
-
import { createResource, sharedConfig, untrack } from "solid-js";
|
|
4
|
+
import { createResource, sharedConfig, untrack, catchError } from "solid-js";
|
|
5
5
|
import { createStore, reconcile, unwrap } from "solid-js/store";
|
|
6
6
|
import { isServer } from "solid-js/web";
|
|
7
7
|
export function createAsync(fn, options) {
|
|
8
8
|
let resource;
|
|
9
9
|
let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
|
|
10
|
-
[resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, options);
|
|
10
|
+
[resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, options);
|
|
11
11
|
const resultAccessor = (() => resource());
|
|
12
|
-
Object.defineProperty(resultAccessor,
|
|
12
|
+
Object.defineProperty(resultAccessor, "latest", {
|
|
13
13
|
get() {
|
|
14
14
|
return resource.latest;
|
|
15
15
|
}
|
|
@@ -18,13 +18,15 @@ export function createAsync(fn, options) {
|
|
|
18
18
|
}
|
|
19
19
|
export function createAsyncStore(fn, options = {}) {
|
|
20
20
|
let resource;
|
|
21
|
-
let prev = () => !resource || resource.state === "unresolved"
|
|
22
|
-
|
|
21
|
+
let prev = () => !resource || resource.state === "unresolved"
|
|
22
|
+
? undefined
|
|
23
|
+
: unwrap(resource.latest);
|
|
24
|
+
[resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, {
|
|
23
25
|
...options,
|
|
24
26
|
storage: (init) => createDeepSignal(init, options.reconcile)
|
|
25
27
|
});
|
|
26
28
|
const resultAccessor = (() => resource());
|
|
27
|
-
Object.defineProperty(resultAccessor,
|
|
29
|
+
Object.defineProperty(resultAccessor, "latest", {
|
|
28
30
|
get() {
|
|
29
31
|
return resource.latest;
|
|
30
32
|
}
|
package/dist/data/query.js
CHANGED
|
@@ -146,6 +146,15 @@ export function query(fn, name) {
|
|
|
146
146
|
function handleResponse(error) {
|
|
147
147
|
return async (v) => {
|
|
148
148
|
if (v instanceof Response) {
|
|
149
|
+
const e = getRequestEvent();
|
|
150
|
+
if (e) {
|
|
151
|
+
for (const [key, value] of v.headers) {
|
|
152
|
+
if (key == "set-cookie")
|
|
153
|
+
e.response.headers.append("set-cookie", value);
|
|
154
|
+
else
|
|
155
|
+
e.response.headers.set(key, value);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
149
158
|
const url = v.headers.get(LocationHeader);
|
|
150
159
|
if (url !== null) {
|
|
151
160
|
// client + server relative redirect
|
|
@@ -155,11 +164,8 @@ export function query(fn, name) {
|
|
|
155
164
|
});
|
|
156
165
|
else if (!isServer)
|
|
157
166
|
window.location.href = url;
|
|
158
|
-
else if (
|
|
159
|
-
|
|
160
|
-
if (e)
|
|
161
|
-
e.response = { status: 302, headers: new Headers({ Location: url }) };
|
|
162
|
-
}
|
|
167
|
+
else if (e)
|
|
168
|
+
e.response.status = 302;
|
|
163
169
|
return;
|
|
164
170
|
}
|
|
165
171
|
if (v.customBody)
|
package/dist/data/response.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RouterResponseInit, CustomResponse } from "../types";
|
|
1
|
+
import type { RouterResponseInit, CustomResponse } from "../types.js";
|
|
2
2
|
export declare function redirect(url: string, init?: number | RouterResponseInit): CustomResponse<never>;
|
|
3
3
|
export declare function reload(init?: RouterResponseInit): CustomResponse<never>;
|
|
4
4
|
export declare function json<T>(data: T, init?: RouterResponseInit): CustomResponse<T>;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export * from "./lifecycle.js";
|
|
|
4
4
|
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, usePreloadRoute } from "./routing.js";
|
|
5
5
|
export { mergeSearchString as _mergeSearchString } from "./utils.js";
|
|
6
6
|
export * from "./data/index.js";
|
|
7
|
-
export type { Location, LocationChange, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RoutePreloadFunc, RoutePreloadFuncArgs, RouteDefinition, RouteDescription, RouteMatch, RouterIntegration, RouterUtils, SetParams, Submission, BeforeLeaveEventArgs, RouteLoadFunc, RouteLoadFuncArgs, RouterResponseInit, CustomResponse } from "./types.js";
|
|
7
|
+
export type { Location, LocationChange, SearchParams, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RoutePreloadFunc, RoutePreloadFuncArgs, RouteDefinition, RouteDescription, RouteMatch, RouterIntegration, RouterUtils, SetParams, Submission, BeforeLeaveEventArgs, RouteLoadFunc, RouteLoadFuncArgs, RouterResponseInit, CustomResponse } from "./types.js";
|
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, batch, createComponent, children, mergeProps, Show, createRoot, sharedConfig, getListener, $TRACK, splitProps, createResource } from 'solid-js';
|
|
2
|
+
import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, batch, createComponent, children, mergeProps, Show, createRoot, sharedConfig, getListener, $TRACK, splitProps, createResource, catchError } from 'solid-js';
|
|
3
3
|
import { createStore, reconcile, unwrap } from 'solid-js/store';
|
|
4
4
|
|
|
5
5
|
function createBeforeLeave() {
|
|
@@ -193,6 +193,9 @@ function createMemoObject(fn) {
|
|
|
193
193
|
},
|
|
194
194
|
ownKeys() {
|
|
195
195
|
return Reflect.ownKeys(fn());
|
|
196
|
+
},
|
|
197
|
+
has(_, property) {
|
|
198
|
+
return property in fn();
|
|
196
199
|
}
|
|
197
200
|
});
|
|
198
201
|
}
|
|
@@ -234,8 +237,18 @@ function expandOptionals(pattern) {
|
|
|
234
237
|
}
|
|
235
238
|
return expandOptionals(suffix).reduce((results, expansion) => [...results, ...prefixes.map(p => p + expansion)], []);
|
|
236
239
|
}
|
|
240
|
+
function setFunctionName(obj, value) {
|
|
241
|
+
Object.defineProperty(obj, "name", {
|
|
242
|
+
value,
|
|
243
|
+
writable: false,
|
|
244
|
+
configurable: false
|
|
245
|
+
});
|
|
246
|
+
return obj;
|
|
247
|
+
}
|
|
237
248
|
|
|
238
249
|
const MAX_REDIRECTS = 100;
|
|
250
|
+
|
|
251
|
+
/** Consider this API opaque and internal. It is likely to change in the future. */
|
|
239
252
|
const RouterContextObj = createContext();
|
|
240
253
|
const RouteContextObj = createContext();
|
|
241
254
|
const useRouter = () => invariant(useContext(RouterContextObj), "<A> and 'use' router primitives can be only used inside a Route.");
|
|
@@ -927,7 +940,10 @@ function Routes(props) {
|
|
|
927
940
|
}
|
|
928
941
|
createRoot(dispose => {
|
|
929
942
|
disposers[i] = dispose;
|
|
930
|
-
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () =>
|
|
943
|
+
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
|
|
944
|
+
const routeMatches = props.routerState.matches();
|
|
945
|
+
return routeMatches[i] ?? routeMatches[0];
|
|
946
|
+
});
|
|
931
947
|
});
|
|
932
948
|
}
|
|
933
949
|
}
|
|
@@ -1159,6 +1175,12 @@ function query(fn, name) {
|
|
|
1159
1175
|
function handleResponse(error) {
|
|
1160
1176
|
return async v => {
|
|
1161
1177
|
if (v instanceof Response) {
|
|
1178
|
+
const e = getRequestEvent();
|
|
1179
|
+
if (e) {
|
|
1180
|
+
for (const [key, value] of v.headers) {
|
|
1181
|
+
if (key == "set-cookie") e.response.headers.append("set-cookie", value);else e.response.headers.set(key, value);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1162
1184
|
const url = v.headers.get(LocationHeader);
|
|
1163
1185
|
if (url !== null) {
|
|
1164
1186
|
// client + server relative redirect
|
|
@@ -1166,15 +1188,7 @@ function query(fn, name) {
|
|
|
1166
1188
|
navigate(url, {
|
|
1167
1189
|
replace: true
|
|
1168
1190
|
});
|
|
1169
|
-
});else if (!isServer) window.location.href = url;else if (
|
|
1170
|
-
const e = getRequestEvent();
|
|
1171
|
-
if (e) e.response = {
|
|
1172
|
-
status: 302,
|
|
1173
|
-
headers: new Headers({
|
|
1174
|
-
Location: url
|
|
1175
|
-
})
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1191
|
+
});else if (!isServer) window.location.href = url;else if (e) e.response.status = 302;
|
|
1178
1192
|
return;
|
|
1179
1193
|
}
|
|
1180
1194
|
if (v.customBody) v = await v.customBody();
|
|
@@ -1319,8 +1333,10 @@ function action(fn, options = {}) {
|
|
|
1319
1333
|
const o = typeof options === "string" ? {
|
|
1320
1334
|
name: options
|
|
1321
1335
|
} : options;
|
|
1322
|
-
const
|
|
1336
|
+
const name = o.name || (!isServer ? String(hashString(fn.toString())) : undefined);
|
|
1337
|
+
const url = fn.url || name && `https://action/${name}` || "";
|
|
1323
1338
|
mutate.base = url;
|
|
1339
|
+
if (name) setFunctionName(mutate, name);
|
|
1324
1340
|
return toAction(mutate, url);
|
|
1325
1341
|
}
|
|
1326
1342
|
function toAction(fn, url) {
|
|
@@ -1382,7 +1398,12 @@ async function handleResponse(response, error, navigate) {
|
|
|
1382
1398
|
} : undefined;
|
|
1383
1399
|
}
|
|
1384
1400
|
|
|
1385
|
-
function setupNativeEvents(
|
|
1401
|
+
function setupNativeEvents({
|
|
1402
|
+
preload = true,
|
|
1403
|
+
explicitLinks = false,
|
|
1404
|
+
actionBase = "/_server",
|
|
1405
|
+
transformUrl
|
|
1406
|
+
} = {}) {
|
|
1386
1407
|
return router => {
|
|
1387
1408
|
const basePath = router.base.path();
|
|
1388
1409
|
const navigateFromRoute = router.navigatorFactory(router.base);
|
|
@@ -1515,7 +1536,7 @@ function Router(props) {
|
|
|
1515
1536
|
saveCurrentDepth();
|
|
1516
1537
|
},
|
|
1517
1538
|
init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
|
|
1518
|
-
if (delta
|
|
1539
|
+
if (delta) {
|
|
1519
1540
|
return !beforeLeave.confirm(delta);
|
|
1520
1541
|
} else {
|
|
1521
1542
|
const s = getSource();
|
|
@@ -1524,7 +1545,12 @@ function Router(props) {
|
|
|
1524
1545
|
});
|
|
1525
1546
|
}
|
|
1526
1547
|
})),
|
|
1527
|
-
create: setupNativeEvents(
|
|
1548
|
+
create: setupNativeEvents({
|
|
1549
|
+
preload: props.preload,
|
|
1550
|
+
explicitLinks: props.explicitLinks,
|
|
1551
|
+
actionBase: props.actionBase,
|
|
1552
|
+
transformUrl: props.transformUrl
|
|
1553
|
+
}),
|
|
1528
1554
|
utils: {
|
|
1529
1555
|
go: delta => window.history.go(delta),
|
|
1530
1556
|
beforeLeave
|
|
@@ -1565,7 +1591,11 @@ function HashRouter(props) {
|
|
|
1565
1591
|
saveCurrentDepth();
|
|
1566
1592
|
},
|
|
1567
1593
|
init: notify => bindEvent(window, "hashchange", notifyIfNotBlocked(notify, delta => !beforeLeave.confirm(delta && delta < 0 ? delta : getSource()))),
|
|
1568
|
-
create: setupNativeEvents(
|
|
1594
|
+
create: setupNativeEvents({
|
|
1595
|
+
preload: props.preload,
|
|
1596
|
+
explicitLinks: props.explicitLinks,
|
|
1597
|
+
actionBase: props.actionBase
|
|
1598
|
+
}),
|
|
1569
1599
|
utils: {
|
|
1570
1600
|
go: delta => window.history.go(delta),
|
|
1571
1601
|
renderPath: path => `#${path}`,
|
|
@@ -1627,7 +1657,11 @@ function MemoryRouter(props) {
|
|
|
1627
1657
|
get: memoryHistory.get,
|
|
1628
1658
|
set: memoryHistory.set,
|
|
1629
1659
|
init: memoryHistory.listen,
|
|
1630
|
-
create: setupNativeEvents(
|
|
1660
|
+
create: setupNativeEvents({
|
|
1661
|
+
preload: props.preload,
|
|
1662
|
+
explicitLinks: props.explicitLinks,
|
|
1663
|
+
actionBase: props.actionBase
|
|
1664
|
+
}),
|
|
1631
1665
|
utils: {
|
|
1632
1666
|
go: memoryHistory.go
|
|
1633
1667
|
}
|
|
@@ -1709,9 +1743,10 @@ function Navigate(props) {
|
|
|
1709
1743
|
function createAsync(fn, options) {
|
|
1710
1744
|
let resource;
|
|
1711
1745
|
let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
|
|
1712
|
-
[resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, options);
|
|
1746
|
+
[resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, options);
|
|
1713
1747
|
const resultAccessor = () => resource();
|
|
1714
|
-
|
|
1748
|
+
if (options?.name) setFunctionName(resultAccessor, options.name);
|
|
1749
|
+
Object.defineProperty(resultAccessor, "latest", {
|
|
1715
1750
|
get() {
|
|
1716
1751
|
return resource.latest;
|
|
1717
1752
|
}
|
|
@@ -1721,12 +1756,12 @@ function createAsync(fn, options) {
|
|
|
1721
1756
|
function createAsyncStore(fn, options = {}) {
|
|
1722
1757
|
let resource;
|
|
1723
1758
|
let prev = () => !resource || resource.state === "unresolved" ? undefined : unwrap(resource.latest);
|
|
1724
|
-
[resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, {
|
|
1759
|
+
[resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, {
|
|
1725
1760
|
...options,
|
|
1726
1761
|
storage: init => createDeepSignal(init, options.reconcile)
|
|
1727
1762
|
});
|
|
1728
1763
|
const resultAccessor = () => resource();
|
|
1729
|
-
Object.defineProperty(resultAccessor,
|
|
1764
|
+
Object.defineProperty(resultAccessor, "latest", {
|
|
1730
1765
|
get() {
|
|
1731
1766
|
return resource.latest;
|
|
1732
1767
|
}
|
|
@@ -1841,4 +1876,4 @@ function json(data, init = {}) {
|
|
|
1841
1876
|
return response;
|
|
1842
1877
|
}
|
|
1843
1878
|
|
|
1844
|
-
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, query, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, usePreloadRoute, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|
|
1879
|
+
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, RouterContextObj as RouterContext, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, query, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, usePreloadRoute, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSX } from "solid-js";
|
|
2
|
-
import type { BaseRouterProps } from "./components.
|
|
2
|
+
import type { BaseRouterProps } from "./components.jsx";
|
|
3
3
|
export declare function hashParser(str: string): string;
|
|
4
4
|
export type HashRouterProps = BaseRouterProps & {
|
|
5
5
|
actionBase?: string;
|
package/dist/routers/Router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Component, JSX } from "solid-js";
|
|
2
|
-
import type { MatchFilters, RouteDefinition, RouterIntegration, RouteSectionProps
|
|
2
|
+
import type { MatchFilters, RouteDefinition, RoutePreloadFunc, RouterIntegration, RouteSectionProps } from "../types.js";
|
|
3
3
|
export type BaseRouterProps = {
|
|
4
4
|
base?: string;
|
|
5
5
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*@refresh skip*/
|
|
2
|
-
import { getRequestEvent, isServer } from "solid-js/web";
|
|
3
2
|
import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
|
|
3
|
+
import { getRequestEvent, isServer } from "solid-js/web";
|
|
4
4
|
import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInPreloadFn } from "../routing.js";
|
|
5
5
|
export const createRouterComponent = (router) => (props) => {
|
|
6
6
|
const { base } = props;
|
|
@@ -70,7 +70,10 @@ function Routes(props) {
|
|
|
70
70
|
}
|
|
71
71
|
createRoot(dispose => {
|
|
72
72
|
disposers[i] = dispose;
|
|
73
|
-
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () =>
|
|
73
|
+
next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
|
|
74
|
+
const routeMatches = props.routerState.matches();
|
|
75
|
+
return routeMatches[i] ?? routeMatches[0];
|
|
76
|
+
});
|
|
74
77
|
});
|
|
75
78
|
}
|
|
76
79
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LocationChange, RouterContext, RouterUtils } from "../types.
|
|
1
|
+
import type { LocationChange, RouterContext, RouterUtils } from "../types.js";
|
|
2
2
|
export declare function createRouter(config: {
|
|
3
3
|
get: () => string | LocationChange;
|
|
4
4
|
set: (next: LocationChange) => void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { JSX } from "solid-js";
|
|
2
|
+
import type { Location, Navigator } from "./types.js";
|
|
3
|
+
declare module "solid-js" {
|
|
4
|
+
namespace JSX {
|
|
5
|
+
interface AnchorHTMLAttributes<T> {
|
|
6
|
+
state?: string;
|
|
7
|
+
noScroll?: boolean;
|
|
8
|
+
replace?: boolean;
|
|
9
|
+
preload?: boolean;
|
|
10
|
+
link?: boolean;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, "state"> {
|
|
15
|
+
href: string;
|
|
16
|
+
replace?: boolean | undefined;
|
|
17
|
+
noScroll?: boolean | undefined;
|
|
18
|
+
state?: unknown | undefined;
|
|
19
|
+
inactiveClass?: string | undefined;
|
|
20
|
+
activeClass?: string | undefined;
|
|
21
|
+
end?: boolean | undefined;
|
|
22
|
+
}
|
|
23
|
+
export declare function A(props: AnchorProps): JSX.Element;
|
|
24
|
+
export interface NavigateProps {
|
|
25
|
+
href: ((args: {
|
|
26
|
+
navigate: Navigator;
|
|
27
|
+
location: Location;
|
|
28
|
+
}) => string) | string;
|
|
29
|
+
state?: unknown;
|
|
30
|
+
}
|
|
31
|
+
export declare function Navigate(props: NavigateProps): null;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createMemo, mergeProps, splitProps } from "solid-js";
|
|
2
|
+
import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing.js";
|
|
3
|
+
import { normalizePath } from "./utils.js";
|
|
4
|
+
export function A(props) {
|
|
5
|
+
props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
|
|
6
|
+
const [, rest] = splitProps(props, [
|
|
7
|
+
"href",
|
|
8
|
+
"state",
|
|
9
|
+
"class",
|
|
10
|
+
"activeClass",
|
|
11
|
+
"inactiveClass",
|
|
12
|
+
"end"
|
|
13
|
+
]);
|
|
14
|
+
const to = useResolvedPath(() => props.href);
|
|
15
|
+
const href = useHref(to);
|
|
16
|
+
const location = useLocation();
|
|
17
|
+
const isActive = createMemo(() => {
|
|
18
|
+
const to_ = to();
|
|
19
|
+
if (to_ === undefined)
|
|
20
|
+
return [false, false];
|
|
21
|
+
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
|
|
22
|
+
const loc = decodeURI(normalizePath(location.pathname).toLowerCase());
|
|
23
|
+
return [props.end ? path === loc : loc.startsWith(path + "/") || loc === path, path === loc];
|
|
24
|
+
});
|
|
25
|
+
return (<a {...rest} href={href() || props.href} state={JSON.stringify(props.state)} classList={{
|
|
26
|
+
...(props.class && { [props.class]: true }),
|
|
27
|
+
[props.inactiveClass]: !isActive()[0],
|
|
28
|
+
[props.activeClass]: isActive()[0],
|
|
29
|
+
...rest.classList
|
|
30
|
+
}} link aria-current={isActive()[1] ? "page" : undefined}/>);
|
|
31
|
+
}
|
|
32
|
+
export function Navigate(props) {
|
|
33
|
+
const navigate = useNavigate();
|
|
34
|
+
const location = useLocation();
|
|
35
|
+
const { href, state } = props;
|
|
36
|
+
const path = typeof href === "function" ? href({ navigate, location }) : href;
|
|
37
|
+
navigate(path, { replace: true, state });
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { JSX } from "solid-js";
|
|
2
|
+
import type { Submission, SubmissionStub, NarrowResponse } from "../types.js";
|
|
3
|
+
export type Action<T extends Array<any>, U, V = T> = (T extends [FormData | URLSearchParams] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<NarrowResponse<U>>) & {
|
|
4
|
+
url: string;
|
|
5
|
+
with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<NarrowResponse<U>>, ...args: A): Action<B, U, V>;
|
|
6
|
+
};
|
|
7
|
+
export declare const actions: Map<string, Action<any, any, any>>;
|
|
8
|
+
export declare function useSubmissions<T extends Array<any>, U, V>(fn: Action<T, U, V>, filter?: (input: V) => boolean): Submission<T, NarrowResponse<U>>[] & {
|
|
9
|
+
pending: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function useSubmission<T extends Array<any>, U, V>(fn: Action<T, U, V>, filter?: (input: V) => boolean): Submission<T, NarrowResponse<U>> | SubmissionStub;
|
|
12
|
+
export declare function useAction<T extends Array<any>, U, V>(action: Action<T, U, V>): (...args: Parameters<Action<T, U, V>>) => Promise<NarrowResponse<U>>;
|
|
13
|
+
export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>;
|
|
14
|
+
export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, options?: {
|
|
15
|
+
name?: string;
|
|
16
|
+
onComplete?: (s: Submission<T, U>) => void;
|
|
17
|
+
}): Action<T, U>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { $TRACK, createMemo, createSignal, onCleanup, getOwner } from "solid-js";
|
|
2
|
+
import { isServer } from "solid-js/web";
|
|
3
|
+
import { useRouter } from "../routing.js";
|
|
4
|
+
import { mockBase, setFunctionName } from "../utils.js";
|
|
5
|
+
import { cacheKeyOp, hashKey, revalidate, query } from "./query.js";
|
|
6
|
+
export const actions = /* #__PURE__ */ new Map();
|
|
7
|
+
export function useSubmissions(fn, filter) {
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.base && (!filter || filter(s.input))));
|
|
10
|
+
return new Proxy([], {
|
|
11
|
+
get(_, property) {
|
|
12
|
+
if (property === $TRACK)
|
|
13
|
+
return subs();
|
|
14
|
+
if (property === "pending")
|
|
15
|
+
return subs().some(sub => !sub.result);
|
|
16
|
+
return subs()[property];
|
|
17
|
+
},
|
|
18
|
+
has(_, property) {
|
|
19
|
+
return property in subs();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function useSubmission(fn, filter) {
|
|
24
|
+
const submissions = useSubmissions(fn, filter);
|
|
25
|
+
return new Proxy({}, {
|
|
26
|
+
get(_, property) {
|
|
27
|
+
if ((submissions.length === 0 && property === "clear") || property === "retry")
|
|
28
|
+
return () => { };
|
|
29
|
+
return submissions[submissions.length - 1]?.[property];
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function useAction(action) {
|
|
34
|
+
const r = useRouter();
|
|
35
|
+
return (...args) => action.apply({ r }, args);
|
|
36
|
+
}
|
|
37
|
+
export function action(fn, options = {}) {
|
|
38
|
+
function mutate(...variables) {
|
|
39
|
+
const router = this.r;
|
|
40
|
+
const form = this.f;
|
|
41
|
+
const p = (router.singleFlight && fn.withOptions
|
|
42
|
+
? fn.withOptions({ headers: { "X-Single-Flight": "true" } })
|
|
43
|
+
: fn)(...variables);
|
|
44
|
+
const [result, setResult] = createSignal();
|
|
45
|
+
let submission;
|
|
46
|
+
function handler(error) {
|
|
47
|
+
return async (res) => {
|
|
48
|
+
const result = await handleResponse(res, error, router.navigatorFactory());
|
|
49
|
+
let retry = null;
|
|
50
|
+
o.onComplete?.({
|
|
51
|
+
...submission,
|
|
52
|
+
result: result?.data,
|
|
53
|
+
error: result?.error,
|
|
54
|
+
pending: false,
|
|
55
|
+
retry() {
|
|
56
|
+
return (retry = submission.retry());
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (retry)
|
|
60
|
+
return retry;
|
|
61
|
+
if (!result)
|
|
62
|
+
return submission.clear();
|
|
63
|
+
setResult(result);
|
|
64
|
+
if (result.error && !form)
|
|
65
|
+
throw result.error;
|
|
66
|
+
return result.data;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
router.submissions[1](s => [
|
|
70
|
+
...s,
|
|
71
|
+
(submission = {
|
|
72
|
+
input: variables,
|
|
73
|
+
url,
|
|
74
|
+
get result() {
|
|
75
|
+
return result()?.data;
|
|
76
|
+
},
|
|
77
|
+
get error() {
|
|
78
|
+
return result()?.error;
|
|
79
|
+
},
|
|
80
|
+
get pending() {
|
|
81
|
+
return !result();
|
|
82
|
+
},
|
|
83
|
+
clear() {
|
|
84
|
+
router.submissions[1](v => v.filter(i => i !== submission));
|
|
85
|
+
},
|
|
86
|
+
retry() {
|
|
87
|
+
setResult(undefined);
|
|
88
|
+
const p = fn(...variables);
|
|
89
|
+
return p.then(handler(), handler(true));
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
]);
|
|
93
|
+
return p.then(handler(), handler(true));
|
|
94
|
+
}
|
|
95
|
+
const o = typeof options === "string" ? { name: options } : options;
|
|
96
|
+
const name = o.name || (!isServer ? String(hashString(fn.toString())) : undefined);
|
|
97
|
+
const url = fn.url || (name && `https://action/${name}`) || "";
|
|
98
|
+
mutate.base = url;
|
|
99
|
+
if (name)
|
|
100
|
+
setFunctionName(mutate, name);
|
|
101
|
+
return toAction(mutate, url);
|
|
102
|
+
}
|
|
103
|
+
function toAction(fn, url) {
|
|
104
|
+
fn.toString = () => {
|
|
105
|
+
if (!url)
|
|
106
|
+
throw new Error("Client Actions need explicit names if server rendered");
|
|
107
|
+
return url;
|
|
108
|
+
};
|
|
109
|
+
fn.with = function (...args) {
|
|
110
|
+
const newFn = function (...passedArgs) {
|
|
111
|
+
return fn.call(this, ...args, ...passedArgs);
|
|
112
|
+
};
|
|
113
|
+
newFn.base = fn.base;
|
|
114
|
+
const uri = new URL(url, mockBase);
|
|
115
|
+
uri.searchParams.set("args", hashKey(args));
|
|
116
|
+
return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
|
|
117
|
+
};
|
|
118
|
+
fn.url = url;
|
|
119
|
+
if (!isServer) {
|
|
120
|
+
actions.set(url, fn);
|
|
121
|
+
getOwner() && onCleanup(() => actions.delete(url));
|
|
122
|
+
}
|
|
123
|
+
return fn;
|
|
124
|
+
}
|
|
125
|
+
const hashString = (s) => s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
|
|
126
|
+
async function handleResponse(response, error, navigate) {
|
|
127
|
+
let data;
|
|
128
|
+
let custom;
|
|
129
|
+
let keys;
|
|
130
|
+
let flightKeys;
|
|
131
|
+
if (response instanceof Response) {
|
|
132
|
+
if (response.headers.has("X-Revalidate"))
|
|
133
|
+
keys = response.headers.get("X-Revalidate").split(",");
|
|
134
|
+
if (response.customBody) {
|
|
135
|
+
data = custom = await response.customBody();
|
|
136
|
+
if (response.headers.has("X-Single-Flight")) {
|
|
137
|
+
data = data._$value;
|
|
138
|
+
delete custom._$value;
|
|
139
|
+
flightKeys = Object.keys(custom);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (response.headers.has("Location")) {
|
|
143
|
+
const locationUrl = response.headers.get("Location") || "/";
|
|
144
|
+
if (locationUrl.startsWith("http")) {
|
|
145
|
+
window.location.href = locationUrl;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
navigate(locationUrl);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (error)
|
|
153
|
+
return { error: response };
|
|
154
|
+
else
|
|
155
|
+
data = response;
|
|
156
|
+
// invalidate
|
|
157
|
+
cacheKeyOp(keys, entry => (entry[0] = 0));
|
|
158
|
+
// set cache
|
|
159
|
+
flightKeys && flightKeys.forEach(k => query.set(k, custom[k]));
|
|
160
|
+
// trigger revalidation
|
|
161
|
+
await revalidate(keys, false);
|
|
162
|
+
return data != null ? { data } : undefined;
|
|
163
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|