router-kit 0.2.5 → 1.0.2

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
@@ -41,6 +41,16 @@ npm install router-kit
41
41
 
42
42
  N.B. `react` et `react-dom` sont des peerDependencies ; installez-les dans votre projet si nécessaire.
43
43
 
44
+ ## Important
45
+
46
+ ⚠️ Tous les hooks et composants de router-kit doivent être utilisés à l'intérieur du `RouterProvider`. Assurez-vous de wrapper votre application avec le `RouterProvider` au plus haut niveau possible.
47
+
48
+ Si vous utilisez des composants ou hooks en dehors du `RouterProvider`, vous obtiendrez une erreur explicite :
49
+
50
+ ```
51
+ RouterKit: Common hooks and components must be used within the RouterProvider returned by createRouter(). Wrap your app with the RouterProvider.
52
+ ```
53
+
44
54
  ## Concepts clés
45
55
 
46
56
  - Route : objet avec `path` (string) et `component` (JSX.Element). Les `children` sont supportés pour construire des arborescences.
@@ -112,33 +122,29 @@ Exemple :
112
122
  - `useRouter()` : hook interne retournant le contexte `{ path, fullPathWithParams, navigate }`. Lance une erreur si utilisé hors du provider.
113
123
  - `useParams()` : renvoie un objet clé/valeur pour les segments paramétrés de la route (ex: `{ id: "42" }`). Se base sur `fullPathWithParams` et `path`.
114
124
  - `useQuery()` : parse `window.location.search` et renvoie un objet `{ [key]: value }`.
125
+ - `useLocation()` : renvoie un objet avec les informations de localisation courante : `{ pathname, search, hash, state }`. Utile pour accéder aux détails de l'URL actuelle.
115
126
 
116
127
  ## Exemple complet
117
128
 
118
129
  ```tsx
119
130
  import React from "react";
120
- import { createRouter, RouterProvider, Link } from "router-kit";
131
+ import { createRouter, RouterProvider } from "router-kit";
121
132
 
122
- const Home = <div>Accueil</div>;
123
- const About = <div>À propos</div>;
133
+ const Home = () => <div>Accueil</div>;
134
+ const About = () => <div>À propos</div>;
124
135
 
125
136
  const routes = createRouter([
126
- { path: "/", component: Home },
127
- { path: "about", component: About },
137
+ { path: "/", component: <Home /> },
138
+ { path: "about", component: <About /> },
128
139
  { path: "/404", component: <div>Not Found</div> },
129
140
  ]);
130
141
 
131
- export default function App() {
132
- return (
133
- <div>
134
- <nav>
135
- <Link to="/">Home</Link>
136
- <Link to="/about">About</Link>
137
- </nav>
138
- <RouterProvider routes={routes} />
139
- </div>
140
- );
142
+ // Les composants de navigation doivent être à l'intérieur du RouterProvider
143
+ function App() {
144
+ return <RouterProvider routes={routes} />;
141
145
  }
146
+
147
+ export default App;
142
148
  ```
143
149
 
144
150
  ## Routes et 404
@@ -3,10 +3,33 @@ import { useEffect, useState } from "react";
3
3
  import join from "url-join";
4
4
  import Page404 from "../pages/404";
5
5
  import RouterContext from "./RouterContext";
