clear-react-router 1.9.7 → 2.0.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 +6 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +40 -27
- package/dist/types.d.ts +8 -3
- package/dist/utils/isCacheItemFresh.d.ts +2 -5
- package/dist/utils/lazy.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -572,6 +572,8 @@ const UserProfile = () => {
|
|
|
572
572
|
staleTime: 60000, // 1 minute — cache is fresh for 60 seconds
|
|
573
573
|
}
|
|
574
574
|
```
|
|
575
|
+
- Stale cache entries are cleaned up incrementally on every navigation, keeping the loader cache from growing unbounded over long sessions.
|
|
576
|
+
|
|
575
577
|
|
|
576
578
|
### `useInvalidate()`
|
|
577
579
|
|
|
@@ -773,11 +775,13 @@ function ProductFilter() {
|
|
|
773
775
|
|
|
774
776
|
## Lazy Loading
|
|
775
777
|
|
|
776
|
-
Clear Router supports code-splitting out of the box. Simply
|
|
778
|
+
Clear Router supports code-splitting out of the box. Simply wrap dynamic import into a library's `lazy` function:
|
|
777
779
|
```tsx
|
|
780
|
+
import { lazy } from 'clear-react-router';
|
|
781
|
+
|
|
778
782
|
{
|
|
779
783
|
path: '/heavy-page',
|
|
780
|
-
element: () => import('./pages/HeavyComponent'),
|
|
784
|
+
element: lazy(() => import('./pages/HeavyComponent')),
|
|
781
785
|
fallback: () => <div>Loading...</div>,
|
|
782
786
|
}
|
|
783
787
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export { useAction } from './hooks/useAction';
|
|
|
11
11
|
export { useRouterContext } from './hooks/useRouterContext';
|
|
12
12
|
export { useSearchParams } from './hooks/useSearchParams';
|
|
13
13
|
export { useFormContext } from './hooks/useFormContext';
|
|
14
|
+
export { lazy } from './utils/lazy';
|
|
14
15
|
export { createRouter } from './utils/utils';
|
|
15
16
|
export type { RouteItem, BlockerState, Location, RouterProps, ElementProps } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Suspense, createContext, lazy, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
1
|
+
import { Suspense, createContext, lazy as lazy$1, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4
4
|
//#endregion
|
|
@@ -113,13 +113,12 @@ var createCommitNavigation = (navigationExecutor, routeItemDataState) => (nextLo
|
|
|
113
113
|
};
|
|
114
114
|
//#endregion
|
|
115
115
|
//#region utils/isCacheItemFresh.ts
|
|
116
|
-
var createIsCacheItemFresh = (loaderMap) => (
|
|
117
|
-
|
|
118
|
-
const item = loaderMap.get(pathname);
|
|
116
|
+
var createIsCacheItemFresh = (loaderMap) => (pathname, search) => {
|
|
117
|
+
const item = loaderMap.get(`${pathname}${search}`);
|
|
119
118
|
if (item === void 0) return false;
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
return Date.now() - item.timestamp <=
|
|
119
|
+
const resolvedStaleTime = item.staleTime ?? routerConfig.defaultStaleTime;
|
|
120
|
+
if (resolvedStaleTime === void 0) return true;
|
|
121
|
+
return Date.now() - item.timestamp <= resolvedStaleTime;
|
|
123
122
|
};
|
|
124
123
|
//#endregion
|
|
125
124
|
//#region ../../node_modules/react/cjs/react-jsx-runtime.production.js
|
|
@@ -162,7 +161,7 @@ var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
162
161
|
})))();
|
|
163
162
|
var createLazyComponent = (importFn, fallback) => {
|
|
164
163
|
const load = () => importFn().then((module) => ({ default: module.default || module }));
|
|
165
|
-
const LazyComp = lazy(load);
|
|
164
|
+
const LazyComp = lazy$1(load);
|
|
166
165
|
const Component = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Suspense, {
|
|
167
166
|
fallback: typeof fallback === "function" ? fallback() : fallback || null,
|
|
168
167
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LazyComp, {})
|
|
@@ -173,12 +172,15 @@ var createLazyComponent = (importFn, fallback) => {
|
|
|
173
172
|
};
|
|
174
173
|
};
|
|
175
174
|
//#endregion
|
|
175
|
+
//#region types.ts
|
|
176
|
+
var LAZY_MARKER = Symbol("clear-router-lazy");
|
|
177
|
+
//#endregion
|
|
176
178
|
//#region utils/utils.ts
|
|
177
|
-
var isLazy = (el) => typeof el.element === "
|
|
179
|
+
var isLazy = (el) => typeof el.element === "object" && el.element !== null && LAZY_MARKER in el.element;
|
|
178
180
|
var parseClientRouteItem = (el, parentPattern = "") => {
|
|
179
181
|
const pattern = `${parentPattern}/${el.path}`.replace(/\/+/g, "/");
|
|
180
|
-
const preloadElement = isLazy(el) ? createLazyComponent(el.element, el.fallback).preloadElement : void 0;
|
|
181
|
-
const resolvedElement = isLazy(el) ? createLazyComponent(el.element, el.fallback).Component : el.element;
|
|
182
|
+
const preloadElement = isLazy(el) ? createLazyComponent(el.element.importFn, el.fallback).preloadElement : void 0;
|
|
183
|
+
const resolvedElement = isLazy(el) ? createLazyComponent(el.element.importFn, el.fallback).Component : el.element;
|
|
182
184
|
return [{
|
|
183
185
|
...el,
|
|
184
186
|
pattern,
|
|
@@ -278,17 +280,14 @@ var createNavigate = (routerState, revalidateCache) => {
|
|
|
278
280
|
const currentLoaderState = loaderMap.get(location.pathname)?.state;
|
|
279
281
|
if (currentLoaderState) loaderStateRef.set(currentLoaderState);
|
|
280
282
|
} else {
|
|
281
|
-
const pendingShouldExist = routeItem?.loader && !isCacheItemFresh(
|
|
282
|
-
routeItem,
|
|
283
|
-
pathname: location.pathname
|
|
284
|
-
});
|
|
283
|
+
const pendingShouldExist = routeItem?.loader && !isCacheItemFresh(location.pathname, location.search);
|
|
285
284
|
pendingState.setState(pendingShouldExist ? {
|
|
286
285
|
routeItem,
|
|
287
286
|
location
|
|
288
287
|
} : void 0);
|
|
289
288
|
}
|
|
290
289
|
};
|
|
291
|
-
const afterEachLoad = (routeItem) => {
|
|
290
|
+
const afterEachLoad = (routeItem, location) => {
|
|
292
291
|
if (!routeItem?.pollingInterval) return;
|
|
293
292
|
interval = window.setInterval(() => revalidateCache({
|
|
294
293
|
routeItem,
|
|
@@ -303,7 +302,7 @@ var createNavigate = (routerState, revalidateCache) => {
|
|
|
303
302
|
pathname: location.pathname,
|
|
304
303
|
search: location.search
|
|
305
304
|
});
|
|
306
|
-
afterEachLoad(routeItem);
|
|
305
|
+
afterEachLoad(routeItem, location);
|
|
307
306
|
};
|
|
308
307
|
const afterLoad = async (routeItem, params) => {
|
|
309
308
|
const { defaultAfterLoad } = routerConfig;
|
|
@@ -420,15 +419,20 @@ var getRetry = (routeItem) => {
|
|
|
420
419
|
var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
421
420
|
var createRevalidateCache = (routerState) => {
|
|
422
421
|
const { loaderStateRef, contextState, loaderMap } = routerState;
|
|
422
|
+
const removeFirstStaleItem = () => {
|
|
423
|
+
const deletedItem = [...loaderMap.entries()].find(([, item]) => {
|
|
424
|
+
const staleTime = item.staleTime ?? routerConfig.defaultStaleTime;
|
|
425
|
+
return staleTime && staleTime + item.timestamp < Date.now();
|
|
426
|
+
});
|
|
427
|
+
if (deletedItem) loaderMap.delete(deletedItem[0]);
|
|
428
|
+
};
|
|
423
429
|
const revalidateCache = async ({ routeItem, pathname, search = "" }, retried = 0) => {
|
|
424
430
|
if (!routeItem?.loader) return;
|
|
425
431
|
const isCacheItemFresh = createIsCacheItemFresh(loaderMap);
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
pathname
|
|
430
|
-
})) {
|
|
431
|
-
const item = loaderMap.get(pathname);
|
|
432
|
+
removeFirstStaleItem();
|
|
433
|
+
if (loadingPromises.has(pathname)) return loadingPromises.get(`${pathname}${search}`);
|
|
434
|
+
if (isCacheItemFresh(pathname, search)) {
|
|
435
|
+
const item = loaderMap.get(`${pathname}${search}`);
|
|
432
436
|
if (item?.state) loaderStateRef.set(item.state);
|
|
433
437
|
return;
|
|
434
438
|
}
|
|
@@ -452,7 +456,8 @@ var createRevalidateCache = (routerState) => {
|
|
|
452
456
|
}));
|
|
453
457
|
loaderMap.set(`${pathname}${search}`, {
|
|
454
458
|
state: loaderStateRef.value,
|
|
455
|
-
timestamp: Date.now()
|
|
459
|
+
timestamp: Date.now(),
|
|
460
|
+
staleTime: routeItem.staleTime
|
|
456
461
|
});
|
|
457
462
|
return {
|
|
458
463
|
data: result,
|
|
@@ -465,7 +470,8 @@ var createRevalidateCache = (routerState) => {
|
|
|
465
470
|
if (retry.delay) await sleep(retry.delay);
|
|
466
471
|
await revalidateCache({
|
|
467
472
|
routeItem,
|
|
468
|
-
pathname
|
|
473
|
+
pathname,
|
|
474
|
+
search
|
|
469
475
|
}, retried + 1);
|
|
470
476
|
return {
|
|
471
477
|
data: null,
|
|
@@ -709,7 +715,8 @@ var renderElement = (Component) => {
|
|
|
709
715
|
//#endregion
|
|
710
716
|
//#region components/Router.tsx
|
|
711
717
|
var EmptyBoundary = ({ children }) => children;
|
|
712
|
-
var
|
|
718
|
+
var IS_MOBILE = isMobile();
|
|
719
|
+
var Router = ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration, defaultLoaderFallback, defaultErrorElement, defaultRetry, defaultStaleTime, context: initialContext, isAnimated = false, spinner = true, defaultPreserveScroll = true, showFallbackOnAnimation = false, defaultPrefetch = IS_MOBILE ? "viewport" : "hover", defaultHoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary }) => {
|
|
713
720
|
const { useRouteItemData, usePendingState } = router.hooks;
|
|
714
721
|
const [routeItemData] = useRouteItemData();
|
|
715
722
|
const [pendingState] = usePendingState();
|
|
@@ -1006,4 +1013,10 @@ var useSearchParams = () => {
|
|
|
1006
1013
|
//#region hooks/useFormContext.ts
|
|
1007
1014
|
var useFormContext = () => useContext(FormContext);
|
|
1008
1015
|
//#endregion
|
|
1009
|
-
|
|
1016
|
+
//#region utils/lazy.ts
|
|
1017
|
+
var lazy = (importFn) => ({
|
|
1018
|
+
[LAZY_MARKER]: true,
|
|
1019
|
+
importFn
|
|
1020
|
+
});
|
|
1021
|
+
//#endregion
|
|
1022
|
+
export { Form, Link, Router, createRouter, lazy, useAction, useBlocker, useFormContext, useInvalidate, useLoaderState, useLocation, useNavigate, useParams, useRouterContext, useSearchParams };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { ComponentType, type CSSProperties, Dispatch, MouseEvent, ReactElement, ReactNode, Ref, SetStateAction } from 'react';
|
|
2
2
|
import { Store, useGlobalState } from './create';
|
|
3
3
|
import { Cell } from './cell';
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export declare const LAZY_MARKER: unique symbol;
|
|
5
|
+
export type LazyComponent = {
|
|
6
|
+
readonly [LAZY_MARKER]: true;
|
|
7
|
+
importFn: () => Promise<{
|
|
8
|
+
default: ComponentType<unknown>;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
7
11
|
export type RenderElement = (() => ReactElement) | ReactElement;
|
|
8
12
|
export type BeforeLoad = (arg: {
|
|
9
13
|
context: Record<string, unknown>;
|
|
@@ -98,6 +102,7 @@ export type RouterProps = {
|
|
|
98
102
|
export type LoaderStateItem = {
|
|
99
103
|
state: LoaderState;
|
|
100
104
|
timestamp: number;
|
|
105
|
+
staleTime: number | undefined;
|
|
101
106
|
};
|
|
102
107
|
export type RouterState = {
|
|
103
108
|
routeItemDataState: Store<RouteItemData>;
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
import { LoaderStateItem
|
|
2
|
-
export declare const createIsCacheItemFresh: (loaderMap: Map<string, LoaderStateItem>) => (
|
|
3
|
-
routeItem?: RouteItem;
|
|
4
|
-
pathname: string;
|
|
5
|
-
}) => boolean;
|
|
1
|
+
import { LoaderStateItem } from '../types';
|
|
2
|
+
export declare const createIsCacheItemFresh: (loaderMap: Map<string, LoaderStateItem>) => (pathname: string, search?: string) => boolean;
|