@solidjs/router
Advanced tools
Comparing version 0.10.0-beta.5 to 0.10.0-beta.6
@@ -1,3 +0,1 @@ | ||
/*@refresh skip*/ | ||
"use client"; | ||
import { createMemo, mergeProps, splitProps } from "solid-js"; | ||
@@ -4,0 +2,0 @@ import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing"; |
@@ -80,9 +80,17 @@ import { $TRACK, createMemo, createSignal } from "solid-js"; | ||
let data; | ||
if (response instanceof Response && redirectStatusCodes.has(response.status)) { | ||
const locationUrl = response.headers.get("Location") || "/"; | ||
if (locationUrl.startsWith("http")) { | ||
window.location.href = locationUrl; | ||
let keys; | ||
if (response instanceof Response) { | ||
if (response.headers.has("X-Revalidate")) { | ||
keys = response.headers.get("X-Revalidate").split(","); | ||
} | ||
else { | ||
navigate(locationUrl); | ||
if (response.customBody) | ||
data = await response.customBody(); | ||
if (redirectStatusCodes.has(response.status)) { | ||
const locationUrl = response.headers.get("Location") || "/"; | ||
if (locationUrl.startsWith("http")) { | ||
window.location.href = locationUrl; | ||
} | ||
else { | ||
navigate(locationUrl); | ||
} | ||
} | ||
@@ -92,5 +100,4 @@ } | ||
data = response; | ||
// TODO: handle keys | ||
await revalidate(); | ||
await revalidate(keys); | ||
return data; | ||
} |
import { type ReconcileOptions } from "solid-js/store"; | ||
export declare function revalidate(key?: string | any[] | void): Promise<void>; | ||
export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): T; | ||
export declare function revalidate(key?: string | string[] | void): Promise<void>; | ||
export type CachedFunction<T extends (...args: any) => U | Response, U> = T & { | ||
keyFor: (...args: Parameters<T>) => string; | ||
key: string; | ||
}; | ||
export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): CachedFunction<T, U>; |
@@ -8,3 +8,15 @@ import { createSignal, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js"; | ||
const PRELOAD_TIMEOUT = 5000; | ||
const CACHE_TIMEOUT = 180000; | ||
let cacheMap = new Map(); | ||
// cleanup forward/back cache | ||
if (!isServer) { | ||
setInterval(() => { | ||
const now = Date.now(); | ||
for (let [k, v] of cacheMap.entries()) { | ||
if (!v[3].size && now - v[0] > CACHE_TIMEOUT) { | ||
cacheMap.delete(k); | ||
} | ||
} | ||
}, 300000); | ||
} | ||
function getCache() { | ||
@@ -17,9 +29,10 @@ if (!isServer) | ||
export function revalidate(key) { | ||
key && !Array.isArray(key) && (key = [key]); | ||
return startTransition(() => { | ||
const now = Date.now(); | ||
for (let k of cacheMap.keys()) { | ||
if (key === undefined || k === key) { | ||
const set = cacheMap.get(k)[3]; | ||
revalidateSignals(set, now); | ||
cacheMap.delete(k); | ||
if (key === undefined || matchKey(k, key)) { | ||
const entry = cacheMap.get(k); | ||
entry[0] = 0; //force cache miss | ||
revalidateSignals(entry[3], now); // retrigger live signals | ||
} | ||
@@ -35,3 +48,3 @@ } | ||
const [store, setStore] = createStore({}); | ||
return ((...args) => { | ||
const cachedFn = ((...args) => { | ||
const cache = getCache(); | ||
@@ -42,3 +55,3 @@ const intent = getIntent(); | ||
const now = Date.now(); | ||
const key = name + (args.length ? ":" + args.join(":") : ""); | ||
const key = name + hashKey(args); | ||
let cached = cache.get(key); | ||
@@ -60,5 +73,6 @@ let version; | ||
if (!isServer && intent === "navigate") { | ||
res = "then" in cached[1] | ||
? cached[1].then(handleResponse(false), handleResponse(true)) | ||
: handleResponse(false)(cached[1]); | ||
res = | ||
"then" in cached[1] | ||
? cached[1].then(handleResponse(false), handleResponse(true)) | ||
: handleResponse(false)(cached[1]); | ||
startTransition(() => revalidateSignals(cached[3], cached[0])); // update version | ||
@@ -87,5 +101,6 @@ } | ||
if (intent !== "preload") { | ||
res = "then" in res | ||
? res.then(handleResponse(false), handleResponse(true)) | ||
: handleResponse(false)(res); | ||
res = | ||
"then" in res | ||
? res.then(handleResponse(false), handleResponse(true)) | ||
: handleResponse(false)(res); | ||
} | ||
@@ -120,2 +135,30 @@ return res; | ||
}); | ||
cachedFn.keyFor = (...args) => name + hashKey(args); | ||
cachedFn.key = name; | ||
return cachedFn; | ||
} | ||
function matchKey(key, keys) { | ||
for (let k of keys) { | ||
if (key.startsWith(k)) | ||
return true; | ||
} | ||
return false; | ||
} | ||
// Modified from the amazing Tanstack Query library (MIT) | ||
// https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168 | ||
function hashKey(args) { | ||
return JSON.stringify(args, (_, val) => isPlainObject(val) | ||
? Object.keys(val) | ||
.sort() | ||
.reduce((result, key) => { | ||
result[key] = val[key]; | ||
return result; | ||
}, {}) | ||
: val); | ||
} | ||
function isPlainObject(obj) { | ||
let proto; | ||
return (obj != null && | ||
typeof obj === "object" && | ||
(!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype)); | ||
} |
export { createAsync } from "./createAsync"; | ||
export { action, useSubmission, useSubmissions, useAction } from "./action"; | ||
export { cache, revalidate } from "./cache"; | ||
export { action, useSubmission, useSubmissions, useAction, type Action } from "./action"; | ||
export { cache, revalidate, type CachedFunction } from "./cache"; | ||
export { redirect } from "./response"; |
@@ -1,1 +0,5 @@ | ||
export declare function redirect(url: string, init?: number | ResponseInit): Response; | ||
export type RouterResponseInit = ResponseInit & { | ||
revalidate?: string | string[]; | ||
}; | ||
export declare function redirect(url: string, init?: number | RouterResponseInit): Response; | ||
export declare function reload(init: RouterResponseInit): Response; |
export function redirect(url, init = 302) { | ||
let responseInit = init; | ||
if (typeof responseInit === "number") { | ||
responseInit = { status: responseInit }; | ||
let responseInit; | ||
let revalidate; | ||
if (typeof init === "number") { | ||
responseInit = { status: init }; | ||
} | ||
else if (typeof responseInit.status === "undefined") { | ||
responseInit.status = 302; | ||
else { | ||
({ revalidate, ...responseInit } = init); | ||
if (typeof responseInit.status === "undefined") { | ||
responseInit.status = 302; | ||
} | ||
} | ||
const headers = new Headers(responseInit.headers); | ||
headers.set("Location", url); | ||
revalidate && headers.set("X-Revalidate", revalidate.toString()); | ||
const response = new Response(null, { | ||
@@ -17,1 +22,8 @@ ...responseInit, | ||
} | ||
export function reload(init) { | ||
const { revalidate, ...responseInit } = init; | ||
return new Response(null, { | ||
...responseInit, | ||
...(revalidate ? { headers: new Headers(responseInit.headers).set("X-Revalidate", revalidate.toString()) } : {}) | ||
}); | ||
} |
@@ -589,3 +589,3 @@ import { isServer, getRequestEvent, createComponent as createComponent$1, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web'; | ||
location, | ||
intent: intent || "navigate" | ||
intent: intent || "initial" | ||
}); | ||
@@ -761,3 +761,16 @@ return route; | ||
const PRELOAD_TIMEOUT = 5000; | ||
const CACHE_TIMEOUT = 180000; | ||
let cacheMap = new Map(); | ||
// cleanup forward/back cache | ||
if (!isServer) { | ||
setInterval(() => { | ||
const now = Date.now(); | ||
for (let [k, v] of cacheMap.entries()) { | ||
if (!v[3].size && now - v[0] > CACHE_TIMEOUT) { | ||
cacheMap.delete(k); | ||
} | ||
} | ||
}, 300000); | ||
} | ||
function getCache() { | ||
@@ -769,9 +782,10 @@ if (!isServer) return cacheMap; | ||
function revalidate(key) { | ||
key && !Array.isArray(key) && (key = [key]); | ||
return startTransition(() => { | ||
const now = Date.now(); | ||
for (let k of cacheMap.keys()) { | ||
if (key === undefined || k === key) { | ||
const set = cacheMap.get(k)[3]; | ||
revalidateSignals(set, now); | ||
cacheMap.delete(k); | ||
if (key === undefined || matchKey(k, key)) { | ||
const entry = cacheMap.get(k); | ||
entry[0] = 0; //force cache miss | ||
revalidateSignals(entry[3], now); // retrigger live signals | ||
} | ||
@@ -781,2 +795,3 @@ } | ||
} | ||
function revalidateSignals(set, time) { | ||
@@ -787,3 +802,3 @@ for (let s of set) s[1](time); | ||
const [store, setStore] = createStore({}); | ||
return (...args) => { | ||
const cachedFn = (...args) => { | ||
const cache = getCache(); | ||
@@ -794,3 +809,3 @@ const intent = getIntent(); | ||
const now = Date.now(); | ||
const key = name + (args.length ? ":" + args.join(":") : ""); | ||
const key = name + hashKey(args); | ||
let cached = cache.get(key); | ||
@@ -864,4 +879,26 @@ let version; | ||
}; | ||
cachedFn.keyFor = (...args) => name + hashKey(args); | ||
cachedFn.key = name; | ||
return cachedFn; | ||
} | ||
function matchKey(key, keys) { | ||
for (let k of keys) { | ||
if (key.startsWith(k)) return true; | ||
} | ||
return false; | ||
} | ||
// Modified from the amazing Tanstack Query library (MIT) | ||
// https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168 | ||
function hashKey(args) { | ||
return JSON.stringify(args, (_, val) => isPlainObject(val) ? Object.keys(val).sort().reduce((result, key) => { | ||
result[key] = val[key]; | ||
return result; | ||
}, {}) : val); | ||
} | ||
function isPlainObject(obj) { | ||
let proto; | ||
return obj != null && typeof obj === "object" && (!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype); | ||
} | ||
const actions = /* #__PURE__ */new Map(); | ||
@@ -936,12 +973,18 @@ function useSubmissions(fn, filter) { | ||
let data; | ||
if (response instanceof Response && redirectStatusCodes.has(response.status)) { | ||
const locationUrl = response.headers.get("Location") || "/"; | ||
if (locationUrl.startsWith("http")) { | ||
window.location.href = locationUrl; | ||
} else { | ||
navigate(locationUrl); | ||
let keys; | ||
if (response instanceof Response) { | ||
if (response.headers.has("X-Revalidate")) { | ||
keys = response.headers.get("X-Revalidate").split(","); | ||
} | ||
if (response.customBody) data = await response.customBody(); | ||
if (redirectStatusCodes.has(response.status)) { | ||
const locationUrl = response.headers.get("Location") || "/"; | ||
if (locationUrl.startsWith("http")) { | ||
window.location.href = locationUrl; | ||
} else { | ||
navigate(locationUrl); | ||
} | ||
} | ||
} else data = response; | ||
// TODO: handle keys | ||
await revalidate(); | ||
await revalidate(keys); | ||
return data; | ||
@@ -1111,3 +1154,3 @@ } | ||
function MemoryRouter(props) { | ||
function createMemoryHistory() { | ||
const entries = ["/"]; | ||
@@ -1122,9 +1165,9 @@ let index = 0; | ||
}; | ||
return createRouter({ | ||
return { | ||
get: () => entries[index], | ||
set({ | ||
set: ({ | ||
value, | ||
scroll, | ||
replace | ||
}) { | ||
}) => { | ||
if (replace) { | ||
@@ -1140,3 +1183,10 @@ entries[index] = value; | ||
}, | ||
init(listener) { | ||
back: () => { | ||
go(-1); | ||
}, | ||
forward: () => { | ||
go(1); | ||
}, | ||
go, | ||
listen: listener => { | ||
listeners.push(listener); | ||
@@ -1147,5 +1197,13 @@ return () => { | ||
}; | ||
}, | ||
} | ||
}; | ||
} | ||
function MemoryRouter(props) { | ||
const memoryHistory = props.history || createMemoryHistory(); | ||
return createRouter({ | ||
get: memoryHistory.get, | ||
set: memoryHistory.set, | ||
init: memoryHistory.listen, | ||
utils: { | ||
go | ||
go: memoryHistory.go | ||
} | ||
@@ -1155,3 +1213,2 @@ })(props); | ||
/*@refresh skip*/ | ||
const _tmpl$ = /*#__PURE__*/template(`<a>`); | ||
@@ -1271,12 +1328,20 @@ function A(props) { | ||
function redirect(url, init = 302) { | ||
let responseInit = init; | ||
if (typeof responseInit === "number") { | ||
let responseInit; | ||
let revalidate; | ||
if (typeof init === "number") { | ||
responseInit = { | ||
status: responseInit | ||
status: init | ||
}; | ||
} else if (typeof responseInit.status === "undefined") { | ||
responseInit.status = 302; | ||
} else { | ||
({ | ||
revalidate, | ||
...responseInit | ||
} = init); | ||
if (typeof responseInit.status === "undefined") { | ||
responseInit.status = 302; | ||
} | ||
} | ||
const headers = new Headers(responseInit.headers); | ||
headers.set("Location", url); | ||
revalidate && headers.set("X-Revalidate", revalidate.toString()); | ||
const response = new Response(null, { | ||
@@ -1289,2 +1354,2 @@ ...responseInit, | ||
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createRouter, redirect, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions }; | ||
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, redirect, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions }; |
import type { Component, JSX } from "solid-js"; | ||
import type { MatchFilters, RouteLoadFunc, RouterIntegration, RouteSectionProps } from "../types"; | ||
export type RouterProps = { | ||
export type BaseRouterProps = { | ||
base?: string; | ||
@@ -9,3 +9,3 @@ actionBase?: string; | ||
}; | ||
export declare const createRouterComponent: (router: RouterIntegration) => (props: RouterProps) => JSX.Element; | ||
export declare const createRouterComponent: (router: RouterIntegration) => (props: BaseRouterProps) => JSX.Element; | ||
export type RouteProps<S extends string> = { | ||
@@ -12,0 +12,0 @@ path?: S | S[]; |
@@ -8,4 +8,4 @@ import type { LocationChange, RouterContext, RouterUtils } from "../types"; | ||
utils?: Partial<RouterUtils>; | ||
}): (props: import("./components").RouterProps) => import("solid-js").JSX.Element; | ||
}): (props: import("./components").BaseRouterProps) => import("solid-js").JSX.Element; | ||
export declare function bindEvent(target: EventTarget, type: string, handler: EventListener): () => void; | ||
export declare function scrollToHash(hash: string, fallbackTop?: boolean): void; |
import type { JSX } from "solid-js"; | ||
import type { RouterProps } from "./components"; | ||
import type { BaseRouterProps } from "./components"; | ||
export declare function hashParser(str: string): string; | ||
export declare function HashRouter(props: RouterProps): JSX.Element; | ||
export type HashRouterProps = BaseRouterProps; | ||
export declare function HashRouter(props: HashRouterProps): JSX.Element; |
export { Route } from "./components"; | ||
export type { RouterProps, RouteProps } from "./components"; | ||
export type { BaseRouterProps, RouteProps } from "./components"; | ||
export { createRouter } from "./createRouter"; | ||
export { Router } from "./Router"; | ||
export type { RouterProps } from "./Router"; | ||
export { HashRouter } from "./HashRouter"; | ||
export { MemoryRouter } from "./MemoryRouter"; | ||
export type { HashRouterProps } from "./HashRouter"; | ||
export { MemoryRouter, createMemoryHistory } from "./MemoryRouter"; | ||
export type { MemoryRouterProps, MemoryHistory } from "./MemoryRouter"; | ||
export { StaticRouter } from "./StaticRouter"; | ||
export type { StaticRouterProps } from "./StaticRouter"; |
@@ -5,3 +5,3 @@ export { Route } from "./components"; | ||
export { HashRouter } from "./HashRouter"; | ||
export { MemoryRouter } from "./MemoryRouter"; | ||
export { MemoryRouter, createMemoryHistory } from "./MemoryRouter"; | ||
export { StaticRouter } from "./StaticRouter"; |
@@ -1,3 +0,21 @@ | ||
import type { RouterProps } from "./components"; | ||
import type { LocationChange } from "../types"; | ||
import type { BaseRouterProps } from "./components"; | ||
import type { JSX } from "solid-js"; | ||
export declare function MemoryRouter(props: RouterProps): JSX.Element; | ||
export type MemoryHistory = { | ||
get: () => string; | ||
set: (change: LocationChange) => void; | ||
go: (delta: number) => void; | ||
listen: (listener: (value: string) => void) => () => void; | ||
}; | ||
export declare function createMemoryHistory(): { | ||
get: () => string; | ||
set: ({ value, scroll, replace }: LocationChange) => void; | ||
back: () => void; | ||
forward: () => void; | ||
go: (n: number) => void; | ||
listen: (listener: (value: string) => void) => () => void; | ||
}; | ||
export type MemoryRouterProps = BaseRouterProps & { | ||
history?: MemoryHistory; | ||
}; | ||
export declare function MemoryRouter(props: MemoryRouterProps): JSX.Element; |
import { createRouter, scrollToHash } from "./createRouter"; | ||
export function MemoryRouter(props) { | ||
export function createMemoryHistory() { | ||
const entries = ["/"]; | ||
@@ -12,5 +12,5 @@ let index = 0; | ||
}; | ||
return createRouter({ | ||
return { | ||
get: () => entries[index], | ||
set({ value, scroll, replace }) { | ||
set: ({ value, scroll, replace }) => { | ||
if (replace) { | ||
@@ -27,3 +27,10 @@ entries[index] = value; | ||
}, | ||
init(listener) { | ||
back: () => { | ||
go(-1); | ||
}, | ||
forward: () => { | ||
go(1); | ||
}, | ||
go, | ||
listen: (listener) => { | ||
listeners.push(listener); | ||
@@ -34,7 +41,15 @@ return () => { | ||
}; | ||
}, | ||
} | ||
}; | ||
} | ||
export function MemoryRouter(props) { | ||
const memoryHistory = props.history || createMemoryHistory(); | ||
return createRouter({ | ||
get: memoryHistory.get, | ||
set: memoryHistory.set, | ||
init: memoryHistory.listen, | ||
utils: { | ||
go | ||
go: memoryHistory.go | ||
} | ||
})(props); | ||
} |
@@ -1,5 +0,6 @@ | ||
import type { RouterProps } from "./components"; | ||
import type { BaseRouterProps } from "./components"; | ||
import type { JSX } from "solid-js"; | ||
export declare function Router(props: RouterProps & { | ||
export type RouterProps = BaseRouterProps & { | ||
url?: string; | ||
}): JSX.Element; | ||
}; | ||
export declare function Router(props: RouterProps): JSX.Element; |
@@ -1,5 +0,6 @@ | ||
import { type RouterProps } from "./components"; | ||
import { type BaseRouterProps } from "./components"; | ||
import type { JSX } from "solid-js"; | ||
export declare function StaticRouter(props: RouterProps & { | ||
export type StaticRouterProps = BaseRouterProps & { | ||
url?: string; | ||
}): JSX.Element; | ||
}; | ||
export declare function StaticRouter(props: StaticRouterProps): JSX.Element; |
@@ -384,4 +384,4 @@ import { createComponent, createContext, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack, useContext, startTransition, resetErrorBoundaries } from "solid-js"; | ||
component.preload(); | ||
load && load({ params, location, intent: intent || "navigate" }); | ||
load && load({ params, location, intent: intent || "initial" }); | ||
return route; | ||
} |
@@ -42,3 +42,3 @@ import type { Component, JSX, Signal } from "solid-js"; | ||
} | ||
export type Intent = "native" | "navigate" | "preload"; | ||
export type Intent = "initial" | "native" | "navigate" | "preload"; | ||
export interface RouteLoadFuncArgs { | ||
@@ -45,0 +45,0 @@ params: Params; |
@@ -9,3 +9,3 @@ { | ||
"license": "MIT", | ||
"version": "0.10.0-beta.5", | ||
"version": "0.10.0-beta.6", | ||
"homepage": "https://github.com/solidjs/solid-router#readme", | ||
@@ -12,0 +12,0 @@ "repository": { |
@@ -385,8 +385,8 @@ <p> | ||
```jsx | ||
import { action, revalidate, redirect } from "@solidjs/router" | ||
// anywhere | ||
const myAction = action(async (data) => { | ||
await doMutation(data); | ||
return redirect("/", { | ||
invalidate: [getUser, data.id] | ||
}) // returns a response | ||
throw redirect("/", { revalidate: getUser.keyFor(data.id) }); // throw a response to do a redirect | ||
}); | ||
@@ -393,0 +393,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
135373
3107