@mpen/rerouter 0.3.0 → 0.3.1
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 +4 -0
- package/{src → cli}/bin.test.ts +24 -2
- package/{src → cli}/bin.ts +27 -18
- package/cli/tsconfig.json +9 -0
- package/dist/acorn-k7ED_tOl.js +4968 -0
- package/dist/angular--Iqdw9UJ.js +4057 -0
- package/dist/babel-hfWAujRY.js +9878 -0
- package/dist/bin.d.ts +1 -1
- package/dist/bin.js +28 -23
- package/dist/estree-C1Zjnvlw.js +7266 -0
- package/dist/flow-BaD9LyIP.js +52912 -0
- package/dist/glimmer-CvCjW_1V.js +7541 -0
- package/dist/graphql-BdtzBuWh.js +1945 -0
- package/dist/html-DkZtUVbo.js +7137 -0
- package/dist/index.d.ts +19 -6
- package/dist/index.js +135 -27
- package/dist/markdown-Z8Vrc69e.js +6876 -0
- package/dist/meriyah-DeO4stuH.js +7590 -0
- package/dist/postcss-BmgGJ0E5.js +6777 -0
- package/dist/prettier-BT_F8kIx.js +15629 -0
- package/dist/typescript-DtIxStjy.js +22936 -0
- package/dist/yaml-CWOPBY0q.js +5281 -0
- package/examples/App.tsx +18 -49
- package/examples/dist/BlogPost-c10d9w2p.js +1 -0
- package/examples/dist/FetchLoading-534mdrgz.js +1 -0
- package/examples/dist/FetchLoading-sbxbdkre.js +1 -0
- package/examples/dist/Home-a1258p25.js +1 -0
- package/examples/dist/KitchenSink-821mjg0h.js +1 -0
- package/examples/dist/Login-wywx6bp7.js +1 -0
- package/examples/dist/Match-1e72jm5w.js +1 -0
- package/examples/dist/NotFound-smxj24jw.js +1 -0
- package/examples/dist/SlowLoading-59xxmbfk.js +1 -0
- package/examples/dist/index-0d4kj0rv.js +2 -0
- package/examples/dist/index-3x197sbt.js +9 -0
- package/examples/dist/index-a2hkfx1n.js +9 -0
- package/examples/dist/index-d21me1mc.js +9 -0
- package/examples/dist/index-ktqdknsn.js +2 -0
- package/examples/dist/index-p53qxxzd.js +2 -0
- package/examples/dist/index.html +67 -0
- package/examples/routes.gen.ts +66 -86
- package/examples/routes.ts +2 -2
- package/examples/server/serve-dist.ts +33 -0
- package/examples/server/tsconfig.json +9 -0
- package/package.json +11 -6
- package/src/components/Link.tsx +8 -6
- package/src/components/Router.test.tsx +183 -0
- package/src/components/Router.tsx +161 -29
- package/src/lib/routes.ts +2 -0
- package/tsconfig.json +3 -2
- package/tsdown.config.ts +3 -4
- package/dist/hooks-Dlwcb0sV.js +0 -20
- package/dist/hooks.d.ts +0 -2
- package/dist/hooks.js +0 -2
- package/dist/index-BYXpNitc.d.ts +0 -5
- /package/{src → cli}/fixtures/bin/kitchen-sink.tsx +0 -0
- /package/{src → cli}/fixtures/bin/optional.tsx +0 -0
- /package/{src → cli}/fixtures/bin/pages/Home.tsx +0 -0
- /package/{src → cli}/fixtures/bin/pages/KitchenSink.tsx +0 -0
- /package/{src → cli}/fixtures/bin/pages/Login.tsx +0 -0
- /package/{src → cli}/fixtures/bin/pages/Match.tsx +0 -0
- /package/{src → cli}/fixtures/bin/pages/NotFound.tsx +0 -0
- /package/{src → cli}/fixtures/bin/pages/Optional.tsx +0 -0
- /package/{src → cli}/fixtures/bin/regexp-groups.tsx +0 -0
- /package/{src → cli}/fixtures/bin/simple.tsx +0 -0
- /package/{src → cli}/fixtures/bin/unnamed.tsx +0 -0
- /package/dist/{routes-Hpf6cwcZ.js → routes-PW-bNm8e.js} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as useUrlSearchParams, t as useUrlPath } from "./index-BYXpNitc.js";
|
|
2
1
|
import { ClassValue } from "@mpen/classcat";
|
|
3
|
-
import * as _$
|
|
2
|
+
import * as _$react from "react";
|
|
4
3
|
import { ComponentType, ReactNode } from "react";
|
|
4
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
import { OverrideProps } from "@mpen/ts-types/react";
|
|
6
6
|
import { Override } from "@mpen/ts-types";
|
|
7
7
|
|
|
@@ -168,6 +168,7 @@ type RouteObject = {
|
|
|
168
168
|
pattern: string | URLPattern;
|
|
169
169
|
component: RouteComponentLoader<any>;
|
|
170
170
|
};
|
|
171
|
+
type Routes = RouteObject[];
|
|
171
172
|
/**
|
|
172
173
|
* Route definition consumed by [`Router`]{@link Router}.
|
|
173
174
|
*
|
|
@@ -237,6 +238,13 @@ interface RouterProps {
|
|
|
237
238
|
* Optional fallback rendered while a matched route component module is loading.
|
|
238
239
|
*/
|
|
239
240
|
loading?: ReactNode;
|
|
241
|
+
/**
|
|
242
|
+
* Delay before rendering [`RouterProps.loading`]{@link RouterProps#loading} for a suspended
|
|
243
|
+
* route, in milliseconds.
|
|
244
|
+
*
|
|
245
|
+
* @defaultValue `400`
|
|
246
|
+
*/
|
|
247
|
+
loadingDelayMs?: number;
|
|
240
248
|
}
|
|
241
249
|
/**
|
|
242
250
|
* Renders the first route that matches the current URL pathname.
|
|
@@ -249,17 +257,22 @@ interface RouterProps {
|
|
|
249
257
|
* import routes from './routes'
|
|
250
258
|
*
|
|
251
259
|
* function App() {
|
|
252
|
-
* return <Router routes={routes} loading={<div>Loading...</div>} />
|
|
260
|
+
* return <Router routes={routes} loading={<div>Loading...</div>} loadingDelayMs={400} />
|
|
253
261
|
* }
|
|
254
262
|
* ```
|
|
255
263
|
*/
|
|
256
264
|
declare function Router({
|
|
257
265
|
routes,
|
|
258
|
-
loading
|
|
259
|
-
|
|
266
|
+
loading,
|
|
267
|
+
loadingDelayMs
|
|
268
|
+
}: RouterProps): string | number | bigint | boolean | _$react_jsx_runtime0.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | _$react.ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
|
|
260
269
|
//#endregion
|
|
261
270
|
//#region src/lib/url.d.ts
|
|
262
271
|
declare function pushUrl(next: string, state?: unknown): void;
|
|
263
272
|
declare function replaceUrl(next: string, state?: unknown): void;
|
|
264
273
|
//#endregion
|
|
265
|
-
|
|
274
|
+
//#region src/hooks/useUrl.d.ts
|
|
275
|
+
declare function useUrlPath(): string;
|
|
276
|
+
declare function useUrlSearchParams(): URLSearchParams;
|
|
277
|
+
//#endregion
|
|
278
|
+
export { Link, LinkProps, NavLink, NavLinkMatch, NavLinkProps, NormalizedRoute, Route, RouteComponent, RouteComponentLoader, RouteComponentModule, RouteObject, RouteParams, Router, RouterProps, Routes, SearchParamsInit, normalizeLegacyPathToRegexpSyntax, normalizeRoutes, pushUrl, replaceUrl, useUrlPath, useUrlSearchParams };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { n as normalizeRoutes, t as normalizeLegacyPathToRegexpSyntax } from "./routes-Hpf6cwcZ.js";
|
|
1
|
+
import { n as normalizeRoutes, t as normalizeLegacyPathToRegexpSyntax } from "./routes-PW-bNm8e.js";
|
|
3
2
|
import { cc } from "@mpen/classcat";
|
|
3
|
+
import { Suspense, startTransition, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
|
-
import { Suspense, lazy, useMemo } from "react";
|
|
6
5
|
//#region src/lib/url.ts
|
|
7
6
|
function pushUrl(next, state) {
|
|
8
7
|
window.history.pushState(state, "", next);
|
|
@@ -49,8 +48,10 @@ function Link({ to, search, children, className, replace, ...rest }) {
|
|
|
49
48
|
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
|
|
50
49
|
if (ev.button !== 0) return;
|
|
51
50
|
ev.preventDefault();
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
startTransition(() => {
|
|
52
|
+
if (replace) replaceUrl(href);
|
|
53
|
+
else pushUrl(href);
|
|
54
|
+
});
|
|
54
55
|
};
|
|
55
56
|
return /* @__PURE__ */ jsx("a", {
|
|
56
57
|
...rest,
|
|
@@ -61,6 +62,24 @@ function Link({ to, search, children, className, replace, ...rest }) {
|
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
//#endregion
|
|
65
|
+
//#region src/hooks/useUrl.ts
|
|
66
|
+
const getPathname = () => window.location.pathname;
|
|
67
|
+
const getSearch = () => window.location.search;
|
|
68
|
+
function subscribe(cb) {
|
|
69
|
+
const handler = () => cb();
|
|
70
|
+
window.addEventListener("popstate", handler);
|
|
71
|
+
return () => {
|
|
72
|
+
window.removeEventListener("popstate", handler);
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function useUrlPath() {
|
|
76
|
+
return useSyncExternalStore(subscribe, getPathname, getPathname);
|
|
77
|
+
}
|
|
78
|
+
function useUrlSearchParams() {
|
|
79
|
+
const search = useSyncExternalStore(subscribe, getSearch, getSearch);
|
|
80
|
+
return useMemo(() => new URLSearchParams(search), [search]);
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
64
83
|
//#region src/components/NavLink.tsx
|
|
65
84
|
function isActivePath(pathname, targetPathname, match) {
|
|
66
85
|
if (pathname === targetPathname) return true;
|
|
@@ -95,14 +114,107 @@ function NavLink({ activeClass, className, inactiveClass, match = "exact", to, .
|
|
|
95
114
|
}
|
|
96
115
|
//#endregion
|
|
97
116
|
//#region src/components/Router.tsx
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
const DEFAULT_LOADING_DELAY_MS = 400;
|
|
118
|
+
const loadedRouteComponents = /* @__PURE__ */ new WeakMap();
|
|
119
|
+
const loadingRouteComponents = /* @__PURE__ */ new WeakMap();
|
|
120
|
+
function loadRouteComponent(component) {
|
|
121
|
+
const loaded = loadedRouteComponents.get(component);
|
|
122
|
+
if (loaded) return Promise.resolve(loaded);
|
|
123
|
+
let loading = loadingRouteComponents.get(component);
|
|
124
|
+
if (!loading) {
|
|
125
|
+
loading = component().then((module) => {
|
|
126
|
+
loadedRouteComponents.set(component, module.default);
|
|
127
|
+
loadingRouteComponents.delete(component);
|
|
128
|
+
return module.default;
|
|
129
|
+
});
|
|
130
|
+
loadingRouteComponents.set(component, loading);
|
|
131
|
+
}
|
|
132
|
+
return loading;
|
|
133
|
+
}
|
|
134
|
+
function findRouteMatch(routes, pathname) {
|
|
135
|
+
for (const route of routes) {
|
|
136
|
+
const params = route.matches(pathname);
|
|
137
|
+
if (!params) continue;
|
|
138
|
+
return {
|
|
139
|
+
route,
|
|
140
|
+
params,
|
|
141
|
+
pathname
|
|
142
|
+
};
|
|
104
143
|
}
|
|
105
|
-
return
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
function getLoadedRoute(match) {
|
|
147
|
+
if (!match) return null;
|
|
148
|
+
const Component = loadedRouteComponents.get(match.route.component);
|
|
149
|
+
if (!Component) return null;
|
|
150
|
+
return {
|
|
151
|
+
...match,
|
|
152
|
+
Component
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function scheduleTransition(cb) {
|
|
156
|
+
queueMicrotask(() => {
|
|
157
|
+
startTransition(cb);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function useRenderedRoute(match, loadingDelayMs) {
|
|
161
|
+
const [renderedRoute, setRenderedRoute] = useState(() => getLoadedRoute(match));
|
|
162
|
+
const [showLoading, setShowLoading] = useState(false);
|
|
163
|
+
const [loadError, setLoadError] = useState(null);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!match) {
|
|
166
|
+
scheduleTransition(() => {
|
|
167
|
+
setRenderedRoute(null);
|
|
168
|
+
setShowLoading(false);
|
|
169
|
+
setLoadError(null);
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const loaded = getLoadedRoute(match);
|
|
174
|
+
if (loaded) {
|
|
175
|
+
scheduleTransition(() => {
|
|
176
|
+
setRenderedRoute(loaded);
|
|
177
|
+
setShowLoading(false);
|
|
178
|
+
setLoadError(null);
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
let active = true;
|
|
183
|
+
let timeout;
|
|
184
|
+
scheduleTransition(() => {
|
|
185
|
+
setShowLoading(false);
|
|
186
|
+
});
|
|
187
|
+
if (loadingDelayMs <= 0) timeout = setTimeout(() => {
|
|
188
|
+
if (active) setShowLoading(true);
|
|
189
|
+
}, 0);
|
|
190
|
+
else timeout = setTimeout(() => {
|
|
191
|
+
if (active) setShowLoading(true);
|
|
192
|
+
}, loadingDelayMs);
|
|
193
|
+
loadRouteComponent(match.route.component).then((Component) => {
|
|
194
|
+
if (!active) return;
|
|
195
|
+
if (timeout) clearTimeout(timeout);
|
|
196
|
+
startTransition(() => {
|
|
197
|
+
setRenderedRoute({
|
|
198
|
+
...match,
|
|
199
|
+
Component
|
|
200
|
+
});
|
|
201
|
+
setShowLoading(false);
|
|
202
|
+
setLoadError(null);
|
|
203
|
+
});
|
|
204
|
+
}).catch((error) => {
|
|
205
|
+
if (!active) return;
|
|
206
|
+
setLoadError(error);
|
|
207
|
+
});
|
|
208
|
+
return () => {
|
|
209
|
+
active = false;
|
|
210
|
+
if (timeout) clearTimeout(timeout);
|
|
211
|
+
};
|
|
212
|
+
}, [loadingDelayMs, match]);
|
|
213
|
+
if (loadError) throw loadError;
|
|
214
|
+
return {
|
|
215
|
+
renderedRoute,
|
|
216
|
+
showLoading
|
|
217
|
+
};
|
|
106
218
|
}
|
|
107
219
|
/**
|
|
108
220
|
* Renders the first route that matches the current URL pathname.
|
|
@@ -115,25 +227,21 @@ function getLazyRouteComponent(component) {
|
|
|
115
227
|
* import routes from './routes'
|
|
116
228
|
*
|
|
117
229
|
* function App() {
|
|
118
|
-
* return <Router routes={routes} loading={<div>Loading...</div>} />
|
|
230
|
+
* return <Router routes={routes} loading={<div>Loading...</div>} loadingDelayMs={400} />
|
|
119
231
|
* }
|
|
120
232
|
* ```
|
|
121
233
|
*/
|
|
122
|
-
function Router({ routes, loading = null }) {
|
|
234
|
+
function Router({ routes, loading = null, loadingDelayMs = DEFAULT_LOADING_DELAY_MS }) {
|
|
123
235
|
const pathname = useUrlPath();
|
|
124
|
-
const normalizedRoutes = useMemo(() => normalizeRoutes(routes)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
children: /* @__PURE__ */ jsx(Component, { ...params })
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
return null;
|
|
236
|
+
const normalizedRoutes = useMemo(() => normalizeRoutes(routes), [routes]);
|
|
237
|
+
const { renderedRoute, showLoading } = useRenderedRoute(useMemo(() => findRouteMatch(normalizedRoutes, pathname), [normalizedRoutes, pathname]), loadingDelayMs);
|
|
238
|
+
if (showLoading) return loading;
|
|
239
|
+
if (!renderedRoute) return null;
|
|
240
|
+
const { Component, params } = renderedRoute;
|
|
241
|
+
return /* @__PURE__ */ jsx(Suspense, {
|
|
242
|
+
fallback: loading,
|
|
243
|
+
children: /* @__PURE__ */ jsx(Component, { ...params })
|
|
244
|
+
});
|
|
137
245
|
}
|
|
138
246
|
//#endregion
|
|
139
247
|
export { Link, NavLink, Router, normalizeLegacyPathToRegexpSyntax, normalizeRoutes, pushUrl, replaceUrl, useUrlPath, useUrlSearchParams };
|