preact-hashish-router 0.0.13 → 0.0.15
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 +5 -0
- package/dist/Route.js +21 -10
- package/dist/Router.js +20 -5
- package/dist/context.d.ts +5 -0
- package/dist/match.d.ts +12 -0
- package/dist/match.js +44 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ import { ErrorRoute, Route, Router, RouterErrorBoundary } from "preact-hashish-r
|
|
|
26
26
|
import _404 from "./routes/404";
|
|
27
27
|
import AboutPage from "./routes/About";
|
|
28
28
|
import HomePage from "./routes/Home";
|
|
29
|
+
import ProductPage from "./routes/Product";
|
|
29
30
|
|
|
30
31
|
export default function App() {
|
|
31
32
|
return (
|
|
@@ -39,6 +40,10 @@ export default function App() {
|
|
|
39
40
|
<AboutPage />
|
|
40
41
|
</Route>
|
|
41
42
|
|
|
43
|
+
<Route path="/product/:id">
|
|
44
|
+
<ProductPage />
|
|
45
|
+
</Route>
|
|
46
|
+
|
|
42
47
|
<ErrorRoute>
|
|
43
48
|
<_404 />
|
|
44
49
|
</ErrorRoute>
|
package/dist/Route.js
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import { Suspense } from "preact/compat";
|
|
2
|
+
import { Suspense, useLayoutEffect, useState } from "preact/compat";
|
|
3
|
+
import { matchRoute } from "./match";
|
|
3
4
|
import { useRouter } from "./useRouter";
|
|
4
5
|
export function Route(props) {
|
|
5
6
|
const router = useRouter();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const [render, setRender] = useState(false);
|
|
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)
|
|
13
26
|
return null;
|
|
14
|
-
}
|
|
15
|
-
router.setItMatch(true);
|
|
16
27
|
if (props.lazy) {
|
|
17
28
|
return _jsx(Suspense, { fallback: props.fallback ?? _jsx("div", { children: "Loading..." }), children: props.children });
|
|
18
29
|
}
|
package/dist/Router.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useLayoutEffect, useMemo, useState } from "preact/hooks";
|
|
3
3
|
import { router_context } from "./context";
|
|
4
4
|
const get_hash_route = () => location.hash.slice(1) || "/";
|
|
5
5
|
export const Router = (props) => {
|
|
6
6
|
const [path, setPath] = useState();
|
|
7
7
|
const [query, setQuery] = useState("");
|
|
8
|
+
const [params, setParams] = useState({});
|
|
9
|
+
const [rest, setRest] = useState("");
|
|
8
10
|
const [itMatch, setItMatch] = useState(false);
|
|
9
11
|
const router_type = useMemo(() => {
|
|
10
12
|
return props.type;
|
|
@@ -35,10 +37,10 @@ export const Router = (props) => {
|
|
|
35
37
|
window.addEventListener("popstate", browserEffectHandler.listener);
|
|
36
38
|
},
|
|
37
39
|
cleanUp: () => {
|
|
38
|
-
window.removeEventListener("
|
|
40
|
+
window.removeEventListener("popstate", browserEffectHandler.listener);
|
|
39
41
|
},
|
|
40
42
|
};
|
|
41
|
-
|
|
43
|
+
useLayoutEffect(() => {
|
|
42
44
|
if (router_type !== "hash")
|
|
43
45
|
return;
|
|
44
46
|
const [path, query] = get_hash_route().split("?");
|
|
@@ -53,7 +55,7 @@ export const Router = (props) => {
|
|
|
53
55
|
hashEffectHandler.effect();
|
|
54
56
|
return () => hashEffectHandler.cleanUp();
|
|
55
57
|
}, []);
|
|
56
|
-
|
|
58
|
+
useLayoutEffect(() => {
|
|
57
59
|
if (router_type !== "browser")
|
|
58
60
|
return;
|
|
59
61
|
setPath(location.pathname);
|
|
@@ -62,6 +64,8 @@ export const Router = (props) => {
|
|
|
62
64
|
return () => browserEffectHandler.cleanUp();
|
|
63
65
|
}, []);
|
|
64
66
|
const handlerManualRouteChange = (newPath) => {
|
|
67
|
+
if (path === newPath)
|
|
68
|
+
return;
|
|
65
69
|
setPath(newPath);
|
|
66
70
|
setItMatch(false);
|
|
67
71
|
if (router_type === "hash") {
|
|
@@ -74,7 +78,18 @@ export const Router = (props) => {
|
|
|
74
78
|
}
|
|
75
79
|
};
|
|
76
80
|
const ProviderValue = useMemo(() => {
|
|
77
|
-
return {
|
|
81
|
+
return {
|
|
82
|
+
path,
|
|
83
|
+
go: handlerManualRouteChange,
|
|
84
|
+
itMatch,
|
|
85
|
+
setItMatch,
|
|
86
|
+
type: router_type,
|
|
87
|
+
query,
|
|
88
|
+
params,
|
|
89
|
+
rest,
|
|
90
|
+
setParams,
|
|
91
|
+
setRest,
|
|
92
|
+
};
|
|
78
93
|
}, [path, handlerManualRouteChange, itMatch, setItMatch, router_type]);
|
|
79
94
|
return _jsx(router_context.Provider, { value: ProviderValue, children: props.children });
|
|
80
95
|
};
|
package/dist/context.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Matches } from "./match";
|
|
1
2
|
export type RouterContext = {
|
|
2
3
|
path: string;
|
|
3
4
|
query: string;
|
|
@@ -5,5 +6,9 @@ export type RouterContext = {
|
|
|
5
6
|
itMatch: boolean;
|
|
6
7
|
setItMatch: (r: boolean) => void;
|
|
7
8
|
type: "hash" | "browser";
|
|
9
|
+
params: Matches["params"];
|
|
10
|
+
setParams: (p: Matches["params"]) => void;
|
|
11
|
+
rest: Matches["rest"];
|
|
12
|
+
setRest: (r: Matches["rest"]) => void;
|
|
8
13
|
};
|
|
9
14
|
export declare const router_context: import("preact").Context<RouterContext>;
|
package/dist/match.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
// Separa la URL y el patrón en segmentos, eliminando elementos vacíos para facilitar la comparación.
|
|
10
|
+
const urlSegments = url.split("/").filter(Boolean);
|
|
11
|
+
const routeSegments = route.split("/").filter(Boolean);
|
|
12
|
+
// Se itera hasta el máximo número de segmentos entre la URL y el patrón para cubrir ambos casos.
|
|
13
|
+
for (let i = 0; i < Math.max(urlSegments.length, routeSegments.length); i++) {
|
|
14
|
+
// Extrae y desestructura el segmento actual del patrón:
|
|
15
|
+
// - isParam: indica si el segmento es un parámetro (comienza con ":").
|
|
16
|
+
// - paramName: nombre del parámetro o valor literal.
|
|
17
|
+
// - modifier: puede ser '+', '*' o '?' para modificar el comportamiento de captura.
|
|
18
|
+
const [, isParam, paramName, modifier] = (routeSegments[i] || "").match(/^(:)?(.*?)([+*?]?)$/) || [];
|
|
19
|
+
// Si el segmento del patrón no es un parámetro y coincide exactamente con el de la URL, continúa.
|
|
20
|
+
if (!isParam && paramName === urlSegments[i])
|
|
21
|
+
continue;
|
|
22
|
+
// Si no es parámetro y el modificador es '*' se captura el resto de la URL.
|
|
23
|
+
if (!isParam && modifier === "*") {
|
|
24
|
+
matches.rest = `/${urlSegments.slice(i).map(decodeURIComponent).join("/")}`;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
// Valida que el segmento sea un parámetro y que, si es obligatorio, exista en la URL.
|
|
28
|
+
if (!isParam || (!urlSegments[i] && modifier !== "?" && modifier !== "*"))
|
|
29
|
+
return;
|
|
30
|
+
// Determina si el modificador indica la captura del resto de la URL ('+' o '*').
|
|
31
|
+
const isRest = modifier === "+" || modifier === "*";
|
|
32
|
+
// Asigna el valor del parámetro:
|
|
33
|
+
// - Si es una captura 'rest', se unen todos los segmentos restantes.
|
|
34
|
+
// - De lo contrario, se decodifica el segmento individual.
|
|
35
|
+
matches.params[paramName] = isRest
|
|
36
|
+
? urlSegments.slice(i).map(decodeURIComponent).join("/")
|
|
37
|
+
: decodeURIComponent(urlSegments[i]);
|
|
38
|
+
// Si se capturaron múltiples segmentos (caso de 'rest'), se interrumpe la iteración.
|
|
39
|
+
if (isRest)
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
// Retorna el objeto con los parámetros y/o resto capturados.
|
|
43
|
+
return matches;
|
|
44
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "preact-hashish-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"description": "A simple router for preact",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc -p ./tsconfig.lib.json",
|
|
9
|
-
"format": "prettier --write src --ignore-unknown",
|
|
9
|
+
"format": "prettier --write src app --ignore-unknown",
|
|
10
10
|
"app:dev": "vite",
|
|
11
11
|
"prepublishOnly": "npm run build && npm run format",
|
|
12
12
|
"push": "npm version patch && git push",
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@preact/preset-vite": "^2.10.1",
|
|
41
41
|
"@types/node": "^22.13.4",
|
|
42
|
-
"preact-hashish-router": "^0.0.6",
|
|
43
42
|
"prettier": "^3.5.1",
|
|
44
43
|
"prettier-plugin-organize-imports": "^4.1.0",
|
|
45
44
|
"typescript": "^5.7.3",
|