clear-react-router 1.8.2 → 1.8.4

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 CHANGED
@@ -70,7 +70,7 @@ It provides first-class support for:
70
70
 
71
71
  ### `createRouter(routes)`
72
72
 
73
- Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic params, builds nested paths.
73
+ Normalizes route configuration, extracts dynamic params, builds nested paths.
74
74
 
75
75
  | Property | Type | Description |
76
76
  |----------|------|-------------|
@@ -463,9 +463,7 @@ const UserProfile = () => {
463
463
 
464
464
  ### `useInvalidate()`
465
465
 
466
- Returns a function that revalidates **cached** route data by executing the route lifecycle again.
467
-
468
- Calling `invalidate()` clears the cached loader result and immediately runs both `beforeLoad` and `loader` for the specified route. This is useful after mutations or any operation that changes data used by the route.
466
+ Returns a function that revalidates **cached** route data. Calling `invalidate()` clears cached route data and immediately runs the corresponding route loader again.
469
467
 
470
468
  #### Current route
471
469
 
@@ -490,12 +488,12 @@ await invalidate('/posts');
490
488
  You can revalidate several routes at once by passing an array of pathnames:
491
489
 
492
490
  ```tsx
493
- await invalidate([ '/posts', '/profile', '/settings' ]);
491
+ await invalidate(['/posts', '/profile', '/settings']);
494
492
  ```
495
493
 
496
494
  #### Dynamic routes
497
495
 
498
- When a dynamic route pattern is provided, every cached route matching that pattern will be revalidated.
496
+ When a dynamic route pattern is provided, every cached route that matches the pattern will be revalidated.
499
497
 
500
498
  For example:
501
499
 
@@ -526,7 +524,7 @@ await invalidate('/posts', { withChildren: true });
526
524
  await invalidate(['/posts', '/users'], { withChildren: true });
527
525
  ```
528
526
 
529
- This will recursively revalidate cached routes inside the route tree.
527
+ This will recursively revalidate all cached child routes.
530
528
 
531
529
  For example, if the following routes have been visited:
532
530
 
@@ -550,17 +548,23 @@ will revalidate the cached child routes:
550
548
  /post/23
551
549
  /post/42/comments
552
550
  ```
551
+ #### Including `beforeLoad`
552
+
553
+ To include the `beforeLoad` function in the revalidation process, pass the `withBeforeLoad` option:
554
+
555
+ ```tsx
556
+ await invalidate('/posts', { withBeforeLoad: true });
557
+ await invalidate(['/posts', '/users'], { withBeforeLoad: true });
558
+ ```
559
+
553
560
  #### Notes
554
561
 
555
- * `invalidate()` re-executes both `beforeLoad` and `loader` for the invalidated route.
556
562
  * **Only routes that already have cached data are revalidated.**
557
- * Cached data is discarded before the new loader starts.
563
+ * Cached data is cleared before the new loader starts.
558
564
  * When used as an event handler, wrap the call in an arrow function:
559
565
 
560
566
  ```tsx
561
- <button onClick={() => invalidate()}>
562
- Refresh
563
- </button>
567
+ <button onClick={() => invalidate()}>Refresh</button>
564
568
  ```
565
569
 
566
570
  Passing `invalidate` directly (`onClick={invalidate}`) is not supported because React passes a `MouseEvent` object to event handlers.
package/dist/index.js CHANGED
@@ -174,55 +174,38 @@ var createLazyComponent = (importFn, fallback) => {
174
174
  //#endregion
175
175
  //#region utils/utils.ts
176
176
  var isLazy = (el) => typeof el.element === "function" && el.element.toString().includes("import(");
177
- var parseClientRouteItem = (el, parentParams = [], parentPath = "") => {
178
- const segments = el.path.split("/").filter(Boolean);
179
- const staticSegments = [];
180
- const currentParams = [...parentParams];
181
- let lastStaticSegment = "";
182
- for (const segment of segments) if (segment.startsWith(":")) {
183
- if (!lastStaticSegment) throw new Error(`Route "${el.path}" cannot start with a parameter.`);
184
- currentParams.push({
185
- key: lastStaticSegment,
186
- value: segment.slice(1)
187
- });
188
- } else {
189
- lastStaticSegment = segment;
190
- staticSegments.push(segment);
191
- }
192
- const path = `${parentPath}/${staticSegments.join("/")}`.replace(/\/+/g, "/");
177
+ var parseClientRouteItem = (el, parentPattern = "") => {
178
+ const pattern = `${parentPattern}/${el.path}`.replace(/\/+/g, "/");
193
179
  const resolvedElement = isLazy(el) ? createLazyComponent(el.element, el.fallback) : el.element;
194
180
  return [{
195
181
  ...el,
196
- path,
197
- params: currentParams,
182
+ pattern,
198
183
  element: resolvedElement
199
- }, ...el.children?.flatMap((child) => parseClientRouteItem(child, currentParams, path)) ?? []];
184
+ }, ...el.children?.flatMap((child) => parseClientRouteItem(child, pattern)) ?? []];
200
185
  };
201
- var createRouter = (clientList) => clientList.flatMap((el) => parseClientRouteItem(el, []));
202
- var getParamsObject = ({ params, pathname }) => {
203
- if (!params) return {};
204
- const split = pathname.split("/");
205
- return (params || []).map((el) => ({
206
- index: split.findIndex((item) => item === el.key),
207
- value: el.value
208
- })).reduce((acc, cur) => ({
209
- ...acc,
210
- [cur.value]: split[cur.index + 1]
211
- }), {});
186
+ var createRouter = (clientList) => clientList.flatMap((el) => parseClientRouteItem(el));
187
+ var getParamsObject = (nextItem, nextPathname) => {
188
+ const { routeItem: stateItem, location: { pathname: statePathname } } = router.state.routeItemDataState.getState();
189
+ const routeItem = nextItem || stateItem;
190
+ const pathname = nextPathname || statePathname;
191
+ if (!routeItem) return {};
192
+ const pathnameSegments = pathname.split("/");
193
+ return routeItem.pattern.split("/").reduce((acc, segment, index) => {
194
+ if (segment.startsWith(":")) acc[segment.slice(1)] = pathnameSegments[index];
195
+ return acc;
196
+ }, {});
212
197
  };
213
198
  var parseWindowLocation = (location) => ({
214
199
  pathname: location.pathname,
215
200
  search: location.search
216
201
  });
217
- var comparePaths = (el, pathname) => {
218
- if (!el.params?.length) return el.path.replaceAll("/", "") === pathname.replaceAll("/", "");
219
- const splitElementPath = el.path.split("/").filter(Boolean);
220
- const paramsLength = el.params ? Object.keys(el.params).length : 0;
221
- const splitPathname = pathname.split("/").filter(Boolean);
222
- return splitElementPath.every((item, index) => item === splitPathname[2 * index]) && splitPathname.length === splitElementPath.length + paramsLength;
202
+ var comparePaths = (route, pathname) => {
203
+ const pattern = route.pattern.split("/").filter(Boolean);
204
+ const current = pathname.split("/").filter(Boolean);
205
+ return pattern.length === current.length ? pattern.every((segment, index) => segment.startsWith(":") || segment === current[index]) : false;
223
206
  };
224
207
  var findRoute = (pathname, includeAll) => {
225
- if (includeAll) return routerConfig.routes.find((el) => el.path === "/*" || comparePaths(el, pathname));
208
+ if (includeAll) return routerConfig.routes.find((el) => el.path === "*" || comparePaths(el, pathname));
226
209
  return routerConfig.routes.find((el) => comparePaths(el, pathname));
227
210
  };
228
211
  //#endregion
@@ -241,10 +224,7 @@ var createNavigate = (routerState, revalidateCache) => {
241
224
  const nextItem = findRoute(location.pathname, true);
242
225
  return {
243
226
  nextItem,
244
- params: getParamsObject({
245
- params: nextItem?.params,
246
- pathname: location.pathname
247
- })
227
+ params: getParamsObject(nextItem, location.pathname)
248
228
  };
249
229
  };
250
230
  const beforeLoad = async (routeItem, params) => {
@@ -323,15 +303,12 @@ var createNavigate = (routerState, revalidateCache) => {
323
303
  //#region runtime/invalidate.ts
324
304
  var redirect = () => Promise.resolve();
325
305
  var createInvalidate = ({ routeItemDataState, loaderStateRef, timestampMap, currentLoaderState, contextState }, revalidateCache) => {
326
- const invalidatePath = async (routeItem, pathname) => {
306
+ const invalidatePath = async (routeItem, pathname, options) => {
327
307
  const routePathname = routeItemDataState.getState().location.pathname;
328
308
  timestampMap.delete(pathname);
329
- const params = getParamsObject({
330
- params: routeItem?.params,
331
- pathname
332
- });
309
+ const params = getParamsObject();
333
310
  try {
334
- if (routeItem?.beforeLoad) {
311
+ if (routeItem?.beforeLoad && options?.withBeforeLoad) {
335
312
  const context = contextState.getState();
336
313
  const setContext = contextState.setState;
337
314
  await routeItem.beforeLoad({
@@ -357,21 +334,21 @@ var createInvalidate = ({ routeItemDataState, loaderStateRef, timestampMap, curr
357
334
  });
358
335
  if (pathname === routePathname) currentLoaderState.setState(loaderStateRef.value);
359
336
  };
360
- const invalidateItem = async (pathname, withChildren) => {
337
+ const invalidateItem = async (pathname, options) => {
361
338
  const routeItem = findRoute(pathname);
362
339
  if (!routeItem) return;
363
340
  const pathnameArray = [];
364
341
  for (const [key] of timestampMap) if (comparePaths(routeItem, key)) pathnameArray.push(key);
365
- await Promise.all(pathnameArray.map((pathname) => invalidatePath(routeItem, pathname)));
366
- if (withChildren && routeItem.children?.length) {
342
+ await Promise.all(pathnameArray.map((pathname) => invalidatePath(routeItem, pathname, options)));
343
+ if (options?.withChildren && routeItem.children?.length) {
367
344
  const childPathList = routeItem.children.map((el) => el.path);
368
- await Promise.all(childPathList.map((el) => invalidateItem(`${pathname}${el}`, true)));
345
+ await Promise.all(childPathList.map((el) => invalidateItem(`${pathname}${el}`, options)));
369
346
  }
370
347
  };
371
348
  return async (pathList, options) => {
372
349
  const routePathname = routeItemDataState.getState().location.pathname;
373
350
  const pathnameList = Array.isArray(pathList) ? pathList : pathList ? [pathList] : [routePathname];
374
- await Promise.all(pathnameList.map((pathname) => invalidateItem(pathname, options?.withChildren)));
351
+ await Promise.all(pathnameList.map((pathname) => invalidateItem(pathname, options)));
375
352
  };
376
353
  };
377
354
  //#endregion
@@ -423,10 +400,7 @@ var createRevalidateCache = (routerState) => {
423
400
  try {
424
401
  const context = contextState.getState();
425
402
  const setContext = contextState.setState;
426
- const params = getParamsObject({
427
- params: routeItem.params,
428
- pathname
429
- });
403
+ const params = getParamsObject(routeItem, pathname);
430
404
  const result = await routeItem?.loader({
431
405
  params,
432
406
  context,
@@ -504,13 +478,10 @@ var createRouterInstance = () => {
504
478
  const invalidate = createInvalidate(routerState, revalidateCache);
505
479
  const prefetch = createPrefetch(revalidateCache);
506
480
  const useGetAction = (actionKey) => {
507
- const { routeItem, location } = routerState.routeItemDataState.getState();
481
+ const { routeItem } = routerState.routeItemDataState.getState();
508
482
  const context = routerState.contextState.getState();
509
483
  const setContext = routerState.contextState.setState;
510
- const params = getParamsObject({
511
- params: routeItem?.params,
512
- pathname: location.pathname
513
- });
484
+ const params = getParamsObject();
514
485
  if (!routeItem) throw new Error("Route not found");
515
486
  if (!routeItem.actions) throw new Error("Route action creator not found");
516
487
  const action = routeItem.actions({
@@ -549,13 +520,7 @@ var createRouterInstance = () => {
549
520
  useCurrentLoaderState: () => useGlobalState(routerState.currentLoaderState),
550
521
  useScrollMap: () => useGlobalState(routerState.scrollMapState),
551
522
  useContextState: () => useGlobalState(routerState.contextState),
552
- useParams: () => {
553
- const routeItemData = routerState.routeItemDataState.getState();
554
- return getParamsObject({
555
- params: routeItemData.routeItem?.params,
556
- pathname: routeItemData.location.pathname
557
- });
558
- },
523
+ useParams: () => getParamsObject(),
559
524
  useNavigate: () => {
560
525
  const { blockedRouteState } = routerState;
561
526
  const { location } = routerState.routeItemDataState.getState();
package/dist/types.d.ts CHANGED
@@ -40,10 +40,7 @@ export type ClientRouteItem = {
40
40
  };
41
41
  export type RouteItem = ClientRouteItem & {
42
42
  element: RenderElement;
43
- params?: {
44
- key: string;
45
- value: string;
46
- }[];
43
+ pattern: string;
47
44
  cacheTimestamp?: number;
48
45
  };
49
46
  export type Location = {
@@ -138,6 +135,7 @@ export type RouterType = {
138
135
  };
139
136
  export type InvalidateOptions = {
140
137
  withChildren?: boolean;
138
+ withBeforeLoad?: boolean;
141
139
  };
142
140
  export type RevalidateCache = ({ routeItem, pathname }: RevalidateCacheArgs) => Promise<unknown> | undefined;
143
141
  export type Options = Partial<{
@@ -1,2 +1,2 @@
1
- export declare const ALL_LOCATIONS = "/*";
1
+ export declare const NOT_FOUND = "*";
2
2
  export declare const findRoute: (pathname: string, includeAll?: boolean) => import("..").RouteItem | undefined;
@@ -1,8 +1,5 @@
1
1
  import { ClientRouteItem, Location, RouteItem } from '../types';
2
2
  export declare const createRouter: (clientList: ClientRouteItem[]) => RouteItem[];
3
- export declare const getParamsObject: ({ params, pathname }: {
4
- params?: RouteItem["params"];
5
- pathname: string;
6
- }) => {};
3
+ export declare const getParamsObject: (nextItem?: RouteItem, nextPathname?: string) => Record<string, string>;
7
4
  export declare const parseWindowLocation: (location: typeof window.location) => Location;
8
- export declare const comparePaths: (el: RouteItem, pathname: string) => boolean;
5
+ export declare const comparePaths: (route: RouteItem, pathname: string) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-react-router",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "A lightweight, type-safe routing library for React applications",
5
5
  "author": "Andrew Bubnov",
6
6
  "scripts": {