clear-react-router 1.0.16 → 1.0.18

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.
Files changed (2) hide show
  1. package/dist/index.js +402 -404
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,440 +1,438 @@
1
- import { Suspense as e, createContext as t, lazy as n, useCallback as r, useContext as i, useEffect as a, useMemo as o, useRef as s, useState as c } from "react";
1
+ import { Suspense, createContext, lazy, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
2
2
  //#region \0rolldown/runtime.js
3
- var l = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), u = /* @__PURE__ */ ((e) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(e, { get: (e, t) => (typeof require < "u" ? require : e)[t] }) : e)(function(e) {
4
- if (typeof require < "u") return require.apply(this, arguments);
5
- throw Error("Calling `require` for \"" + e + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
6
- }), d = t({}), f = t({}), p = t({}), m = /* @__PURE__ */ l(((e) => {
7
- var t = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
8
- function r(e, n, r) {
9
- var i = null;
10
- if (r !== void 0 && (i = "" + r), n.key !== void 0 && (i = "" + n.key), "key" in n) for (var a in r = {}, n) a !== "key" && (r[a] = n[a]);
11
- else r = n;
12
- return n = r.ref, {
13
- $$typeof: t,
14
- type: e,
15
- key: i,
16
- ref: n === void 0 ? null : n,
17
- props: r
3
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
4
+ //#endregion
5
+ //#region context/RouterContext.ts
6
+ var ActionsContext = createContext({});
7
+ var DataContext = createContext({});
8
+ var NavigationContext = createContext({});
9
+ //#endregion
10
+ //#region ../../node_modules/react/cjs/react-jsx-runtime.production.js
11
+ /**
12
+ * @license React
13
+ * react-jsx-runtime.production.js
14
+ *
15
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
16
+ *
17
+ * This source code is licensed under the MIT license found in the
18
+ * LICENSE file in the root directory of this source tree.
19
+ */
20
+ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((exports) => {
21
+ var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
22
+ function jsxProd(type, config, maybeKey) {
23
+ var key = null;
24
+ void 0 !== maybeKey && (key = "" + maybeKey);
25
+ void 0 !== config.key && (key = "" + config.key);
26
+ if ("key" in config) {
27
+ maybeKey = {};
28
+ for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]);
29
+ } else maybeKey = config;
30
+ config = maybeKey.ref;
31
+ return {
32
+ $$typeof: REACT_ELEMENT_TYPE,
33
+ type,
34
+ key,
35
+ ref: void 0 !== config ? config : null,
36
+ props: maybeKey
18
37
  };
19
38
  }
20
- e.Fragment = n, e.jsx = r, e.jsxs = r;
21
- })), h = /* @__PURE__ */ l(((e) => {
22
- process.env.NODE_ENV !== "production" && (function() {
23
- function t(e) {
24
- if (e == null) return null;
25
- if (typeof e == "function") return e.$$typeof === k ? null : e.displayName || e.name || null;
26
- if (typeof e == "string") return e;
27
- switch (e) {
28
- case v: return "Fragment";
29
- case b: return "Profiler";
30
- case y: return "StrictMode";
31
- case w: return "Suspense";
32
- case T: return "SuspenseList";
33
- case O: return "Activity";
34
- }
35
- if (typeof e == "object") switch (typeof e.tag == "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), e.$$typeof) {
36
- case _: return "Portal";
37
- case S: return e.displayName || "Context";
38
- case x: return (e._context.displayName || "Context") + ".Consumer";
39
- case C:
40
- var n = e.render;
41
- return e = e.displayName, e ||= (e = n.displayName || n.name || "", e === "" ? "ForwardRef" : "ForwardRef(" + e + ")"), e;
42
- case E: return n = e.displayName || null, n === null ? t(e.type) || "Memo" : n;
43
- case D:
44
- n = e._payload, e = e._init;
45
- try {
46
- return t(e(n));
47
- } catch {}
48
- }
49
- return null;
50
- }
51
- function n(e) {
52
- return "" + e;
53
- }
54
- function r(e) {
55
- try {
56
- n(e);
57
- var t = !1;
58
- } catch {
59
- t = !0;
60
- }
61
- if (t) {
62
- t = console;
63
- var r = t.error, i = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
64
- return r.call(t, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", i), n(e);
65
- }
66
- }
67
- function i(e) {
68
- if (e === v) return "<>";
69
- if (typeof e == "object" && e && e.$$typeof === D) return "<...>";
70
- try {
71
- var n = t(e);
72
- return n ? "<" + n + ">" : "<...>";
73
- } catch {
74
- return "<...>";
75
- }
76
- }
77
- function a() {
78
- var e = A.A;
79
- return e === null ? null : e.getOwner();
80
- }
81
- function o() {
82
- return Error("react-stack-top-frame");
83
- }
84
- function s(e) {
85
- if (j.call(e, "key")) {
86
- var t = Object.getOwnPropertyDescriptor(e, "key").get;
87
- if (t && t.isReactWarning) return !1;
88
- }
89
- return e.key !== void 0;
90
- }
91
- function c(e, t) {
92
- function n() {
93
- P || (P = !0, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", t));
94
- }
95
- n.isReactWarning = !0, Object.defineProperty(e, "key", {
96
- get: n,
97
- configurable: !0
98
- });
99
- }
100
- function l() {
101
- var e = t(this.type);
102
- return F[e] || (F[e] = !0, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")), e = this.props.ref, e === void 0 ? null : e;
103
- }
104
- function d(e, t, n, r, i, a) {
105
- var o = n.ref;
106
- return e = {
107
- $$typeof: g,
108
- type: e,
109
- key: t,
110
- props: n,
111
- _owner: r
112
- }, (o === void 0 ? null : o) === null ? Object.defineProperty(e, "ref", {
113
- enumerable: !1,
114
- value: null
115
- }) : Object.defineProperty(e, "ref", {
116
- enumerable: !1,
117
- get: l
118
- }), e._store = {}, Object.defineProperty(e._store, "validated", {
119
- configurable: !1,
120
- enumerable: !1,
121
- writable: !0,
122
- value: 0
123
- }), Object.defineProperty(e, "_debugInfo", {
124
- configurable: !1,
125
- enumerable: !1,
126
- writable: !0,
127
- value: null
128
- }), Object.defineProperty(e, "_debugStack", {
129
- configurable: !1,
130
- enumerable: !1,
131
- writable: !0,
132
- value: i
133
- }), Object.defineProperty(e, "_debugTask", {
134
- configurable: !1,
135
- enumerable: !1,
136
- writable: !0,
137
- value: a
138
- }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
139
- }
140
- function f(e, n, i, o, l, u) {
141
- var f = n.children;
142
- if (f !== void 0) if (o) if (M(f)) {
143
- for (o = 0; o < f.length; o++) p(f[o]);
144
- Object.freeze && Object.freeze(f);
145
- } else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
146
- else p(f);
147
- if (j.call(n, "key")) {
148
- f = t(e);
149
- var m = Object.keys(n).filter(function(e) {
150
- return e !== "key";
151
- });
152
- o = 0 < m.length ? "{key: someKey, " + m.join(": ..., ") + ": ...}" : "{key: someKey}", R[f + o] || (m = 0 < m.length ? "{" + m.join(": ..., ") + ": ...}" : "{}", console.error("A props object containing a \"key\" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />", o, f, m, f), R[f + o] = !0);
153
- }
154
- if (f = null, i !== void 0 && (r(i), f = "" + i), s(n) && (r(n.key), f = "" + n.key), "key" in n) for (var h in i = {}, n) h !== "key" && (i[h] = n[h]);
155
- else i = n;
156
- return f && c(i, typeof e == "function" ? e.displayName || e.name || "Unknown" : e), d(e, f, i, a(), l, u);
157
- }
158
- function p(e) {
159
- m(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e && e.$$typeof === D && (e._payload.status === "fulfilled" ? m(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
160
- }
161
- function m(e) {
162
- return typeof e == "object" && !!e && e.$$typeof === g;
163
- }
164
- var h = u("react"), g = Symbol.for("react.transitional.element"), _ = Symbol.for("react.portal"), v = Symbol.for("react.fragment"), y = Symbol.for("react.strict_mode"), b = Symbol.for("react.profiler"), x = Symbol.for("react.consumer"), S = Symbol.for("react.context"), C = Symbol.for("react.forward_ref"), w = Symbol.for("react.suspense"), T = Symbol.for("react.suspense_list"), E = Symbol.for("react.memo"), D = Symbol.for("react.lazy"), O = Symbol.for("react.activity"), k = Symbol.for("react.client.reference"), A = h.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, j = Object.prototype.hasOwnProperty, M = Array.isArray, N = console.createTask ? console.createTask : function() {
165
- return null;
166
- };
167
- h = { react_stack_bottom_frame: function(e) {
168
- return e();
169
- } };
170
- var P, F = {}, I = h.react_stack_bottom_frame.bind(h, o)(), L = N(i(o)), R = {};
171
- e.Fragment = v, e.jsx = function(e, t, n) {
172
- var r = 1e4 > A.recentlyCreatedOwnerStacks++;
173
- return f(e, t, n, !1, r ? Error("react-stack-top-frame") : I, r ? N(i(e)) : L);
174
- }, e.jsxs = function(e, t, n) {
175
- var r = 1e4 > A.recentlyCreatedOwnerStacks++;
176
- return f(e, t, n, !0, r ? Error("react-stack-top-frame") : I, r ? N(i(e)) : L);
177
- };
178
- })();
179
- })), g = (/* @__PURE__ */ l(((e, t) => {
180
- process.env.NODE_ENV === "production" ? t.exports = m() : t.exports = h();
181
- })))(), _ = ({ children: e, setContext: t, context: n, updateBlockedRoute: r, updateLocation: i, location: a, params: o, prefetchLoader: s, loaderCache: c, blockerState: l }) => /* @__PURE__ */ (0, g.jsx)(d.Provider, {
182
- value: {
183
- updateLocation: i,
184
- updateBlockedRoute: r,
185
- prefetchLoader: s,
186
- setContext: t
187
- },
188
- children: /* @__PURE__ */ (0, g.jsx)(f.Provider, {
39
+ exports.jsx = jsxProd;
40
+ }));
41
+ //#endregion
42
+ //#region provider/RouterProvider.tsx
43
+ var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
44
+ module.exports = require_react_jsx_runtime_production();
45
+ })))();
46
+ var RouterProvider = ({ children, setContext, context, updateBlockedRoute, updateLocation, location, params, prefetchLoader, loaderCache, blockerState }) => {
47
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionsContext.Provider, {
189
48
  value: {
190
- context: n,
191
- loaderCache: c
49
+ updateLocation,
50
+ updateBlockedRoute,
51
+ prefetchLoader,
52
+ setContext
192
53
  },
193
- children: /* @__PURE__ */ (0, g.jsx)(p.Provider, {
54
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataContext.Provider, {
194
55
  value: {
195
- blockerState: l,
196
- params: o,
197
- location: a
56
+ context,
57
+ loaderCache
198
58
  },
199
- children: e
59
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationContext.Provider, {
60
+ value: {
61
+ blockerState,
62
+ params,
63
+ location
64
+ },
65
+ children
66
+ })
200
67
  })
201
- })
202
- }), v = (t, r) => {
203
- let i = n(() => t().then((e) => ({ default: e.default || e })));
204
- return () => /* @__PURE__ */ (0, g.jsx)(e, {
205
- fallback: typeof r == "function" ? r() : r || null,
206
- children: /* @__PURE__ */ (0, g.jsx)(i, {})
207
68
  });
208
- }, y = (e) => typeof e.element == "function" && e.element.toString().includes("import("), b = (e, t = [], n = "") => {
209
- let r = e.path.match(/:[^/]+/g), i = e.path.replaceAll(/:[^/]+(\/|$)/g, "").split("/").filter(Boolean), a = e.path.split("/"), o = r ? [...t, ...r.map((e, t) => ({
210
- key: i[t],
211
- value: e.slice(1)
212
- }))] : t, s = o.length ? `${n}${a.slice(0, a.length - 1).join("/")}` : e.path, c = y(e) ? v(e.element, e.fallback) : e.element;
69
+ };
70
+ //#endregion
71
+ //#region utils/createLazyComponent.tsx
72
+ var createLazyComponent = (importFn, fallback) => {
73
+ const LazyComp = lazy(() => importFn().then((module) => ({ default: module.default || module })));
74
+ return () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Suspense, {
75
+ fallback: typeof fallback === "function" ? fallback() : fallback || null,
76
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LazyComp, {})
77
+ });
78
+ };
79
+ //#endregion
80
+ //#region utils/utils.ts
81
+ var isLazy = (el) => typeof el.element === "function" && el.element.toString().includes("import(");
82
+ var parseClientRouteItem = (el, parentParams = [], parentPath = "") => {
83
+ const currentParamsList = el.path.match(/:[^/]+/g);
84
+ const normalizedSplitPath = el.path.replaceAll(/:[^/]+(\/|$)/g, "").split("/").filter(Boolean);
85
+ const splitPath = el.path.split("/");
86
+ const currentParams = currentParamsList ? [...parentParams, ...currentParamsList.map((param, index) => ({
87
+ key: normalizedSplitPath[index],
88
+ value: param.slice(1)
89
+ }))] : parentParams;
90
+ const path = currentParams.length ? `${parentPath}${splitPath.slice(0, splitPath.length - 1).join("/")}` : el.path;
91
+ const resolvedElement = isLazy(el) ? createLazyComponent(el.element, el.fallback) : el.element;
213
92
  return [{
214
- ...e,
215
- path: s,
216
- params: o,
217
- element: c
218
- }, ...e.children?.flatMap((e) => b(e, o, s)) || []];
219
- }, x = (e) => e.flatMap((e) => b(e, [])), S = (e) => {
220
- let { pathname: t } = window.location, n = t.split("/");
221
- return (e || []).map((e) => ({
222
- index: n.findIndex((t) => t === e.key),
223
- value: e.value
224
- })).reduce((e, t) => ({
225
- ...e,
226
- [t.value]: n[t.index + 1]
93
+ ...el,
94
+ path,
95
+ params: currentParams,
96
+ element: resolvedElement
97
+ }, ...el.children?.flatMap((child) => parseClientRouteItem(child, currentParams, path)) || []];
98
+ };
99
+ var createRouter = (clientList) => clientList.flatMap((el) => parseClientRouteItem(el, []));
100
+ var getParamsObject = (params) => {
101
+ const { pathname } = window.location;
102
+ const split = pathname.split("/");
103
+ return (params || []).map((el) => ({
104
+ index: split.findIndex((item) => item === el.key),
105
+ value: el.value
106
+ })).reduce((acc, cur) => ({
107
+ ...acc,
108
+ [cur.value]: split[cur.index + 1]
227
109
  }), {});
228
- }, C = (e) => ({
229
- pathname: e.pathname,
230
- search: e.search
231
- }), w = (e, t) => {
232
- let n = e.path.split("/").filter(Boolean), r = e.params ? Object.keys(e.params).length : 0, i = t.split("/").filter(Boolean);
233
- return n.every((e, t) => e === i[t + +!!t]) && i.length === n.length + r;
234
- }, T = (e) => {
235
- let t = s(e);
236
- return a(() => {
237
- t.current = e;
238
- }, [e]), t;
239
- }, E = (e) => typeof e == "object" && !!e && e.cause === "redirect", D = ({ setLocation: e, routeList: t, context: n, revalidateCache: i }) => {
240
- let [l, u] = c({
110
+ };
111
+ var parseWindowLocation = (location) => ({
112
+ pathname: location.pathname,
113
+ search: location.search
114
+ });
115
+ var comparePaths = (el, pathname) => {
116
+ const splitElementPath = el.path.split("/").filter(Boolean);
117
+ const paramsLength = el.params ? Object.keys(el.params).length : 0;
118
+ const splitPathname = pathname.split("/").filter(Boolean);
119
+ return splitElementPath.every((item, index) => item === splitPathname[index + (index ? 1 : 0)]) && splitPathname.length === splitElementPath.length + paramsLength;
120
+ };
121
+ //#endregion
122
+ //#region hooks/useLatest.ts
123
+ var useLatest = (value) => {
124
+ const ref = useRef(value);
125
+ useEffect(() => {
126
+ ref.current = value;
127
+ }, [value]);
128
+ return ref;
129
+ };
130
+ //#endregion
131
+ //#region hooks/useHandleNavigation.ts
132
+ var isRedirect = (error) => typeof error === "object" && error !== null && error.cause === "redirect";
133
+ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache }) => {
134
+ const [blockedRoute, setBlockedRoute] = useState({
241
135
  from: "",
242
136
  to: ""
243
- }), d = s(""), f = T(r(async (r) => {
137
+ });
138
+ const prevPathname = useRef("");
139
+ const setNextLocationRef = useLatest(useCallback(async (nextLocation) => {
244
140
  try {
245
- let a = t.find((e) => w(e, r.pathname));
246
- console.log("before beforeLoad execution", a), a?.beforeLoad && await a?.beforeLoad(n), console.log("after beforeLoad execution", a), e(r), r.pathname !== window.location.pathname && (history.pushState(null, "", r.pathname), d.current = r.pathname), await i(a), a?.afterLoad && await a?.afterLoad(n);
247
- } catch (t) {
248
- console.log("redirect caught", t);
249
- let n = t;
250
- if (!E(n)) return n;
251
- console.log("redirect after type guard"), history.replaceState(null, "", n.url), e({
252
- pathname: n.url,
253
- search: n.search
141
+ const nextItem = routeList.find((el) => comparePaths(el, nextLocation.pathname));
142
+ if (nextItem?.beforeLoad) await nextItem?.beforeLoad(context);
143
+ setLocation(nextLocation);
144
+ if (nextLocation.pathname !== window.location.pathname) {
145
+ history.pushState(null, "", nextLocation.pathname);
146
+ prevPathname.current = nextLocation.pathname;
147
+ }
148
+ await revalidateCache(nextItem);
149
+ if (nextItem?.afterLoad) await nextItem?.afterLoad(context);
150
+ } catch (error) {
151
+ const redirect = error;
152
+ if (!isRedirect(redirect)) return redirect;
153
+ history.replaceState(null, "", redirect.url);
154
+ setLocation({
155
+ pathname: redirect.url,
156
+ search: redirect.search
254
157
  });
255
158
  }
256
159
  }, [
257
- n,
258
- i,
259
- t,
260
- e
261
- ])), p = r(({ type: e, payload: t = "" }) => u((n) => n.from === t && e === "charge" ? n : t && n.from !== t && e === "charge" ? {
262
- ...n,
263
- from: t
264
- } : e === "reset" ? {
265
- ...n,
266
- to: ""
267
- } : (e === "process" && f.current({ pathname: n.to }), !n.from && !n.to ? n : {
268
- from: "",
269
- to: ""
270
- })), [f]), m = r(async (e) => {
271
- l.from ? u((t) => ({
272
- ...t,
273
- to: e.pathname
274
- })) : await f.current(e);
275
- }, [l.from, f]);
276
- return a(() => {
277
- let e = async (e) => {
278
- let t = C(e.target.location);
279
- d.current === l.from ? (u({
280
- from: d.current,
281
- to: t.pathname
282
- }), history.replaceState(null, "", d.current)) : f.current(t);
160
+ context,
161
+ revalidateCache,
162
+ routeList,
163
+ setLocation
164
+ ]));
165
+ const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
166
+ if (prevState.from === payload && type === "charge") return prevState;
167
+ if (payload && prevState.from !== payload && type === "charge") return {
168
+ ...prevState,
169
+ from: payload
283
170
  };
284
- return window.addEventListener("popstate", e), () => window.removeEventListener("popstate", e);
285
- }, [l.from, f]), a(() => {
286
- let e = C(window.location);
287
- f.current(e), d.current = e.pathname;
288
- }, [f]), {
289
- blockerState: o(() => l.from && l.to ? "blocked" : l.from ? "charged" : "unblocked", [l]),
290
- updateLocation: m,
291
- updateBlockedRoute: p
292
- };
293
- }, O = (e) => {
294
- let [t, n] = c({}), [i, a] = c(!1), [o, l] = c({}), u = s({}), d = r(({ key: e, value: t }) => n((n) => ({
295
- ...n,
296
- [e]: t
297
- })), []), f = r((e) => {
298
- if (!e) return !0;
299
- let t = u.current[e.path];
300
- return !!(t && Date.now() - t < (e.staleTime || 0));
301
- }, []), p = r(async (e) => {
302
- if (e?.loader && !f(e)) {
303
- l((t) => ({
304
- ...t,
305
- [e.path]: !0
306
- })), n((t) => Object.keys(t).filter((t) => t !== e.path).reduce((e, n) => ({
307
- ...e,
308
- [n]: t[n]
309
- }), {}));
310
- try {
311
- a(!1);
312
- let t = await e?.loader();
313
- u.current = {
314
- ...u.current,
315
- [e.path]: Date.now()
316
- }, d({
317
- key: e.path,
318
- value: t
171
+ if (type === "reset") return {
172
+ ...prevState,
173
+ to: ""
174
+ };
175
+ if (type === "process") setNextLocationRef.current({ pathname: prevState.to });
176
+ if (!prevState.from && !prevState.to) return prevState;
177
+ return {
178
+ from: "",
179
+ to: ""
180
+ };
181
+ }), [setNextLocationRef]);
182
+ const updateLocation = useCallback(async (nextLocation) => {
183
+ if (blockedRoute.from) setBlockedRoute((prevState) => ({
184
+ ...prevState,
185
+ to: nextLocation.pathname
186
+ }));
187
+ else await setNextLocationRef.current(nextLocation);
188
+ }, [blockedRoute.from, setNextLocationRef]);
189
+ useEffect(() => {
190
+ const handler = async (event) => {
191
+ const newLocation = parseWindowLocation(event.target.location);
192
+ if (prevPathname.current === blockedRoute.from) {
193
+ setBlockedRoute({
194
+ from: prevPathname.current,
195
+ to: newLocation.pathname
319
196
  });
320
- } catch {
321
- a(!0);
322
- } finally {
323
- l((t) => ({
324
- ...t,
325
- [e.path]: !1
326
- }));
327
- }
197
+ history.replaceState(null, "", prevPathname.current);
198
+ } else setNextLocationRef.current(newLocation);
199
+ };
200
+ window.addEventListener("popstate", handler);
201
+ return () => window.removeEventListener("popstate", handler);
202
+ }, [blockedRoute.from, setNextLocationRef]);
203
+ useEffect(() => {
204
+ const currentLocation = parseWindowLocation(window.location);
205
+ setNextLocationRef.current(currentLocation);
206
+ prevPathname.current = currentLocation.pathname;
207
+ }, [setNextLocationRef]);
208
+ return {
209
+ blockerState: useMemo(() => {
210
+ if (blockedRoute.from && blockedRoute.to) return "blocked";
211
+ if (blockedRoute.from) return "charged";
212
+ return "unblocked";
213
+ }, [blockedRoute]),
214
+ updateLocation,
215
+ updateBlockedRoute
216
+ };
217
+ };
218
+ //#endregion
219
+ //#region hooks/useLoader.ts
220
+ var useLoader = (routeList) => {
221
+ const [loaderCache, setLoaderCache] = useState({});
222
+ const [loaderError, setLoaderError] = useState(false);
223
+ const [isLoadingMap, setIsLoadingMap] = useState({});
224
+ const cacheTimestampsRef = useRef({});
225
+ const updateCache = useCallback(({ key, value }) => setLoaderCache((prevState) => ({
226
+ ...prevState,
227
+ [key]: value
228
+ })), []);
229
+ const isCacheItemFresh = useCallback((routeItem) => {
230
+ if (!routeItem) return true;
231
+ const currentCacheTimestamp = cacheTimestampsRef.current[routeItem.path];
232
+ return Boolean(currentCacheTimestamp && Date.now() - currentCacheTimestamp < (routeItem.staleTime || 0));
233
+ }, []);
234
+ const revalidateCache = useCallback(async (routeItem) => {
235
+ if (!routeItem?.loader) return;
236
+ if (isCacheItemFresh(routeItem)) return;
237
+ setIsLoadingMap((prev) => ({
238
+ ...prev,
239
+ [routeItem.path]: true
240
+ }));
241
+ setLoaderCache((prevState) => Object.keys(prevState).filter((el) => el !== routeItem.path).reduce((acc, cur) => ({
242
+ ...acc,
243
+ [cur]: prevState[cur]
244
+ }), {}));
245
+ try {
246
+ setLoaderError(false);
247
+ const result = await routeItem?.loader();
248
+ cacheTimestampsRef.current = {
249
+ ...cacheTimestampsRef.current,
250
+ [routeItem.path]: Date.now()
251
+ };
252
+ updateCache({
253
+ key: routeItem.path,
254
+ value: result
255
+ });
256
+ } catch {
257
+ setLoaderError(true);
258
+ } finally {
259
+ setIsLoadingMap((prev) => ({
260
+ ...prev,
261
+ [routeItem.path]: false
262
+ }));
328
263
  }
329
- }, [f, d]), m = r(async (t) => {
330
- let n = e.find((e) => w(e, t));
331
- n && await p(n);
332
- }, [p, e]);
264
+ }, [isCacheItemFresh, updateCache]);
265
+ const prefetchLoader = useCallback(async (pathname) => {
266
+ const item = routeList.find((el) => comparePaths(el, pathname));
267
+ if (item) await revalidateCache(item);
268
+ }, [revalidateCache, routeList]);
333
269
  return {
334
- loaderCache: t[window.location.pathname],
335
- loaderError: i,
336
- prefetchLoader: m,
337
- revalidateCache: p,
338
- isLoading: o[window.location.pathname]
270
+ loaderCache: loaderCache[window.location.pathname],
271
+ loaderError,
272
+ prefetchLoader,
273
+ revalidateCache,
274
+ isLoading: isLoadingMap[window.location.pathname]
339
275
  };
340
- }, k = (e) => e ? typeof e == "function" ? /* @__PURE__ */ (0, g.jsx)(e, {}) : e : null, A = "error 404. Page not found", j = "*", M = ({ routeList: e, context: t = {} }) => {
341
- let [n, r] = c(C(window.location)), [i, a] = c(t), s = o(() => e.find((e) => e.path === j || w(e, n.pathname)), [n.pathname, e]), { loaderError: l, loaderCache: u, prefetchLoader: d, revalidateCache: f, isLoading: p } = O(e), { blockerState: m, updateLocation: h, updateBlockedRoute: v } = D({
342
- setLocation: r,
343
- routeList: e,
344
- context: i,
345
- revalidateCache: f
346
- }), y = o(() => s?.params ? S(s.params) : {}, [s]), b = o(() => ({
347
- location: n,
348
- updateLocation: h,
349
- params: y,
350
- loaderCache: u,
351
- prefetchLoader: d,
352
- updateBlockedRoute: v,
353
- blockerState: m,
354
- context: i,
355
- setContext: a
276
+ };
277
+ //#endregion
278
+ //#region utils/renderElement.tsx
279
+ var renderElement = (Component) => {
280
+ if (!Component) return null;
281
+ return typeof Component === "function" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {}) : Component;
282
+ };
283
+ //#endregion
284
+ //#region components/Router.tsx
285
+ var PAGE_NOT_FOUND = "error 404. Page not found";
286
+ var ALL_LOCATIONS = "*";
287
+ var Router = ({ routeList, context: initialContext = {} }) => {
288
+ const [location, setLocation] = useState(parseWindowLocation(window.location));
289
+ const [context, setContext] = useState(initialContext);
290
+ const routeItem = useMemo(() => routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, location.pathname)), [location.pathname, routeList]);
291
+ const { loaderError, loaderCache, prefetchLoader, revalidateCache, isLoading } = useLoader(routeList);
292
+ const { blockerState, updateLocation, updateBlockedRoute } = useHandleNavigation({
293
+ setLocation,
294
+ routeList,
295
+ context,
296
+ revalidateCache
297
+ });
298
+ const params = useMemo(() => routeItem?.params ? getParamsObject(routeItem.params) : {}, [routeItem]);
299
+ const providerProps = useMemo(() => ({
300
+ location,
301
+ updateLocation,
302
+ params,
303
+ loaderCache,
304
+ prefetchLoader,
305
+ updateBlockedRoute,
306
+ blockerState,
307
+ context,
308
+ setContext
356
309
  }), [
357
- m,
358
- u,
359
- n,
360
- y,
361
- d,
362
- i,
363
- v,
364
- h
310
+ blockerState,
311
+ loaderCache,
312
+ location,
313
+ params,
314
+ prefetchLoader,
315
+ context,
316
+ updateBlockedRoute,
317
+ updateLocation
365
318
  ]);
366
- return s?.loader && !l && p ? /* @__PURE__ */ (0, g.jsx)(_, {
367
- ...b,
368
- children: k(s?.loaderFallback)
369
- }) : l ? /* @__PURE__ */ (0, g.jsx)(_, {
370
- ...b,
371
- children: k(s?.errorElement)
372
- }) : /* @__PURE__ */ (0, g.jsx)(_, {
373
- ...b,
374
- children: k(s?.element) || A
319
+ if (routeItem?.loader && !loaderError && isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, {
320
+ ...providerProps,
321
+ children: renderElement(routeItem?.loaderFallback)
375
322
  });
376
- }, N = () => {
377
- let e = i(p);
378
- if (!Object.keys(e).length) throw Error("useNavigationState must be used within Router component");
379
- return e;
380
- }, P = () => {
381
- let e = i(d);
382
- if (!Object.keys(e).length) throw Error("useRouterActions must be used within Router component");
383
- return e;
384
- }, F = () => {
385
- let e = i(f);
386
- if (!Object.keys(e).length) throw Error("useRouterData must be used within Router component");
387
- return e;
388
- }, I = () => {
389
- let { updateLocation: e } = P();
390
- return r(async (t) => t === -1 ? history.go(-1) : await e(t), [e]);
391
- }, L = ({ children: e, to: t, prefetch: n = !0 }) => {
392
- let { prefetchLoader: r } = P(), i = I();
393
- return /* @__PURE__ */ (0, g.jsx)("a", {
323
+ if (loaderError) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, {
324
+ ...providerProps,
325
+ children: renderElement(routeItem?.errorElement)
326
+ });
327
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, {
328
+ ...providerProps,
329
+ children: renderElement(routeItem?.element) || PAGE_NOT_FOUND
330
+ });
331
+ };
332
+ //#endregion
333
+ //#region hooks/useServiceContext.ts
334
+ var useNavigationState = () => {
335
+ const context = useContext(NavigationContext);
336
+ if (!Object.keys(context).length) throw new Error("useNavigationState must be used within Router component");
337
+ return context;
338
+ };
339
+ var useRouterActions = () => {
340
+ const context = useContext(ActionsContext);
341
+ if (!Object.keys(context).length) throw new Error("useRouterActions must be used within Router component");
342
+ return context;
343
+ };
344
+ var useRouterData = () => {
345
+ const context = useContext(DataContext);
346
+ if (!Object.keys(context).length) throw new Error("useRouterData must be used within Router component");
347
+ return context;
348
+ };
349
+ //#endregion
350
+ //#region hooks/useNavigate.ts
351
+ var useNavigate = () => {
352
+ const { updateLocation } = useRouterActions();
353
+ return useCallback(async (arg) => arg === -1 ? history.go(-1) : await updateLocation(arg), [updateLocation]);
354
+ };
355
+ //#endregion
356
+ //#region components/Link.tsx
357
+ var Link = ({ children, to, prefetch = true }) => {
358
+ const { prefetchLoader } = useRouterActions();
359
+ const navigate = useNavigate();
360
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
394
361
  style: { cursor: "pointer" },
395
- onClick: () => i({ pathname: t }),
396
- onMouseOver: () => n && r(t),
397
- children: e
362
+ onClick: () => navigate({ pathname: to }),
363
+ onMouseOver: () => prefetch && prefetchLoader(to),
364
+ children
398
365
  });
399
- }, R = () => {
400
- let { params: e } = N();
401
- return e;
402
- }, z = () => N().location, B = () => {
403
- let { loaderCache: e } = F();
404
- return e;
405
- }, V = (e) => {
406
- let { location: { pathname: t }, blockerState: n } = N(), { updateBlockedRoute: r } = P(), i = e();
407
- return a(() => r(i ? {
366
+ };
367
+ //#endregion
368
+ //#region hooks/useParams.ts
369
+ var useParams = () => {
370
+ const { params } = useNavigationState();
371
+ return params;
372
+ };
373
+ //#endregion
374
+ //#region hooks/useLocation.ts
375
+ var useLocation = () => {
376
+ return useNavigationState().location;
377
+ };
378
+ //#endregion
379
+ //#region hooks/useLoaderState.ts
380
+ var useLoaderState = () => {
381
+ const { loaderCache } = useRouterData();
382
+ return loaderCache;
383
+ };
384
+ //#endregion
385
+ //#region hooks/useBlocker.ts
386
+ var useBlocker = (blockerFn) => {
387
+ const { location: { pathname }, blockerState } = useNavigationState();
388
+ const { updateBlockedRoute } = useRouterActions();
389
+ const shouldBlock = blockerFn();
390
+ useEffect(() => updateBlockedRoute(shouldBlock ? {
408
391
  type: "charge",
409
- payload: t
392
+ payload: pathname
410
393
  } : { type: "unblock" }), [
411
- i,
412
- t,
413
- r
414
- ]), {
415
- state: n,
416
- process: () => r({ type: "process" }),
417
- reset: () => r({ type: "reset" })
394
+ shouldBlock,
395
+ pathname,
396
+ updateBlockedRoute
397
+ ]);
398
+ return {
399
+ state: blockerState,
400
+ process: () => updateBlockedRoute({ type: "process" }),
401
+ reset: () => updateBlockedRoute({ type: "reset" })
418
402
  };
419
- }, H = (e) => {
420
- a(() => {
421
- let t = (t) => {
422
- e && (t.preventDefault(), e());
403
+ };
404
+ //#endregion
405
+ //#region hooks/useBeforeUnload.ts
406
+ var useBeforeUnload = (callback) => {
407
+ useEffect(() => {
408
+ const handler = (event) => {
409
+ if (!callback) return;
410
+ event.preventDefault();
411
+ callback();
423
412
  };
424
- return window.addEventListener("beforeunload", t), () => window.removeEventListener("beforeunload", t);
425
- }, [e]);
426
- }, U = () => {
427
- let { context: e } = F(), { setContext: t } = P();
413
+ window.addEventListener("beforeunload", handler);
414
+ return () => window.removeEventListener("beforeunload", handler);
415
+ }, [callback]);
416
+ };
417
+ //#endregion
418
+ //#region hooks/useRouterContext.ts
419
+ var useRouterContext = () => {
420
+ const { context } = useRouterData();
421
+ const { setContext } = useRouterActions();
428
422
  return {
429
- context: e,
430
- setContext: t
423
+ context,
424
+ setContext
431
425
  };
432
- }, W = (e, t) => {
433
- let n = /* @__PURE__ */ Error();
434
- throw n.cause = "redirect", Object.assign(n, {
435
- url: e,
436
- search: t || ""
426
+ };
427
+ //#endregion
428
+ //#region utils/redirect.ts
429
+ var redirect = (url, search) => {
430
+ const error = /* @__PURE__ */ new Error();
431
+ error.cause = "redirect";
432
+ throw Object.assign(error, {
433
+ url,
434
+ search: search || ""
437
435
  });
438
436
  };
439
437
  //#endregion
440
- export { L as Link, M as Router, x as createRouter, W as redirect, H as useBeforeUnload, V as useBlocker, B as useLoaderState, z as useLocation, I as useNavigate, R as useParams, U as useRouterContext };
438
+ export { Link, Router, createRouter, redirect, useBeforeUnload, useBlocker, useLoaderState, useLocation, useNavigate, useParams, useRouterContext };
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "clear-react-router",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "A lightweight, type-safe routing library for React applications",
5
5
  "author": "Andrew Bubnov",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
6
  "scripts": {
10
7
  "build": "vite build && tsc --emitDeclarationOnly"
11
8
  },
@@ -31,6 +28,9 @@
31
28
  ],
32
29
  "license": "MIT",
33
30
  "type": "module",
31
+ "main": "./dist/index.js",
32
+ "module": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
34
  "exports": {
35
35
  ".": {
36
36
  "types": "./dist/index.d.ts",