preact-hashish-router 0.0.18 → 0.1.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 +9 -33
- package/dist/A.d.ts +4 -2
- package/dist/A.js +10 -13
- package/dist/NotFound.d.ts +6 -0
- package/dist/NotFound.js +10 -0
- package/dist/RenderMatchedRoute.d.ts +1 -0
- package/dist/RenderMatchedRoute.js +16 -0
- package/dist/Route.d.ts +12 -4
- package/dist/Route.js +4 -29
- package/dist/Router.d.ts +3 -14
- package/dist/Router.js +51 -90
- package/dist/RouterErrorBoundary.js +1 -5
- package/dist/context.d.ts +22 -14
- package/dist/context.js +37 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.js +6 -7
- package/dist/matcher.js +6 -0
- package/dist/router/matcher.d.ts +11 -0
- package/dist/router/matcher.js +9 -0
- package/package.json +6 -1
- package/dist/ErrorRoute.d.ts +0 -4
- package/dist/ErrorRoute.js +0 -12
- package/dist/Redirect.d.ts +0 -4
- package/dist/Redirect.js +0 -10
- package/dist/match.d.ts +0 -12
- package/dist/match.js +0 -42
- package/dist/useInternalRouter.d.ts +0 -1
- package/dist/useInternalRouter.js +0 -9
- package/dist/useRouter.d.ts +0 -3
- package/dist/useRouter.js +0 -16
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- Error handling integration with `ErrorRoute`.
|
|
8
8
|
- Fully typed.
|
|
9
9
|
- Ultra lightweight.
|
|
10
|
-
-
|
|
10
|
+
- Minimal external dependencies.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -22,16 +22,16 @@ npm install preact-hashish-router@latest
|
|
|
22
22
|
First, ensure your application is wrapped within the router context. This will allow you to access routes and related functions.
|
|
23
23
|
|
|
24
24
|
```tsx
|
|
25
|
-
import {
|
|
26
|
-
import _404 from "./routes/404";
|
|
25
|
+
import { Route, Router, RouterErrorBoundary } from "preact-hashish-router";
|
|
27
26
|
import AboutPage from "./routes/About";
|
|
28
27
|
import HomePage from "./routes/Home";
|
|
29
28
|
import ProductPage from "./routes/Product";
|
|
30
29
|
|
|
31
30
|
export default function App() {
|
|
32
31
|
return (
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// or hash for hash-based routing
|
|
33
|
+
<RouterErrorBoundary>
|
|
34
|
+
<Router type="browser">
|
|
35
35
|
<Route path="/">
|
|
36
36
|
<HomePage />
|
|
37
37
|
</Route>
|
|
@@ -43,12 +43,8 @@ export default function App() {
|
|
|
43
43
|
<Route path="/product/:id">
|
|
44
44
|
<ProductPage />
|
|
45
45
|
</Route>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<_404 />
|
|
49
|
-
</ErrorRoute>
|
|
50
|
-
</RouterErrorBoundary>
|
|
51
|
-
</Router>
|
|
46
|
+
</Router>
|
|
47
|
+
</RouterErrorBoundary>
|
|
52
48
|
);
|
|
53
49
|
}
|
|
54
50
|
```
|
|
@@ -61,10 +57,10 @@ The `useRouter` hook gives you access to the router context to programmatically
|
|
|
61
57
|
import { useRouter } from "preact-hashish-router";
|
|
62
58
|
|
|
63
59
|
function HomePage() {
|
|
64
|
-
const
|
|
60
|
+
const { go, params, path, searchParams } = useRouter();
|
|
65
61
|
|
|
66
62
|
function goToAbout() {
|
|
67
|
-
|
|
63
|
+
go("/about");
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
return (
|
|
@@ -93,26 +89,6 @@ export default function Header() {
|
|
|
93
89
|
}
|
|
94
90
|
```
|
|
95
91
|
|
|
96
|
-
### `<Redirect />` Component
|
|
97
|
-
|
|
98
|
-
```tsx
|
|
99
|
-
import { Redirect } from "preact-hashish-router";
|
|
100
|
-
|
|
101
|
-
export default function ProductPage() {
|
|
102
|
-
return (
|
|
103
|
-
<>
|
|
104
|
-
<header>
|
|
105
|
-
<nav>
|
|
106
|
-
<A href="/">Home</A>
|
|
107
|
-
<A href="/about">About</A>
|
|
108
|
-
</nav>
|
|
109
|
-
</header>
|
|
110
|
-
<Redirect to="/" />
|
|
111
|
-
</>
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
92
|
## Development
|
|
117
93
|
|
|
118
94
|
If you have any improvements or find any issues, feel free to contribute or open an issue in the associated repository.
|
package/dist/A.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type AProps =
|
|
1
|
+
import { ComponentProps } from "preact/compat";
|
|
2
|
+
export type AProps = Omit<ComponentProps<"a">, "href"> & {
|
|
3
|
+
href: string;
|
|
4
|
+
};
|
|
3
5
|
export declare const A: import("preact").FunctionalComponent<import("preact/compat").PropsWithoutRef<AProps> & {
|
|
4
6
|
ref?: import("preact").Ref<HTMLAnchorElement> | undefined;
|
|
5
7
|
}>;
|
package/dist/A.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "preact/compat";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
router.go(href.toString());
|
|
14
|
-
};
|
|
15
|
-
return (_jsx("a", { href: router.type === "browser" ? href : `#${href}`, className: className, "data-route-active": isActive, ...props, onClick: router.type === "browser" ? browserRouterClickAnchorHandler : undefined }));
|
|
3
|
+
import { useHashisherContext } from "./context";
|
|
4
|
+
export const A = forwardRef(({ href, ...props }, forwardedRef) => {
|
|
5
|
+
const { go } = useHashisherContext();
|
|
6
|
+
if (!href) {
|
|
7
|
+
throw new Error("A: href must be defined");
|
|
8
|
+
}
|
|
9
|
+
return (_jsx("a", { ref: forwardedRef, href: href, onClick: (event) => {
|
|
10
|
+
event.preventDefault();
|
|
11
|
+
go(href);
|
|
12
|
+
}, ...props }));
|
|
16
13
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { VNode } from "preact";
|
|
2
|
+
export declare const NotFound: (props: {
|
|
3
|
+
element: VNode<any>;
|
|
4
|
+
}) => import("preact").JSX.Element;
|
|
5
|
+
export declare const set_not_found_element: (el: VNode<any>) => void;
|
|
6
|
+
export declare const get_not_found_element: () => VNode<any>;
|
package/dist/NotFound.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
export const NotFound = (props) => {
|
|
3
|
+
set_not_found_element(props.element);
|
|
4
|
+
return _jsx(_Fragment, {});
|
|
5
|
+
};
|
|
6
|
+
let not_found_element = _jsx("div", { children: "404 Not Found" });
|
|
7
|
+
export const set_not_found_element = (el) => {
|
|
8
|
+
not_found_element = el;
|
|
9
|
+
};
|
|
10
|
+
export const get_not_found_element = () => not_found_element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const RenderMatchedRoute: () => import("preact").VNode<any>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { Suspense } from "preact/compat";
|
|
3
|
+
import { useHashisherContext } from "./context";
|
|
4
|
+
import { get_not_found_element } from "./NotFound";
|
|
5
|
+
export const RenderMatchedRoute = () => {
|
|
6
|
+
const { active_route_data } = useHashisherContext();
|
|
7
|
+
if (!active_route_data)
|
|
8
|
+
return get_not_found_element();
|
|
9
|
+
if (active_route_data.component === null) {
|
|
10
|
+
return get_not_found_element();
|
|
11
|
+
}
|
|
12
|
+
if (active_route_data.lazy) {
|
|
13
|
+
return _jsx(Suspense, { fallback: active_route_data.fallback, children: active_route_data.component });
|
|
14
|
+
}
|
|
15
|
+
return active_route_data.component;
|
|
16
|
+
};
|
package/dist/Route.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { VNode } from "preact";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
export type RouteProps = {
|
|
3
|
+
/** The route path matcher
|
|
4
|
+
* @example "/"
|
|
5
|
+
* "/product/:id"
|
|
6
|
+
* "/paper/*"
|
|
7
|
+
* "/docs/**"
|
|
8
|
+
*/
|
|
4
9
|
path: string;
|
|
5
|
-
|
|
10
|
+
/** The node that will be rendered if match */
|
|
11
|
+
element: VNode<any>;
|
|
12
|
+
/** Shoud be wrapped in a \<Suspense /> tag */
|
|
6
13
|
lazy?: boolean;
|
|
14
|
+
/** Fallback to display when the element is loading when lazy is true */
|
|
7
15
|
fallback?: VNode;
|
|
8
16
|
};
|
|
9
|
-
export declare function Route(props: RouteProps): import("preact").
|
|
17
|
+
export declare function Route(props: RouteProps): import("preact").JSX.Element;
|
package/dist/Route.js
CHANGED
|
@@ -1,31 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { matchRoute } from "./match";
|
|
4
|
-
import { useInternalRouter } from "./useInternalRouter";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { add_route_to_matcher } from "./router/matcher";
|
|
5
3
|
export function Route(props) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
useLayoutEffect(() => {
|
|
9
|
-
setRender(false);
|
|
10
|
-
if (props.exact === undefined) {
|
|
11
|
-
props.exact = false;
|
|
12
|
-
}
|
|
13
|
-
if (props.exact === true && router.path !== props.path) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const matches = matchRoute(router.path || "/", props.path);
|
|
17
|
-
if (props.exact === false && matches === undefined) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
router.setParams(matches?.params || {});
|
|
21
|
-
router.setRest(matches?.rest);
|
|
22
|
-
router.setItMatch(true);
|
|
23
|
-
setRender(true);
|
|
24
|
-
}, [router.path]);
|
|
25
|
-
if (!render)
|
|
26
|
-
return null;
|
|
27
|
-
if (props.lazy) {
|
|
28
|
-
return _jsx(Suspense, { fallback: props.fallback ?? _jsx("div", { children: "Loading..." }), children: props.children });
|
|
29
|
-
}
|
|
30
|
-
return props.children;
|
|
4
|
+
add_route_to_matcher(props.path, props);
|
|
5
|
+
return _jsx(_Fragment, {});
|
|
31
6
|
}
|
package/dist/Router.d.ts
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import { PropsWithChildren } from "preact/compat";
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Only for `hash` routers.
|
|
7
|
-
*
|
|
8
|
-
* Decide if the initial pathname will be rewrite as the initial hash.
|
|
9
|
-
*
|
|
10
|
-
* `Caution`: This will replace the initial url hash
|
|
11
|
-
* @default false
|
|
12
|
-
*/
|
|
13
|
-
redirect_path_to_hash?: boolean;
|
|
14
|
-
};
|
|
2
|
+
export type RouterProps = PropsWithChildren<{
|
|
3
|
+
type: "browser";
|
|
4
|
+
}>;
|
|
15
5
|
export declare const Router: (props: RouterProps) => import("preact").JSX.Element;
|
|
16
|
-
export {};
|
package/dist/Router.js
CHANGED
|
@@ -1,101 +1,62 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
import { useCallback, useLayoutEffect, useState } from "preact/hooks";
|
|
3
|
+
import { findRoute } from "rou3";
|
|
4
|
+
import { parseURL } from "ufo";
|
|
5
|
+
import { HashisherContext } from "./context";
|
|
6
|
+
import { RenderMatchedRoute } from "./RenderMatchedRoute";
|
|
7
|
+
import { Matcher } from "./router/matcher";
|
|
5
8
|
export const Router = (props) => {
|
|
6
|
-
const [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
const [active_path, set_active_path] = useState(() => {
|
|
10
|
+
if (typeof window !== "undefined")
|
|
11
|
+
return window.location.pathname;
|
|
12
|
+
return null;
|
|
13
|
+
});
|
|
14
|
+
const [params, setParams] = useState(undefined);
|
|
15
|
+
const [searchParams, setSearchParams] = useState(new URLSearchParams());
|
|
16
|
+
const [active_route_data, set_active_route_data] = useState(null);
|
|
17
|
+
const execute_path_change = useCallback((raw_path) => {
|
|
18
|
+
const url = parseURL(window.location.href);
|
|
19
|
+
const newPath = raw_path === null ? url.pathname : raw_path;
|
|
20
|
+
const route_data = findRoute(Matcher, undefined, newPath);
|
|
21
|
+
if (!route_data) {
|
|
22
|
+
set_active_path(newPath);
|
|
23
|
+
setSearchParams(new URLSearchParams(url.search));
|
|
24
|
+
set_active_route_data(null);
|
|
25
|
+
setParams(undefined);
|
|
26
|
+
if (props.type === "browser") {
|
|
27
|
+
window.history.pushState(null, "", newPath);
|
|
21
28
|
}
|
|
22
|
-
},
|
|
23
|
-
effect: () => {
|
|
24
|
-
window.addEventListener("hashchange", hashEffectHandler.listener);
|
|
25
|
-
},
|
|
26
|
-
cleanUp: () => {
|
|
27
|
-
window.removeEventListener("hashchange", hashEffectHandler.listener);
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
const browserEffectHandler = {
|
|
31
|
-
listener: () => {
|
|
32
|
-
setPath(location.pathname);
|
|
33
|
-
setQuery(location.search.split("?")[1] || "");
|
|
34
|
-
if (path !== location.pathname) {
|
|
35
|
-
setItMatch(false);
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
effect: () => {
|
|
39
|
-
window.addEventListener("popstate", browserEffectHandler.listener);
|
|
40
|
-
},
|
|
41
|
-
cleanUp: () => {
|
|
42
|
-
window.removeEventListener("popstate", browserEffectHandler.listener);
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
useLayoutEffect(() => {
|
|
46
|
-
if (router_type !== "hash")
|
|
47
29
|
return;
|
|
48
|
-
const [path, query] = get_hash_route().split("?");
|
|
49
|
-
setQuery(query || "");
|
|
50
|
-
setPath(path);
|
|
51
|
-
if (props.redirect_path_to_hash === true) {
|
|
52
|
-
if (location.pathname !== "/") {
|
|
53
|
-
location.hash = location.pathname;
|
|
54
|
-
location.pathname = "";
|
|
55
|
-
}
|
|
56
30
|
}
|
|
57
|
-
|
|
58
|
-
|
|
31
|
+
set_active_path(newPath);
|
|
32
|
+
setSearchParams(new URLSearchParams(url.search));
|
|
33
|
+
setParams({ ...route_data.params });
|
|
34
|
+
set_active_route_data({ ...route_data.data });
|
|
35
|
+
if (props.type === "browser") {
|
|
36
|
+
window.history.pushState(null, "", newPath);
|
|
37
|
+
}
|
|
59
38
|
}, []);
|
|
60
39
|
useLayoutEffect(() => {
|
|
61
|
-
if (
|
|
40
|
+
if (props.type !== "browser")
|
|
62
41
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
42
|
+
const listener = () => {
|
|
43
|
+
execute_path_change(null);
|
|
44
|
+
};
|
|
45
|
+
window.addEventListener("popstate", listener);
|
|
46
|
+
listener();
|
|
47
|
+
return () => {
|
|
48
|
+
window.removeEventListener("popstate", listener);
|
|
49
|
+
};
|
|
67
50
|
}, []);
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
return;
|
|
72
|
-
if (router_type === "hash") {
|
|
73
|
-
location.hash = newPath;
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (router_type === "browser") {
|
|
77
|
-
setPath(np);
|
|
78
|
-
setQuery(nq || "");
|
|
79
|
-
if (path !== np) {
|
|
80
|
-
setItMatch(false);
|
|
81
|
-
}
|
|
82
|
-
history.pushState(null, "", new URL(newPath, location.origin));
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
51
|
+
const go_imperative = (newPath) => {
|
|
52
|
+
const pathname = parseURL(newPath).pathname;
|
|
53
|
+
execute_path_change(pathname);
|
|
85
54
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
go: handlerManualRouteChange,
|
|
90
|
-
itMatch,
|
|
91
|
-
setItMatch,
|
|
92
|
-
type: router_type,
|
|
93
|
-
query,
|
|
55
|
+
return (_jsxs(HashisherContext.Provider, { value: {
|
|
56
|
+
active_path,
|
|
57
|
+
searchParams,
|
|
94
58
|
params,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
};
|
|
99
|
-
}, [path, handlerManualRouteChange, itMatch, setItMatch, router_type]);
|
|
100
|
-
return _jsx(router_context.Provider, { value: ProviderValue, children: props.children });
|
|
59
|
+
active_route_data,
|
|
60
|
+
go: go_imperative,
|
|
61
|
+
}, children: [props.children, _jsx(RenderMatchedRoute, {})] }));
|
|
101
62
|
};
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { Component } from "preact";
|
|
3
3
|
export class RouterErrorBoundary extends Component {
|
|
4
|
-
|
|
5
|
-
super(...arguments);
|
|
6
|
-
this.state = { error: null };
|
|
7
|
-
}
|
|
4
|
+
state = { error: null };
|
|
8
5
|
static getDerivedStateFromError(error) {
|
|
9
6
|
return { error: error.message };
|
|
10
7
|
}
|
|
11
8
|
componentDidCatch(error) {
|
|
12
|
-
console.error(error);
|
|
13
9
|
this.setState({ error: error.message });
|
|
14
10
|
}
|
|
15
11
|
render() {
|
package/dist/context.d.ts
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { RouteMatched } from "./router/matcher";
|
|
2
|
+
export type HashisherContextVal = {
|
|
3
|
+
active_path: string | null;
|
|
4
|
+
params: RouteMatched["params"];
|
|
5
|
+
active_route_data: RouteMatched["data"] | null;
|
|
6
|
+
searchParams: URLSearchParams;
|
|
7
|
+
};
|
|
8
|
+
export type HashisherContextMethods = {
|
|
9
|
+
go(newPath: string): void;
|
|
10
|
+
};
|
|
11
|
+
export declare const HashisherContext: import("preact").Context<HashisherContextVal & HashisherContextMethods>;
|
|
12
|
+
export declare const useHashisherContext: () => HashisherContextVal & HashisherContextMethods;
|
|
13
|
+
export declare function useParams<T extends Record<string, string>>(): T & {
|
|
14
|
+
_?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function useSearchParams(): URLSearchParams;
|
|
17
|
+
export declare const useRouter: () => {
|
|
18
|
+
path: string | null;
|
|
19
|
+
params: Record<string, string> | undefined;
|
|
20
|
+
go: (newPath: string) => void;
|
|
21
|
+
searchParams: URLSearchParams;
|
|
22
|
+
};
|
package/dist/context.js
CHANGED
|
@@ -1,2 +1,38 @@
|
|
|
1
1
|
import { createContext } from "preact";
|
|
2
|
-
|
|
2
|
+
import { useContext } from "preact/hooks";
|
|
3
|
+
export const HashisherContext = createContext({
|
|
4
|
+
active_path: "",
|
|
5
|
+
active_route_data: null,
|
|
6
|
+
params: undefined,
|
|
7
|
+
searchParams: new URLSearchParams(),
|
|
8
|
+
go() { },
|
|
9
|
+
});
|
|
10
|
+
export const useHashisherContext = () => {
|
|
11
|
+
const c = useContext(HashisherContext);
|
|
12
|
+
if (!c)
|
|
13
|
+
throw new Error("useHashisherContext should be inside a HashisherContext provider");
|
|
14
|
+
return c;
|
|
15
|
+
};
|
|
16
|
+
export function useParams() {
|
|
17
|
+
const c = useContext(HashisherContext);
|
|
18
|
+
if (!c)
|
|
19
|
+
throw new Error("useParams should be inside a HashisherContext provider");
|
|
20
|
+
return c.params;
|
|
21
|
+
}
|
|
22
|
+
export function useSearchParams() {
|
|
23
|
+
const c = useContext(HashisherContext);
|
|
24
|
+
if (!c)
|
|
25
|
+
throw new Error("useSearchParams should be inside a HashisherContext provider");
|
|
26
|
+
return c.searchParams;
|
|
27
|
+
}
|
|
28
|
+
export const useRouter = () => {
|
|
29
|
+
const c = useContext(HashisherContext);
|
|
30
|
+
if (!c)
|
|
31
|
+
throw new Error("useRouter should be inside a HashisherContext provider");
|
|
32
|
+
return {
|
|
33
|
+
path: c.active_path,
|
|
34
|
+
params: c.params,
|
|
35
|
+
go: c.go,
|
|
36
|
+
searchParams: c.searchParams,
|
|
37
|
+
};
|
|
38
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export * from "./useRouter";
|
|
1
|
+
export { A } from "./A";
|
|
2
|
+
export { useParams, useRouter, useSearchParams } from "./context";
|
|
3
|
+
export { NotFound } from "./NotFound";
|
|
4
|
+
export { Route } from "./Route";
|
|
5
|
+
export { Router } from "./Router";
|
|
6
|
+
export { RouterErrorBoundary } from "./RouterErrorBoundary";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export * from "./useRouter";
|
|
1
|
+
export { A } from "./A";
|
|
2
|
+
export { useParams, useRouter, useSearchParams } from "./context";
|
|
3
|
+
export { NotFound } from "./NotFound";
|
|
4
|
+
export { Route } from "./Route";
|
|
5
|
+
export { Router } from "./Router";
|
|
6
|
+
export { RouterErrorBoundary } from "./RouterErrorBoundary";
|
package/dist/matcher.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { VNode } from "preact";
|
|
2
|
+
import { MatchedRoute } from "rou3";
|
|
3
|
+
import { RouteProps } from "../Route";
|
|
4
|
+
export type MatcherPayload = {
|
|
5
|
+
component: VNode<any>;
|
|
6
|
+
lazy: boolean;
|
|
7
|
+
fallback: VNode<any> | null;
|
|
8
|
+
};
|
|
9
|
+
export type RouteMatched = MatchedRoute<MatcherPayload>;
|
|
10
|
+
export declare const Matcher: import("rou3").RouterContext<MatcherPayload>;
|
|
11
|
+
export declare const add_route_to_matcher: (path: string, data: Omit<RouteProps, "path">) => void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { addRoute, createRouter } from "rou3";
|
|
2
|
+
export const Matcher = createRouter();
|
|
3
|
+
export const add_route_to_matcher = (path, data) => {
|
|
4
|
+
addRoute(Matcher, undefined, path, {
|
|
5
|
+
component: data.element,
|
|
6
|
+
fallback: data.fallback || null,
|
|
7
|
+
lazy: Boolean(data.lazy),
|
|
8
|
+
});
|
|
9
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "preact-hashish-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"description": "A simple router for preact",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@preact/preset-vite": "^2.10.2",
|
|
38
38
|
"@types/node": "^22.17.1",
|
|
39
|
+
"preact-render-to-string": "^6.5.13",
|
|
39
40
|
"prettier": "^3.6.2",
|
|
40
41
|
"prettier-plugin-organize-imports": "^4.2.0",
|
|
41
42
|
"typescript": "^5.9.2",
|
|
@@ -49,5 +50,9 @@
|
|
|
49
50
|
],
|
|
50
51
|
"peerDependencies": {
|
|
51
52
|
"preact": "^10.27.0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"rou3": "^0.7.3",
|
|
56
|
+
"ufo": "^1.6.1"
|
|
52
57
|
}
|
|
53
58
|
}
|
package/dist/ErrorRoute.d.ts
DELETED
package/dist/ErrorRoute.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import { Suspense } from "preact/compat";
|
|
3
|
-
import { useInternalRouter } from "./useInternalRouter";
|
|
4
|
-
export function ErrorRoute(props) {
|
|
5
|
-
const router = useInternalRouter();
|
|
6
|
-
if (router.itMatch)
|
|
7
|
-
return null;
|
|
8
|
-
if (props.lazy) {
|
|
9
|
-
return _jsx(Suspense, { fallback: _jsx("div", { children: "Loading..." }), children: props.children });
|
|
10
|
-
}
|
|
11
|
-
return props.children;
|
|
12
|
-
}
|
package/dist/Redirect.d.ts
DELETED
package/dist/Redirect.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import { Fragment, useLayoutEffect } from "preact/compat";
|
|
3
|
-
import { useInternalRouter } from "./useInternalRouter";
|
|
4
|
-
export const Redirect = (props) => {
|
|
5
|
-
const router = useInternalRouter();
|
|
6
|
-
useLayoutEffect(() => {
|
|
7
|
-
router.go(props.to);
|
|
8
|
-
}, []);
|
|
9
|
-
return _jsx(Fragment, {});
|
|
10
|
-
};
|
package/dist/match.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export type Matches = {
|
|
2
|
-
params: Record<string, string>;
|
|
3
|
-
rest?: string;
|
|
4
|
-
};
|
|
5
|
-
/**
|
|
6
|
-
* Compara una URL contra un patrón definido y extrae parámetros.
|
|
7
|
-
* @param url - La URL a analizar (ejemplo: "/user/12345").
|
|
8
|
-
* @param route - El patrón de URL (ejemplo: "/user/:id").
|
|
9
|
-
* @param matches - Objeto opcional para acumular coincidencias, inicializado por defecto.
|
|
10
|
-
* @returns Un objeto Matches si la URL coincide con el patrón; de lo contrario, undefined.
|
|
11
|
-
*/
|
|
12
|
-
export declare const matchRoute: (url: string, route: string, matches?: Matches) => Matches | undefined;
|
package/dist/match.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compara una URL contra un patrón definido y extrae parámetros.
|
|
3
|
-
* @param url - La URL a analizar (ejemplo: "/user/12345").
|
|
4
|
-
* @param route - El patrón de URL (ejemplo: "/user/:id").
|
|
5
|
-
* @param matches - Objeto opcional para acumular coincidencias, inicializado por defecto.
|
|
6
|
-
* @returns Un objeto Matches si la URL coincide con el patrón; de lo contrario, undefined.
|
|
7
|
-
*/
|
|
8
|
-
export const matchRoute = (url, route, matches = { params: {} }) => {
|
|
9
|
-
const urlSegments = url.split("/").filter(Boolean);
|
|
10
|
-
const routeSegments = route.split("/").filter(Boolean);
|
|
11
|
-
// Se itera hasta el máximo número de segmentos entre la URL y el patrón para cubrir ambos casos.
|
|
12
|
-
for (let i = 0; i < Math.max(urlSegments.length, routeSegments.length); i++) {
|
|
13
|
-
// Extrae y desestructura el segmento actual del patrón:
|
|
14
|
-
// - isParam: indica si el segmento es un parámetro (comienza con ":").
|
|
15
|
-
// - paramName: nombre del parámetro o valor literal.
|
|
16
|
-
// - modifier: puede ser '+', '*' o '?' para modificar el comportamiento de captura.
|
|
17
|
-
const [, isParam, paramName, modifier] = (routeSegments[i] || "").match(/^(:)?(.*?)([+*?]?)$/) || [];
|
|
18
|
-
// Si el segmento del patrón no es un parámetro y coincide exactamente con el de la URL, continúa.
|
|
19
|
-
if (!isParam && paramName === urlSegments[i])
|
|
20
|
-
continue;
|
|
21
|
-
// Si no es parámetro y el modificador es '*' se captura el resto de la URL.
|
|
22
|
-
if (!isParam && modifier === "*") {
|
|
23
|
-
matches.rest = `/${urlSegments.slice(i).map(decodeURIComponent).join("/")}`;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
// Valida que el segmento sea un parámetro y que, si es obligatorio, exista en la URL.
|
|
27
|
-
if (!isParam || (!urlSegments[i] && modifier !== "?" && modifier !== "*"))
|
|
28
|
-
return;
|
|
29
|
-
// Determina si el modificador indica la captura del resto de la URL ('+' o '*').
|
|
30
|
-
const isRest = modifier === "+" || modifier === "*";
|
|
31
|
-
// Asigna el valor del parámetro:
|
|
32
|
-
// - Si es una captura 'rest', se unen todos los segmentos restantes.
|
|
33
|
-
// - De lo contrario, se decodifica el segmento individual.
|
|
34
|
-
matches.params[paramName] = isRest
|
|
35
|
-
? urlSegments.slice(i).map(decodeURIComponent).join("/")
|
|
36
|
-
: decodeURIComponent(urlSegments[i]);
|
|
37
|
-
// Si se capturaron múltiples segmentos (caso de 'rest'), se interrumpe la iteración.
|
|
38
|
-
if (isRest)
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
return matches;
|
|
42
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function useInternalRouter(): import("./context").RouterContext;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { useContext } from "preact/hooks";
|
|
2
|
-
import { router_context } from "./context";
|
|
3
|
-
export function useInternalRouter() {
|
|
4
|
-
const context = useContext(router_context);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error("useInternalRouter should be used within a Router");
|
|
7
|
-
}
|
|
8
|
-
return context;
|
|
9
|
-
}
|
package/dist/useRouter.d.ts
DELETED
package/dist/useRouter.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { useContext } from "preact/hooks";
|
|
2
|
-
import { router_context } from "./context";
|
|
3
|
-
export function useRouter() {
|
|
4
|
-
const context = useContext(router_context);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error("useRouter should be used within a Router");
|
|
7
|
-
}
|
|
8
|
-
const { go, path, params, rest, query } = context;
|
|
9
|
-
return {
|
|
10
|
-
go,
|
|
11
|
-
path,
|
|
12
|
-
params,
|
|
13
|
-
rest,
|
|
14
|
-
query,
|
|
15
|
-
};
|
|
16
|
-
}
|