@benjavicente/router-core 1.168.9
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/LICENSE +21 -0
- package/README.md +5 -0
- package/bin/intent.js +25 -0
- package/dist/cjs/Matches.cjs +17 -0
- package/dist/cjs/Matches.cjs.map +1 -0
- package/dist/cjs/Matches.d.cts +139 -0
- package/dist/cjs/RouterProvider.d.cts +27 -0
- package/dist/cjs/config.cjs +11 -0
- package/dist/cjs/config.cjs.map +1 -0
- package/dist/cjs/config.d.cts +17 -0
- package/dist/cjs/defer.cjs +41 -0
- package/dist/cjs/defer.cjs.map +1 -0
- package/dist/cjs/defer.d.cts +37 -0
- package/dist/cjs/fileRoute.d.cts +24 -0
- package/dist/cjs/global.d.cts +7 -0
- package/dist/cjs/hash-scroll.cjs +20 -0
- package/dist/cjs/hash-scroll.cjs.map +1 -0
- package/dist/cjs/hash-scroll.d.cts +7 -0
- package/dist/cjs/history.d.cts +8 -0
- package/dist/cjs/index.cjs +96 -0
- package/dist/cjs/index.d.cts +53 -0
- package/dist/cjs/invariant.cjs +8 -0
- package/dist/cjs/invariant.cjs.map +1 -0
- package/dist/cjs/invariant.d.cts +1 -0
- package/dist/cjs/isServer/client.cjs +7 -0
- package/dist/cjs/isServer/client.cjs.map +1 -0
- package/dist/cjs/isServer/client.d.cts +1 -0
- package/dist/cjs/isServer/development.cjs +7 -0
- package/dist/cjs/isServer/development.cjs.map +1 -0
- package/dist/cjs/isServer/development.d.cts +1 -0
- package/dist/cjs/isServer/server.cjs +7 -0
- package/dist/cjs/isServer/server.cjs.map +1 -0
- package/dist/cjs/isServer/server.d.cts +1 -0
- package/dist/cjs/link.cjs +6 -0
- package/dist/cjs/link.cjs.map +1 -0
- package/dist/cjs/link.d.cts +221 -0
- package/dist/cjs/load-matches.cjs +659 -0
- package/dist/cjs/load-matches.cjs.map +1 -0
- package/dist/cjs/load-matches.d.cts +18 -0
- package/dist/cjs/location.d.cts +50 -0
- package/dist/cjs/lru-cache.cjs +70 -0
- package/dist/cjs/lru-cache.cjs.map +1 -0
- package/dist/cjs/lru-cache.d.cts +6 -0
- package/dist/cjs/manifest.cjs +18 -0
- package/dist/cjs/manifest.cjs.map +1 -0
- package/dist/cjs/manifest.d.cts +35 -0
- package/dist/cjs/new-process-route-tree.cjs +754 -0
- package/dist/cjs/new-process-route-tree.cjs.map +1 -0
- package/dist/cjs/new-process-route-tree.d.cts +236 -0
- package/dist/cjs/not-found.cjs +26 -0
- package/dist/cjs/not-found.cjs.map +1 -0
- package/dist/cjs/not-found.d.cts +32 -0
- package/dist/cjs/path.cjs +252 -0
- package/dist/cjs/path.cjs.map +1 -0
- package/dist/cjs/path.d.cts +56 -0
- package/dist/cjs/qss.cjs +70 -0
- package/dist/cjs/qss.cjs.map +1 -0
- package/dist/cjs/qss.d.cts +33 -0
- package/dist/cjs/redirect.cjs +56 -0
- package/dist/cjs/redirect.cjs.map +1 -0
- package/dist/cjs/redirect.d.cts +77 -0
- package/dist/cjs/rewrite.cjs +68 -0
- package/dist/cjs/rewrite.cjs.map +1 -0
- package/dist/cjs/rewrite.d.cts +30 -0
- package/dist/cjs/root.cjs +7 -0
- package/dist/cjs/root.cjs.map +1 -0
- package/dist/cjs/root.d.cts +3 -0
- package/dist/cjs/route.cjs +100 -0
- package/dist/cjs/route.cjs.map +1 -0
- package/dist/cjs/route.d.cts +552 -0
- package/dist/cjs/routeInfo.d.cts +54 -0
- package/dist/cjs/router.cjs +1173 -0
- package/dist/cjs/router.cjs.map +1 -0
- package/dist/cjs/router.d.cts +734 -0
- package/dist/cjs/scroll-restoration-inline.cjs +6 -0
- package/dist/cjs/scroll-restoration-inline.cjs.map +1 -0
- package/dist/cjs/scroll-restoration-inline.d.cts +6 -0
- package/dist/cjs/scroll-restoration-script/client.cjs +9 -0
- package/dist/cjs/scroll-restoration-script/client.cjs.map +1 -0
- package/dist/cjs/scroll-restoration-script/client.d.cts +2 -0
- package/dist/cjs/scroll-restoration-script/server.cjs +30 -0
- package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -0
- package/dist/cjs/scroll-restoration-script/server.d.cts +2 -0
- package/dist/cjs/scroll-restoration.cjs +191 -0
- package/dist/cjs/scroll-restoration.cjs.map +1 -0
- package/dist/cjs/scroll-restoration.d.cts +38 -0
- package/dist/cjs/searchMiddleware.cjs +55 -0
- package/dist/cjs/searchMiddleware.cjs.map +1 -0
- package/dist/cjs/searchMiddleware.d.cts +25 -0
- package/dist/cjs/searchParams.cjs +65 -0
- package/dist/cjs/searchParams.cjs.map +1 -0
- package/dist/cjs/searchParams.d.cts +31 -0
- package/dist/cjs/ssr/client.cjs +7 -0
- package/dist/cjs/ssr/client.d.cts +6 -0
- package/dist/cjs/ssr/constants.cjs +8 -0
- package/dist/cjs/ssr/constants.cjs.map +1 -0
- package/dist/cjs/ssr/constants.d.cts +3 -0
- package/dist/cjs/ssr/createRequestHandler.cjs +44 -0
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -0
- package/dist/cjs/ssr/createRequestHandler.d.cts +9 -0
- package/dist/cjs/ssr/handlerCallback.cjs +8 -0
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -0
- package/dist/cjs/ssr/handlerCallback.d.cts +9 -0
- package/dist/cjs/ssr/headers.cjs +21 -0
- package/dist/cjs/ssr/headers.cjs.map +1 -0
- package/dist/cjs/ssr/headers.d.cts +3 -0
- package/dist/cjs/ssr/json.cjs +11 -0
- package/dist/cjs/ssr/json.cjs.map +1 -0
- package/dist/cjs/ssr/json.d.cts +10 -0
- package/dist/cjs/ssr/serializer/RawStream.cjs +287 -0
- package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/RawStream.d.cts +64 -0
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs +32 -0
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.d.cts +9 -0
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs +13 -0
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/seroval-plugins.d.cts +2 -0
- package/dist/cjs/ssr/serializer/transformer.cjs +53 -0
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/transformer.d.cts +91 -0
- package/dist/cjs/ssr/server.cjs +13 -0
- package/dist/cjs/ssr/server.d.cts +6 -0
- package/dist/cjs/ssr/ssr-client.cjs +183 -0
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -0
- package/dist/cjs/ssr/ssr-client.d.cts +10 -0
- package/dist/cjs/ssr/ssr-match-id.cjs +12 -0
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -0
- package/dist/cjs/ssr/ssr-match-id.d.cts +2 -0
- package/dist/cjs/ssr/ssr-server.cjs +279 -0
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -0
- package/dist/cjs/ssr/ssr-server.d.cts +42 -0
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +327 -0
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -0
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +11 -0
- package/dist/cjs/ssr/tsrScript.cjs +6 -0
- package/dist/cjs/ssr/tsrScript.cjs.map +1 -0
- package/dist/cjs/ssr/tsrScript.d.cts +1 -0
- package/dist/cjs/ssr/types.d.cts +30 -0
- package/dist/cjs/stores.cjs +148 -0
- package/dist/cjs/stores.cjs.map +1 -0
- package/dist/cjs/stores.d.cts +70 -0
- package/dist/cjs/structuralSharing.d.cts +4 -0
- package/dist/cjs/typePrimitives.d.cts +65 -0
- package/dist/cjs/useLoaderData.d.cts +5 -0
- package/dist/cjs/useLoaderDeps.d.cts +5 -0
- package/dist/cjs/useNavigate.d.cts +3 -0
- package/dist/cjs/useParams.d.cts +5 -0
- package/dist/cjs/useRouteContext.d.cts +9 -0
- package/dist/cjs/useSearch.d.cts +5 -0
- package/dist/cjs/utils.cjs +339 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/cjs/utils.d.cts +178 -0
- package/dist/cjs/validators.d.cts +51 -0
- package/dist/esm/Matches.d.ts +139 -0
- package/dist/esm/Matches.js +17 -0
- package/dist/esm/Matches.js.map +1 -0
- package/dist/esm/RouterProvider.d.ts +27 -0
- package/dist/esm/config.d.ts +17 -0
- package/dist/esm/config.js +11 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/defer.d.ts +37 -0
- package/dist/esm/defer.js +40 -0
- package/dist/esm/defer.js.map +1 -0
- package/dist/esm/fileRoute.d.ts +24 -0
- package/dist/esm/global.d.ts +7 -0
- package/dist/esm/hash-scroll.d.ts +7 -0
- package/dist/esm/hash-scroll.js +20 -0
- package/dist/esm/hash-scroll.js.map +1 -0
- package/dist/esm/history.d.ts +8 -0
- package/dist/esm/index.d.ts +53 -0
- package/dist/esm/index.js +24 -0
- package/dist/esm/invariant.d.ts +1 -0
- package/dist/esm/invariant.js +8 -0
- package/dist/esm/invariant.js.map +1 -0
- package/dist/esm/isServer/client.d.ts +1 -0
- package/dist/esm/isServer/client.js +6 -0
- package/dist/esm/isServer/client.js.map +1 -0
- package/dist/esm/isServer/development.d.ts +1 -0
- package/dist/esm/isServer/development.js +6 -0
- package/dist/esm/isServer/development.js.map +1 -0
- package/dist/esm/isServer/server.d.ts +1 -0
- package/dist/esm/isServer/server.js +6 -0
- package/dist/esm/isServer/server.js.map +1 -0
- package/dist/esm/link.d.ts +221 -0
- package/dist/esm/link.js +6 -0
- package/dist/esm/link.js.map +1 -0
- package/dist/esm/load-matches.d.ts +18 -0
- package/dist/esm/load-matches.js +657 -0
- package/dist/esm/load-matches.js.map +1 -0
- package/dist/esm/location.d.ts +50 -0
- package/dist/esm/lru-cache.d.ts +6 -0
- package/dist/esm/lru-cache.js +70 -0
- package/dist/esm/lru-cache.js.map +1 -0
- package/dist/esm/manifest.d.ts +35 -0
- package/dist/esm/manifest.js +17 -0
- package/dist/esm/manifest.js.map +1 -0
- package/dist/esm/new-process-route-tree.d.ts +236 -0
- package/dist/esm/new-process-route-tree.js +749 -0
- package/dist/esm/new-process-route-tree.js.map +1 -0
- package/dist/esm/not-found.d.ts +32 -0
- package/dist/esm/not-found.js +25 -0
- package/dist/esm/not-found.js.map +1 -0
- package/dist/esm/path.d.ts +56 -0
- package/dist/esm/path.js +243 -0
- package/dist/esm/path.js.map +1 -0
- package/dist/esm/qss.d.ts +33 -0
- package/dist/esm/qss.js +69 -0
- package/dist/esm/qss.js.map +1 -0
- package/dist/esm/redirect.d.ts +77 -0
- package/dist/esm/redirect.js +53 -0
- package/dist/esm/redirect.js.map +1 -0
- package/dist/esm/rewrite.d.ts +30 -0
- package/dist/esm/rewrite.js +65 -0
- package/dist/esm/rewrite.js.map +1 -0
- package/dist/esm/root.d.ts +3 -0
- package/dist/esm/root.js +7 -0
- package/dist/esm/root.js.map +1 -0
- package/dist/esm/route.d.ts +552 -0
- package/dist/esm/route.js +98 -0
- package/dist/esm/route.js.map +1 -0
- package/dist/esm/routeInfo.d.ts +54 -0
- package/dist/esm/router.d.ts +734 -0
- package/dist/esm/router.js +1165 -0
- package/dist/esm/router.js.map +1 -0
- package/dist/esm/scroll-restoration-inline.d.ts +6 -0
- package/dist/esm/scroll-restoration-inline.js +6 -0
- package/dist/esm/scroll-restoration-inline.js.map +1 -0
- package/dist/esm/scroll-restoration-script/client.d.ts +2 -0
- package/dist/esm/scroll-restoration-script/client.js +8 -0
- package/dist/esm/scroll-restoration-script/client.js.map +1 -0
- package/dist/esm/scroll-restoration-script/server.d.ts +2 -0
- package/dist/esm/scroll-restoration-script/server.js +29 -0
- package/dist/esm/scroll-restoration-script/server.js.map +1 -0
- package/dist/esm/scroll-restoration.d.ts +38 -0
- package/dist/esm/scroll-restoration.js +187 -0
- package/dist/esm/scroll-restoration.js.map +1 -0
- package/dist/esm/searchMiddleware.d.ts +25 -0
- package/dist/esm/searchMiddleware.js +54 -0
- package/dist/esm/searchMiddleware.js.map +1 -0
- package/dist/esm/searchParams.d.ts +31 -0
- package/dist/esm/searchParams.js +62 -0
- package/dist/esm/searchParams.js.map +1 -0
- package/dist/esm/ssr/client.d.ts +6 -0
- package/dist/esm/ssr/client.js +4 -0
- package/dist/esm/ssr/constants.d.ts +3 -0
- package/dist/esm/ssr/constants.js +7 -0
- package/dist/esm/ssr/constants.js.map +1 -0
- package/dist/esm/ssr/createRequestHandler.d.ts +9 -0
- package/dist/esm/ssr/createRequestHandler.js +44 -0
- package/dist/esm/ssr/createRequestHandler.js.map +1 -0
- package/dist/esm/ssr/handlerCallback.d.ts +9 -0
- package/dist/esm/ssr/handlerCallback.js +8 -0
- package/dist/esm/ssr/handlerCallback.js.map +1 -0
- package/dist/esm/ssr/headers.d.ts +3 -0
- package/dist/esm/ssr/headers.js +21 -0
- package/dist/esm/ssr/headers.js.map +1 -0
- package/dist/esm/ssr/json.d.ts +10 -0
- package/dist/esm/ssr/json.js +11 -0
- package/dist/esm/ssr/json.js.map +1 -0
- package/dist/esm/ssr/serializer/RawStream.d.ts +64 -0
- package/dist/esm/ssr/serializer/RawStream.js +282 -0
- package/dist/esm/ssr/serializer/RawStream.js.map +1 -0
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.d.ts +9 -0
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js +33 -0
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -0
- package/dist/esm/ssr/serializer/seroval-plugins.d.ts +2 -0
- package/dist/esm/ssr/serializer/seroval-plugins.js +13 -0
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -0
- package/dist/esm/ssr/serializer/transformer.d.ts +91 -0
- package/dist/esm/ssr/serializer/transformer.js +51 -0
- package/dist/esm/ssr/serializer/transformer.js.map +1 -0
- package/dist/esm/ssr/server.d.ts +6 -0
- package/dist/esm/ssr/server.js +5 -0
- package/dist/esm/ssr/ssr-client.d.ts +10 -0
- package/dist/esm/ssr/ssr-client.js +183 -0
- package/dist/esm/ssr/ssr-client.js.map +1 -0
- package/dist/esm/ssr/ssr-match-id.d.ts +2 -0
- package/dist/esm/ssr/ssr-match-id.js +11 -0
- package/dist/esm/ssr/ssr-match-id.js.map +1 -0
- package/dist/esm/ssr/ssr-server.d.ts +42 -0
- package/dist/esm/ssr/ssr-server.js +277 -0
- package/dist/esm/ssr/ssr-server.js.map +1 -0
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +11 -0
- package/dist/esm/ssr/transformStreamWithRouter.js +325 -0
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -0
- package/dist/esm/ssr/tsrScript.d.ts +0 -0
- package/dist/esm/ssr/tsrScript.js +6 -0
- package/dist/esm/ssr/tsrScript.js.map +1 -0
- package/dist/esm/ssr/types.d.ts +30 -0
- package/dist/esm/stores.d.ts +70 -0
- package/dist/esm/stores.js +146 -0
- package/dist/esm/stores.js.map +1 -0
- package/dist/esm/structuralSharing.d.ts +4 -0
- package/dist/esm/typePrimitives.d.ts +65 -0
- package/dist/esm/useLoaderData.d.ts +5 -0
- package/dist/esm/useLoaderDeps.d.ts +5 -0
- package/dist/esm/useNavigate.d.ts +3 -0
- package/dist/esm/useParams.d.ts +5 -0
- package/dist/esm/useRouteContext.d.ts +9 -0
- package/dist/esm/useSearch.d.ts +5 -0
- package/dist/esm/utils.d.ts +178 -0
- package/dist/esm/utils.js +322 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/validators.d.ts +51 -0
- package/package.json +200 -0
- package/skills/router-core/SKILL.md +139 -0
- package/skills/router-core/auth-and-guards/SKILL.md +458 -0
- package/skills/router-core/code-splitting/SKILL.md +322 -0
- package/skills/router-core/data-loading/SKILL.md +485 -0
- package/skills/router-core/navigation/SKILL.md +448 -0
- package/skills/router-core/not-found-and-errors/SKILL.md +435 -0
- package/skills/router-core/path-params/SKILL.md +382 -0
- package/skills/router-core/search-params/SKILL.md +349 -0
- package/skills/router-core/search-params/references/validation-patterns.md +379 -0
- package/skills/router-core/ssr/SKILL.md +437 -0
- package/skills/router-core/type-safety/SKILL.md +497 -0
- package/src/Matches.ts +291 -0
- package/src/RouterProvider.ts +47 -0
- package/src/config.ts +42 -0
- package/src/defer.ts +69 -0
- package/src/fileRoute.ts +164 -0
- package/src/global.ts +9 -0
- package/src/hash-scroll.ts +21 -0
- package/src/history.ts +9 -0
- package/src/index.ts +471 -0
- package/src/invariant.ts +3 -0
- package/src/isServer/client.ts +1 -0
- package/src/isServer/development.ts +2 -0
- package/src/isServer/server.ts +1 -0
- package/src/link.ts +704 -0
- package/src/load-matches.ts +1281 -0
- package/src/location.ts +51 -0
- package/src/lru-cache.ts +74 -0
- package/src/manifest.ts +68 -0
- package/src/new-process-route-tree.ts +1387 -0
- package/src/not-found.ts +41 -0
- package/src/path.ts +436 -0
- package/src/qss.ts +81 -0
- package/src/redirect.ts +179 -0
- package/src/rewrite.ts +93 -0
- package/src/root.ts +3 -0
- package/src/route.ts +2235 -0
- package/src/routeInfo.ts +235 -0
- package/src/router.ts +3207 -0
- package/src/scroll-restoration-inline.ts +81 -0
- package/src/scroll-restoration-script/client.ts +5 -0
- package/src/scroll-restoration-script/server.ts +64 -0
- package/src/scroll-restoration.ts +357 -0
- package/src/searchMiddleware.ts +76 -0
- package/src/searchParams.ts +90 -0
- package/src/ssr/client.ts +6 -0
- package/src/ssr/constants.ts +3 -0
- package/src/ssr/createRequestHandler.ts +98 -0
- package/src/ssr/handlerCallback.ts +15 -0
- package/src/ssr/headers.ts +40 -0
- package/src/ssr/json.ts +16 -0
- package/src/ssr/serializer/RawStream.ts +464 -0
- package/src/ssr/serializer/ShallowErrorPlugin.ts +43 -0
- package/src/ssr/serializer/seroval-plugins.ts +12 -0
- package/src/ssr/serializer/transformer.ts +312 -0
- package/src/ssr/server.ts +14 -0
- package/src/ssr/ssr-client.ts +313 -0
- package/src/ssr/ssr-match-id.ts +7 -0
- package/src/ssr/ssr-server.ts +425 -0
- package/src/ssr/transformStreamWithRouter.ts +493 -0
- package/src/ssr/tsrScript.ts +20 -0
- package/src/ssr/types.ts +41 -0
- package/src/stores.ts +342 -0
- package/src/structuralSharing.ts +7 -0
- package/src/typePrimitives.ts +181 -0
- package/src/useLoaderData.ts +20 -0
- package/src/useLoaderDeps.ts +13 -0
- package/src/useNavigate.ts +13 -0
- package/src/useParams.ts +20 -0
- package/src/useRouteContext.ts +39 -0
- package/src/useSearch.ts +20 -0
- package/src/utils.ts +708 -0
- package/src/validators.ts +121 -0
- package/src/vite-env.d.ts +4 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export default function (options: {
|
|
2
|
+
storageKey: string
|
|
3
|
+
key?: string
|
|
4
|
+
behavior?: ScrollToOptions['behavior']
|
|
5
|
+
shouldScrollRestoration?: boolean
|
|
6
|
+
}) {
|
|
7
|
+
let byKey
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
byKey = JSON.parse(sessionStorage.getItem(options.storageKey) || '{}')
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(error)
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const resolvedKey = options.key || window.history.state?.__TSR_key
|
|
17
|
+
const elementEntries = resolvedKey ? byKey[resolvedKey] : undefined
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
options.shouldScrollRestoration &&
|
|
21
|
+
elementEntries &&
|
|
22
|
+
typeof elementEntries === 'object' &&
|
|
23
|
+
Object.keys(elementEntries).length > 0
|
|
24
|
+
) {
|
|
25
|
+
for (const elementSelector in elementEntries) {
|
|
26
|
+
const entry = elementEntries[elementSelector]
|
|
27
|
+
|
|
28
|
+
if (!entry || typeof entry !== 'object') {
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const scrollX = entry.scrollX
|
|
33
|
+
const scrollY = entry.scrollY
|
|
34
|
+
|
|
35
|
+
if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (elementSelector === 'window') {
|
|
40
|
+
window.scrollTo({
|
|
41
|
+
top: scrollY,
|
|
42
|
+
left: scrollX,
|
|
43
|
+
behavior: options.behavior,
|
|
44
|
+
})
|
|
45
|
+
} else if (elementSelector) {
|
|
46
|
+
let element
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
element = document.querySelector(elementSelector)
|
|
50
|
+
} catch {
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (element) {
|
|
55
|
+
element.scrollLeft = scrollX
|
|
56
|
+
element.scrollTop = scrollY
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hash = window.location.hash.split('#', 2)[1]
|
|
65
|
+
|
|
66
|
+
if (hash) {
|
|
67
|
+
const hashScrollIntoViewOptions =
|
|
68
|
+
window.history.state?.__hashScrollIntoViewOptions ?? true
|
|
69
|
+
|
|
70
|
+
if (hashScrollIntoViewOptions) {
|
|
71
|
+
const el = document.getElementById(hash)
|
|
72
|
+
if (el) {
|
|
73
|
+
el.scrollIntoView(hashScrollIntoViewOptions)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
window.scrollTo({ top: 0, left: 0, behavior: options.behavior })
|
|
81
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import minifiedScrollRestorationScript from '../scroll-restoration-inline?script-string'
|
|
2
|
+
import {
|
|
3
|
+
defaultGetScrollRestorationKey,
|
|
4
|
+
storageKey,
|
|
5
|
+
} from '../scroll-restoration'
|
|
6
|
+
import { escapeHtml } from '../utils'
|
|
7
|
+
import type { AnyRouter } from '../router'
|
|
8
|
+
|
|
9
|
+
type InlineScrollRestorationScriptOptions = {
|
|
10
|
+
storageKey: string
|
|
11
|
+
key?: string
|
|
12
|
+
behavior?: ScrollToOptions['behavior']
|
|
13
|
+
shouldScrollRestoration?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(
|
|
17
|
+
JSON.stringify({
|
|
18
|
+
storageKey,
|
|
19
|
+
shouldScrollRestoration: true,
|
|
20
|
+
} satisfies InlineScrollRestorationScriptOptions),
|
|
21
|
+
)})`
|
|
22
|
+
|
|
23
|
+
function getScrollRestorationScript(
|
|
24
|
+
options: InlineScrollRestorationScriptOptions,
|
|
25
|
+
) {
|
|
26
|
+
if (
|
|
27
|
+
options.storageKey === storageKey &&
|
|
28
|
+
options.shouldScrollRestoration === true &&
|
|
29
|
+
options.key === undefined &&
|
|
30
|
+
options.behavior === undefined
|
|
31
|
+
) {
|
|
32
|
+
return defaultInlineScrollRestorationScript
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return `(${minifiedScrollRestorationScript})(${escapeHtml(JSON.stringify(options))})`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getScrollRestorationScriptForRouter(router: AnyRouter) {
|
|
39
|
+
if (
|
|
40
|
+
typeof router.options.scrollRestoration === 'function' &&
|
|
41
|
+
!router.options.scrollRestoration({ location: router.latestLocation })
|
|
42
|
+
) {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const getKey = router.options.getScrollRestorationKey
|
|
47
|
+
if (!getKey) {
|
|
48
|
+
return defaultInlineScrollRestorationScript
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const location = router.latestLocation
|
|
52
|
+
const userKey = getKey(location)
|
|
53
|
+
const defaultKey = defaultGetScrollRestorationKey(location)
|
|
54
|
+
|
|
55
|
+
if (userKey === defaultKey) {
|
|
56
|
+
return defaultInlineScrollRestorationScript
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return getScrollRestorationScript({
|
|
60
|
+
storageKey,
|
|
61
|
+
shouldScrollRestoration: true,
|
|
62
|
+
key: userKey,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { isServer } from '@benjavicente/router-core/isServer'
|
|
2
|
+
import { functionalUpdate, isPlainObject } from './utils'
|
|
3
|
+
import type { AnyRouter } from './router'
|
|
4
|
+
import type { ParsedLocation } from './location'
|
|
5
|
+
import type { NonNullableUpdater } from './utils'
|
|
6
|
+
|
|
7
|
+
export type ScrollRestorationEntry = { scrollX: number; scrollY: number }
|
|
8
|
+
|
|
9
|
+
type ScrollRestorationByElement = Record<string, ScrollRestorationEntry>
|
|
10
|
+
|
|
11
|
+
type ScrollRestorationByKey = Record<string, ScrollRestorationByElement>
|
|
12
|
+
|
|
13
|
+
type ScrollRestorationCache = {
|
|
14
|
+
readonly state: ScrollRestorationByKey
|
|
15
|
+
set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void
|
|
16
|
+
persist: () => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ScrollRestorationOptions = {
|
|
20
|
+
getKey?: (location: ParsedLocation) => string
|
|
21
|
+
scrollBehavior?: ScrollToOptions['behavior']
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getSafeSessionStorage() {
|
|
25
|
+
try {
|
|
26
|
+
return typeof window !== 'undefined' &&
|
|
27
|
+
typeof window.sessionStorage === 'object'
|
|
28
|
+
? window.sessionStorage
|
|
29
|
+
: undefined
|
|
30
|
+
} catch {
|
|
31
|
+
// silent
|
|
32
|
+
return undefined
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// SessionStorage key used to store scroll positions across navigations.
|
|
37
|
+
export const storageKey = 'tsr-scroll-restoration-v1_3'
|
|
38
|
+
|
|
39
|
+
function createScrollRestorationCache(): ScrollRestorationCache | null {
|
|
40
|
+
const safeSessionStorage = getSafeSessionStorage()
|
|
41
|
+
if (!safeSessionStorage) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let state: ScrollRestorationByKey = {}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(safeSessionStorage.getItem(storageKey) || '{}')
|
|
49
|
+
if (isPlainObject(parsed)) {
|
|
50
|
+
state = parsed as ScrollRestorationByKey
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
// ignore invalid session storage payloads
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const persist = () => {
|
|
57
|
+
try {
|
|
58
|
+
safeSessionStorage.setItem(storageKey, JSON.stringify(state))
|
|
59
|
+
} catch {
|
|
60
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
61
|
+
console.warn(
|
|
62
|
+
'[ts-router] Could not persist scroll restoration state to sessionStorage.',
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
get state() {
|
|
70
|
+
return state
|
|
71
|
+
},
|
|
72
|
+
set: (updater) => {
|
|
73
|
+
state = functionalUpdate(updater, state) || state
|
|
74
|
+
},
|
|
75
|
+
persist,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const scrollRestorationCache = createScrollRestorationCache()
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The default `getKey` function for `useScrollRestoration`.
|
|
83
|
+
* It returns the `key` from the location state or the `href` of the location.
|
|
84
|
+
*
|
|
85
|
+
* The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
|
|
86
|
+
*/
|
|
87
|
+
export const defaultGetScrollRestorationKey = (location: ParsedLocation) => {
|
|
88
|
+
return location.state.__TSR_key! || location.href
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getCssSelector(el: any): string {
|
|
92
|
+
const path = []
|
|
93
|
+
let parent: HTMLElement
|
|
94
|
+
while ((parent = el.parentNode)) {
|
|
95
|
+
path.push(
|
|
96
|
+
`${el.tagName}:nth-child(${Array.prototype.indexOf.call(parent.children, el) + 1})`,
|
|
97
|
+
)
|
|
98
|
+
el = parent
|
|
99
|
+
}
|
|
100
|
+
return `${path.reverse().join(' > ')}`.toLowerCase()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getElementScrollRestorationEntry(
|
|
104
|
+
router: AnyRouter,
|
|
105
|
+
options: (
|
|
106
|
+
| {
|
|
107
|
+
id: string
|
|
108
|
+
getElement?: () => Window | Element | undefined | null
|
|
109
|
+
}
|
|
110
|
+
| {
|
|
111
|
+
id?: string
|
|
112
|
+
getElement: () => Window | Element | undefined | null
|
|
113
|
+
}
|
|
114
|
+
) & {
|
|
115
|
+
getKey?: (location: ParsedLocation) => string
|
|
116
|
+
},
|
|
117
|
+
): ScrollRestorationEntry | undefined {
|
|
118
|
+
const getKey = options.getKey || defaultGetScrollRestorationKey
|
|
119
|
+
const restoreKey = getKey(router.latestLocation)
|
|
120
|
+
|
|
121
|
+
if (options.id) {
|
|
122
|
+
return scrollRestorationCache?.state[restoreKey]?.[
|
|
123
|
+
`[${scrollRestorationIdAttribute}="${options.id}"]`
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const element = options.getElement?.()
|
|
128
|
+
if (!element) {
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return scrollRestorationCache?.state[restoreKey]?.[
|
|
133
|
+
element instanceof Window ? windowScrollTarget : getCssSelector(element)
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let ignoreScroll = false
|
|
138
|
+
const windowScrollTarget = 'window'
|
|
139
|
+
const scrollRestorationIdAttribute = 'data-scroll-restoration-id'
|
|
140
|
+
type ScrollTarget = typeof windowScrollTarget | Element
|
|
141
|
+
|
|
142
|
+
export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
143
|
+
if (!scrollRestorationCache && !(isServer ?? router.isServer)) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const cache = scrollRestorationCache
|
|
148
|
+
|
|
149
|
+
const shouldScrollRestoration =
|
|
150
|
+
force ?? router.options.scrollRestoration ?? false
|
|
151
|
+
|
|
152
|
+
if (shouldScrollRestoration) {
|
|
153
|
+
router.isScrollRestoring = true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
(isServer ?? router.isServer) ||
|
|
158
|
+
router.isScrollRestorationSetup ||
|
|
159
|
+
!cache
|
|
160
|
+
) {
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
router.isScrollRestorationSetup = true
|
|
165
|
+
ignoreScroll = false
|
|
166
|
+
|
|
167
|
+
const getKey =
|
|
168
|
+
router.options.getScrollRestorationKey || defaultGetScrollRestorationKey
|
|
169
|
+
const trackedScrollEntries = new Map<ScrollTarget, ScrollRestorationEntry>()
|
|
170
|
+
|
|
171
|
+
window.history.scrollRestoration = 'manual'
|
|
172
|
+
|
|
173
|
+
const onScroll = (event: Event) => {
|
|
174
|
+
if (ignoreScroll || !router.isScrollRestoring) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (event.target === document || event.target === window) {
|
|
179
|
+
trackedScrollEntries.set(windowScrollTarget, {
|
|
180
|
+
scrollX: window.scrollX || 0,
|
|
181
|
+
scrollY: window.scrollY || 0,
|
|
182
|
+
})
|
|
183
|
+
} else {
|
|
184
|
+
const target = event.target as Element
|
|
185
|
+
trackedScrollEntries.set(target, {
|
|
186
|
+
scrollX: target.scrollLeft || 0,
|
|
187
|
+
scrollY: target.scrollTop || 0,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Snapshot the current page's tracked scroll targets before navigation or unload.
|
|
193
|
+
const snapshotCurrentScrollTargets = (restoreKey?: string) => {
|
|
194
|
+
if (
|
|
195
|
+
!router.isScrollRestoring ||
|
|
196
|
+
!restoreKey ||
|
|
197
|
+
trackedScrollEntries.size === 0 ||
|
|
198
|
+
!cache
|
|
199
|
+
) {
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const keyEntry = (cache.state[restoreKey] ||=
|
|
204
|
+
{} as ScrollRestorationByElement)
|
|
205
|
+
|
|
206
|
+
for (const [target, position] of trackedScrollEntries) {
|
|
207
|
+
let selector: string | undefined
|
|
208
|
+
|
|
209
|
+
if (target === windowScrollTarget) {
|
|
210
|
+
selector = windowScrollTarget
|
|
211
|
+
} else if (target.isConnected) {
|
|
212
|
+
const attrId = target.getAttribute(scrollRestorationIdAttribute)
|
|
213
|
+
selector = attrId
|
|
214
|
+
? `[${scrollRestorationIdAttribute}="${attrId}"]`
|
|
215
|
+
: getCssSelector(target)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!selector) {
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
keyEntry[selector] = position
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
document.addEventListener('scroll', onScroll, true)
|
|
227
|
+
router.subscribe('onBeforeLoad', (event) => {
|
|
228
|
+
snapshotCurrentScrollTargets(
|
|
229
|
+
event.fromLocation ? getKey(event.fromLocation) : undefined,
|
|
230
|
+
)
|
|
231
|
+
trackedScrollEntries.clear()
|
|
232
|
+
})
|
|
233
|
+
window.addEventListener('pagehide', () => {
|
|
234
|
+
snapshotCurrentScrollTargets(
|
|
235
|
+
getKey(
|
|
236
|
+
router.stores.resolvedLocation.state ?? router.stores.location.state,
|
|
237
|
+
),
|
|
238
|
+
)
|
|
239
|
+
cache.persist()
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
// Restore destination scroll after the new route has rendered.
|
|
243
|
+
router.subscribe('onRendered', (event) => {
|
|
244
|
+
const cacheKey = getKey(event.toLocation)
|
|
245
|
+
const behavior = router.options.scrollRestorationBehavior
|
|
246
|
+
const scrollToTopSelectors = router.options.scrollToTopSelectors
|
|
247
|
+
trackedScrollEntries.clear()
|
|
248
|
+
|
|
249
|
+
if (!router.resetNextScroll) {
|
|
250
|
+
router.resetNextScroll = true
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (
|
|
255
|
+
typeof router.options.scrollRestoration === 'function' &&
|
|
256
|
+
!router.options.scrollRestoration({ location: router.latestLocation })
|
|
257
|
+
) {
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
ignoreScroll = true
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const elementEntries = router.isScrollRestoring
|
|
265
|
+
? cache.state[cacheKey]
|
|
266
|
+
: undefined
|
|
267
|
+
let restored = false
|
|
268
|
+
|
|
269
|
+
if (elementEntries) {
|
|
270
|
+
for (const elementSelector in elementEntries) {
|
|
271
|
+
const entry = elementEntries[elementSelector]
|
|
272
|
+
|
|
273
|
+
if (!isPlainObject(entry)) {
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const { scrollX, scrollY } = entry as {
|
|
278
|
+
scrollX?: unknown
|
|
279
|
+
scrollY?: unknown
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {
|
|
283
|
+
continue
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (elementSelector === windowScrollTarget) {
|
|
287
|
+
window.scrollTo({
|
|
288
|
+
top: scrollY as number,
|
|
289
|
+
left: scrollX as number,
|
|
290
|
+
behavior,
|
|
291
|
+
})
|
|
292
|
+
restored = true
|
|
293
|
+
} else if (elementSelector) {
|
|
294
|
+
let element
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
element = document.querySelector(elementSelector)
|
|
298
|
+
} catch {
|
|
299
|
+
continue
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (element) {
|
|
303
|
+
element.scrollLeft = scrollX as number
|
|
304
|
+
element.scrollTop = scrollY as number
|
|
305
|
+
restored = true
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!restored) {
|
|
312
|
+
const hash = router.history.location.hash.slice(1)
|
|
313
|
+
|
|
314
|
+
if (hash) {
|
|
315
|
+
const hashScrollIntoViewOptions =
|
|
316
|
+
window.history.state?.__hashScrollIntoViewOptions ?? true
|
|
317
|
+
|
|
318
|
+
if (hashScrollIntoViewOptions) {
|
|
319
|
+
const el = document.getElementById(hash)
|
|
320
|
+
if (el) {
|
|
321
|
+
el.scrollIntoView(hashScrollIntoViewOptions)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
const scrollOptions = {
|
|
326
|
+
top: 0,
|
|
327
|
+
left: 0,
|
|
328
|
+
behavior,
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
window.scrollTo(scrollOptions)
|
|
332
|
+
if (scrollToTopSelectors) {
|
|
333
|
+
for (const selector of scrollToTopSelectors) {
|
|
334
|
+
if (selector === windowScrollTarget) continue
|
|
335
|
+
const element =
|
|
336
|
+
typeof selector === 'function'
|
|
337
|
+
? selector()
|
|
338
|
+
: document.querySelector(selector)
|
|
339
|
+
if (element) {
|
|
340
|
+
element.scrollTo(scrollOptions)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} finally {
|
|
347
|
+
ignoreScroll = false
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (router.isScrollRestoring) {
|
|
351
|
+
cache.set((state) => {
|
|
352
|
+
state[cacheKey] ||= {} as ScrollRestorationByElement
|
|
353
|
+
return state
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { deepEqual } from './utils'
|
|
2
|
+
import type { NoInfer, PickOptional } from './utils'
|
|
3
|
+
import type { SearchMiddleware } from './route'
|
|
4
|
+
import type { IsRequiredParams } from './link'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Retain specified search params across navigations.
|
|
8
|
+
*
|
|
9
|
+
* If `keys` is `true`, retain all current params. Otherwise, copy only the
|
|
10
|
+
* listed keys from the current search into the next search.
|
|
11
|
+
*
|
|
12
|
+
* @param keys `true` to retain all, or a list of keys to retain.
|
|
13
|
+
* @returns A search middleware suitable for route `search.middlewares`.
|
|
14
|
+
* @link https://tanstack.com/router/latest/docs/framework/react/api/router/retainSearchParamsFunction
|
|
15
|
+
*/
|
|
16
|
+
export function retainSearchParams<TSearchSchema extends object>(
|
|
17
|
+
keys: Array<keyof TSearchSchema> | true,
|
|
18
|
+
): SearchMiddleware<TSearchSchema> {
|
|
19
|
+
return ({ search, next }) => {
|
|
20
|
+
const result = next(search)
|
|
21
|
+
if (keys === true) {
|
|
22
|
+
return { ...search, ...result }
|
|
23
|
+
}
|
|
24
|
+
const copy = { ...result }
|
|
25
|
+
// add missing keys from search to copy
|
|
26
|
+
keys.forEach((key) => {
|
|
27
|
+
if (!(key in copy)) {
|
|
28
|
+
copy[key] = search[key]
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
return copy
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Remove optional or default-valued search params from navigations.
|
|
37
|
+
*
|
|
38
|
+
* - Pass `true` (only if there are no required search params) to strip all.
|
|
39
|
+
* - Pass an array to always remove those optional keys.
|
|
40
|
+
* - Pass an object of default values; keys equal (deeply) to the defaults are removed.
|
|
41
|
+
*
|
|
42
|
+
* @returns A search middleware suitable for route `search.middlewares`.
|
|
43
|
+
* @link https://tanstack.com/router/latest/docs/framework/react/api/router/stripSearchParamsFunction
|
|
44
|
+
*/
|
|
45
|
+
export function stripSearchParams<
|
|
46
|
+
TSearchSchema,
|
|
47
|
+
TOptionalProps = PickOptional<NoInfer<TSearchSchema>>,
|
|
48
|
+
const TValues =
|
|
49
|
+
| Partial<NoInfer<TOptionalProps>>
|
|
50
|
+
| Array<keyof TOptionalProps>,
|
|
51
|
+
const TInput = IsRequiredParams<TSearchSchema> extends never
|
|
52
|
+
? TValues | true
|
|
53
|
+
: TValues,
|
|
54
|
+
>(input: NoInfer<TInput>): SearchMiddleware<TSearchSchema> {
|
|
55
|
+
return ({ search, next }) => {
|
|
56
|
+
if (input === true) {
|
|
57
|
+
return {}
|
|
58
|
+
}
|
|
59
|
+
const result = next(search) as Record<string, unknown>
|
|
60
|
+
const newResult = { ...result }
|
|
61
|
+
if (Array.isArray(input)) {
|
|
62
|
+
input.forEach((key) => {
|
|
63
|
+
delete newResult[key]
|
|
64
|
+
})
|
|
65
|
+
} else {
|
|
66
|
+
Object.entries(input as Record<string, unknown>).forEach(
|
|
67
|
+
([key, value]) => {
|
|
68
|
+
if (deepEqual(result[key], value)) {
|
|
69
|
+
delete newResult[key]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
return newResult as any
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { decode, encode } from './qss'
|
|
2
|
+
import type { AnySchema } from './validators'
|
|
3
|
+
|
|
4
|
+
/** Default `parseSearch` that strips leading '?' and JSON-parses values. */
|
|
5
|
+
export const defaultParseSearch = parseSearchWith(JSON.parse)
|
|
6
|
+
/** Default `stringifySearch` using JSON.stringify for complex values. */
|
|
7
|
+
export const defaultStringifySearch = stringifySearchWith(
|
|
8
|
+
JSON.stringify,
|
|
9
|
+
JSON.parse,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build a `parseSearch` function using a provided JSON-like parser.
|
|
14
|
+
*
|
|
15
|
+
* The returned function strips a leading `?`, decodes values, and attempts to
|
|
16
|
+
* JSON-parse string values using the given `parser`.
|
|
17
|
+
*
|
|
18
|
+
* @param parser Function to parse a string value (e.g. `JSON.parse`).
|
|
19
|
+
* @returns A `parseSearch` function compatible with `Router` options.
|
|
20
|
+
* @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization
|
|
21
|
+
*/
|
|
22
|
+
export function parseSearchWith(parser: (str: string) => any) {
|
|
23
|
+
return (searchStr: string): AnySchema => {
|
|
24
|
+
if (searchStr[0] === '?') {
|
|
25
|
+
searchStr = searchStr.substring(1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const query: Record<string, unknown> = decode(searchStr)
|
|
29
|
+
|
|
30
|
+
// Try to parse any query params that might be json
|
|
31
|
+
for (const key in query) {
|
|
32
|
+
const value = query[key]
|
|
33
|
+
if (typeof value === 'string') {
|
|
34
|
+
try {
|
|
35
|
+
query[key] = parser(value)
|
|
36
|
+
} catch (_err) {
|
|
37
|
+
// silent
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return query
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build a `stringifySearch` function using a provided serializer.
|
|
48
|
+
*
|
|
49
|
+
* Non-primitive values are serialized with `stringify`. If a `parser` is
|
|
50
|
+
* supplied, string values that are parseable are re-serialized to ensure
|
|
51
|
+
* symmetry with `parseSearch`.
|
|
52
|
+
*
|
|
53
|
+
* @param stringify Function to serialize a value (e.g. `JSON.stringify`).
|
|
54
|
+
* @param parser Optional parser to detect parseable strings.
|
|
55
|
+
* @returns A `stringifySearch` function compatible with `Router` options.
|
|
56
|
+
* @link https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization
|
|
57
|
+
*/
|
|
58
|
+
export function stringifySearchWith(
|
|
59
|
+
stringify: (search: any) => string,
|
|
60
|
+
parser?: (str: string) => any,
|
|
61
|
+
) {
|
|
62
|
+
const hasParser = typeof parser === 'function'
|
|
63
|
+
function stringifyValue(val: any) {
|
|
64
|
+
if (typeof val === 'object' && val !== null) {
|
|
65
|
+
try {
|
|
66
|
+
return stringify(val)
|
|
67
|
+
} catch (_err) {
|
|
68
|
+
// silent
|
|
69
|
+
}
|
|
70
|
+
} else if (hasParser && typeof val === 'string') {
|
|
71
|
+
try {
|
|
72
|
+
// Check if it's a valid parseable string.
|
|
73
|
+
// If it is, then stringify it again.
|
|
74
|
+
parser(val)
|
|
75
|
+
return stringify(val)
|
|
76
|
+
} catch (_err) {
|
|
77
|
+
// silent
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return val
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (search: Record<string, any>) => {
|
|
84
|
+
const searchStr = encode(search, stringifyValue)
|
|
85
|
+
return searchStr ? `?${searchStr}` : ''
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type SearchSerializer = (searchObj: Record<string, any>) => string
|
|
90
|
+
export type SearchParser = (searchStr: string) => Record<string, any>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { mergeHeaders } from './headers'
|
|
2
|
+
export { json } from './json'
|
|
3
|
+
export type { JsonResponse } from './json'
|
|
4
|
+
export { hydrate } from './ssr-client'
|
|
5
|
+
export * from './ssr-client'
|
|
6
|
+
export type { TsrSsrGlobal, DehydratedMatch, DehydratedRouter } from './types'
|