6
+ const validateUrl = (url) => {
7
+ try {
8
+ new URL(url, window.location.origin);
9
+ return true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ };
6
15
  const RouterProvider = ({ routes }) => {
7
- const [path, setPath] = useState(window.location.pathname);
8
- let fullPathWithParams = "";
16
+ const [path, setPath] = useState("");
17
+ const [fullPathWithParams, setFullPathWithParams] = useState("");
9
18
  let page404 = null;
19
+ useEffect(() => {
20
+ setPath(window.location.pathname);
21
+ const handleLocationChange = () => {
22
+ setPath(window.location.pathname);
23
+ };
24
+ window.addEventListener("popstate", handleLocationChange);
25
+ window.addEventListener("pushState", handleLocationChange);
26
+ window.addEventListener("replaceState", handleLocationChange);
27
+ return () => {
28
+ window.removeEventListener("popstate", handleLocationChange);
29
+ window.removeEventListener("pushState", handleLocationChange);
30
+ window.removeEventListener("replaceState", handleLocationChange);
31
+ };
32
+ }, []);
10
33
  const pathValidation = (routeFullPath, currentPath) => {
11
34
  const routePaths = routeFullPath.split("|");
12
35
  for (const routePath of routePaths) {
@@ -39,7 +62,9 @@ const RouterProvider = ({ routes }) => {
39
62
  }
40
63
  const fullPath = join(parentPath, `/${route.path}`);
41
64
  if (pathValidation(fullPath, currentPath)) {
42
- fullPathWithParams = fullPath;
65
+ if (fullPath !== fullPathWithParams) {
66
+ setFullPathWithParams(fullPath);
67
+ }
43
68
  return route.component;
44
69
  }
45
70
  if (route.children) {
@@ -50,23 +75,26 @@ const RouterProvider = ({ routes }) => {
50
75
  }
51
76
  return null;
52
77
  };
53
- fullPathWithParams = "";
54
- const matchedComponent = getComponent(routes, path);
55
- const component = matchedComponent !== null && matchedComponent !== void 0 ? matchedComponent : (page404 || _jsx(Page404, {}));
56
78
  const navigate = (to, options) => {
57
- if (options === null || options === void 0 ? void 0 : options.replace) {
58
- window.history.replaceState({}, "", to);
79
+ if (!validateUrl(to)) {
80
+ console.error(`RouterKit: Invalid URL "${to}"`);
81
+ return;
59
82
  }
60
- else {
61
- window.history.pushState({}, "", to);
83
+ try {
84
+ if (options === null || options === void 0 ? void 0 : options.replace) {
85
+ window.history.replaceState((options === null || options === void 0 ? void 0 : options.state) || {}, "", to);
86
+ }
87
+ else {
88
+ window.history.pushState((options === null || options === void 0 ? void 0 : options.state) || {}, "", to);
89
+ }
90
+ setPath(to);
91
+ }
92
+ catch (error) {
93
+ console.error("RouterKit: Navigation failed", error);
62
94
  }
63
- setPath(to);
64
95
  };
65
- useEffect(() => {
66
- const handlePop = () => setPath(window.location.pathname);
67
- window.addEventListener("popstate", handlePop);
68
- return () => window.removeEventListener("popstate", handlePop);
69
- }, []);
96
+ const matchedComponent = getComponent(routes, path);
97
+ const component = matchedComponent !== null && matchedComponent !== void 0 ? matchedComponent : (page404 || _jsx(Page404, {}));
70
98
  return (_jsx(RouterContext.Provider, { value: { path, fullPathWithParams, navigate }, children: component }));
71
99
  };
72
100
  export default RouterProvider;
@@ -0,0 +1,2 @@
1
+ import type { Location } from "../types";
2
+ export declare function useLocation(): Location;
@@ -0,0 +1,16 @@
1
+ export function useLocation() {
2
+ if (typeof window === "undefined") {
3
+ return {
4
+ pathname: "",
5
+ search: "",
6
+ hash: "",
7
+ state: null,
8
+ };
9
+ }
10
+ return {
11
+ pathname: window.location.pathname,
12
+ search: window.location.search,
13
+ hash: window.location.hash,
14
+ state: window.history.state,
15
+ };
16
+ }
@@ -1,6 +1,3 @@
1
1
  export declare const useParams: () => {
2
2
  [key: string]: string;
3
3
  };
4
- export declare const useQuery: () => {
5
- [key: string]: string;
6
- };
@@ -1,5 +1,4 @@
1
1
  import { useRouter } from "./useRouter";
2
- // Retourne un objet { [paramName]: value }
3
2
  export const useParams = () => {
4
3
  const { path, fullPathWithParams } = useRouter();
5
4
  const params = {};
@@ -14,14 +13,3 @@ export const useParams = () => {
14
13
  });
15
14
  return params;
16
15
  };
17
- // Parse la query string proprement (ex: ?a=1&b=2)
18
- export const useQuery = () => {
19
- const query = {};
20
- if (typeof window === "undefined")
21
- return query;
22
- const usp = new URLSearchParams(window.location.search);
23
- usp.forEach((value, key) => {
24
- query[key] = value;
25
- });
26
- return query;
27
- };
@@ -0,0 +1,3 @@
1
+ export declare const useQuery: () => {
2
+ [key: string]: string;
3
+ };
@@ -0,0 +1,10 @@
1
+ export const useQuery = () => {
2
+ const query = {};
3
+ if (typeof window === "undefined")
4
+ return query;
5
+ const usp = new URLSearchParams(window.location.search);
6
+ usp.forEach((value, key) => {
7
+ query[key] = value;
8
+ });
9
+ return query;
10
+ };
@@ -27,14 +27,10 @@ import RouterContext from "../context/RouterContext";
27
27
  export function useRouter() {
28
28
  const ctx = useContext(RouterContext);
29
29
  if (!ctx) {
30
- const message = "Common hooks and components must be used within the RouterProvider returned by createRouter(). Wrap your app with the RouterProvider.";
31
- if (typeof window !== "undefined" && window.console && console.error) {
32
- console.error("%cRouterKit%c " + message, "color: #fff; background: #d9534f; font-weight: 700; padding: 2px 6px; border-radius: 3px;", "color: #d9534f;");
30
+ if (typeof window === "undefined") {
31
+ throw new Error("RouterKit: useRouter cannot be used during server side rendering");
33
32
  }
34
- else {
35
- console.error("RouterKit: " + message);
36
- }
37
- throw new Error("RouterKit: " + message);
33
+ throw new Error("RouterKit: useRouter must be used within RouterProvider");
38
34
  }
39
35
  return ctx;
40
36
  }
package/dist/index.d.ts CHANGED
@@ -2,4 +2,6 @@ export { default as Link } from "./components/Link";
2
2
  export { default as NavLink } from "./components/NavLink";
3
3
  export { default as RouterProvider } from "./context/RouterProvider";
4
4
  export { default as createRouter } from "./core/createRouter";
5
- export { useParams, useQuery } from "./hooks/hook";
5
+ export { useLocation } from "./hooks/useLocation";
6
+ export { useParams } from "./hooks/useParams";
7
+ export { useQuery } from "./hooks/useQuery";
package/dist/index.js CHANGED
@@ -2,4 +2,6 @@ export { default as Link } from "./components/Link";
2
2
  export { default as NavLink } from "./components/NavLink";
3
3
  export { default as RouterProvider } from "./context/RouterProvider";
4
4
  export { default as createRouter } from "./core/createRouter";
5
- export { useParams, useQuery } from "./hooks/hook";
5
+ export { useLocation } from "./hooks/useLocation";
6
+ export { useParams } from "./hooks/useParams";
7
+ export { useQuery } from "./hooks/useQuery";
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "email": "mohammed.bencheikh.dev@gmail.com",
6
6
  "url": "https://mohammedbencheikh.com/"
7
7
  },
8
- "version": "0.2.5",
8
+ "version": "1.0.2",
9
9
  "description": "A small React routing provider library",
10
10
  "main": "dist/index.js",
11
11
  "types": "dist/index.d.ts",