@weldjs/router 0.2.0
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/dist/index.cjs +200 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +71 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +164 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Link: () => Link,
|
|
24
|
+
ProtectedRoute: () => ProtectedRoute,
|
|
25
|
+
Redirect: () => Redirect,
|
|
26
|
+
Route: () => Route,
|
|
27
|
+
WeldRouter: () => WeldRouter,
|
|
28
|
+
useLocation: () => useLocation,
|
|
29
|
+
useNavigate: () => useNavigate,
|
|
30
|
+
useParams: () => useParams,
|
|
31
|
+
useRouter: () => useRouter,
|
|
32
|
+
useSearchParams: () => useSearchParams
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/context.tsx
|
|
37
|
+
var import_react = require("react");
|
|
38
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
+
var RouterContext = (0, import_react.createContext)(null);
|
|
40
|
+
function useRouter() {
|
|
41
|
+
const ctx = (0, import_react.useContext)(RouterContext);
|
|
42
|
+
if (!ctx) throw new Error("[WeldRouter] Hooks must be used inside <WeldRouter>");
|
|
43
|
+
return ctx;
|
|
44
|
+
}
|
|
45
|
+
function WeldRouter({ children, base = "" }) {
|
|
46
|
+
const getLocation = () => ({
|
|
47
|
+
pathname: window.location.pathname.replace(base, "") || "/",
|
|
48
|
+
search: window.location.search,
|
|
49
|
+
hash: window.location.hash
|
|
50
|
+
});
|
|
51
|
+
const [location, setLocation] = (0, import_react.useState)(getLocation);
|
|
52
|
+
const [params, setParams] = (0, import_react.useState)({});
|
|
53
|
+
(0, import_react.useEffect)(() => {
|
|
54
|
+
const handler = () => setLocation(getLocation());
|
|
55
|
+
window.addEventListener("popstate", handler);
|
|
56
|
+
return () => window.removeEventListener("popstate", handler);
|
|
57
|
+
}, [base]);
|
|
58
|
+
const navigate = (0, import_react.useCallback)((to, options) => {
|
|
59
|
+
const url = base + to;
|
|
60
|
+
if (options?.replace) {
|
|
61
|
+
window.history.replaceState(options.state ?? null, "", url);
|
|
62
|
+
} else {
|
|
63
|
+
window.history.pushState(options?.state ?? null, "", url);
|
|
64
|
+
}
|
|
65
|
+
setLocation(getLocation());
|
|
66
|
+
}, [base]);
|
|
67
|
+
const back = (0, import_react.useCallback)(() => window.history.back(), []);
|
|
68
|
+
const forward = (0, import_react.useCallback)(() => window.history.forward(), []);
|
|
69
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterContext.Provider, { value: { ...location, params, navigate, back, forward }, children });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/Route.tsx
|
|
73
|
+
var import_react2 = require("react");
|
|
74
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
75
|
+
function matchPath(pattern, pathname) {
|
|
76
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
77
|
+
const pathnameParts = pathname.split("/").filter(Boolean);
|
|
78
|
+
if (patternParts.length !== pathnameParts.length) {
|
|
79
|
+
return { matched: false, params: {} };
|
|
80
|
+
}
|
|
81
|
+
const params = {};
|
|
82
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
83
|
+
const pp = patternParts[i];
|
|
84
|
+
const lp = pathnameParts[i];
|
|
85
|
+
if (pp.startsWith(":")) {
|
|
86
|
+
params[pp.slice(1)] = decodeURIComponent(lp);
|
|
87
|
+
} else if (pp !== lp) {
|
|
88
|
+
return { matched: false, params: {} };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (pattern === "/" && pathname !== "/") return { matched: false, params: {} };
|
|
92
|
+
return { matched: true, params };
|
|
93
|
+
}
|
|
94
|
+
function Route({ path, component: Component, children, exact = true }) {
|
|
95
|
+
const ctx = (0, import_react2.useContext)(RouterContext);
|
|
96
|
+
if (!ctx) return null;
|
|
97
|
+
const { matched, params } = matchPath(path, ctx.pathname);
|
|
98
|
+
if (!matched) return null;
|
|
99
|
+
(0, import_react2.useEffect)(() => {
|
|
100
|
+
ctx.params = params;
|
|
101
|
+
});
|
|
102
|
+
if (Component) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Component, { ...params });
|
|
103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/Link.tsx
|
|
107
|
+
var import_react3 = require("react");
|
|
108
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
109
|
+
function Link({
|
|
110
|
+
to,
|
|
111
|
+
children,
|
|
112
|
+
replace = false,
|
|
113
|
+
activeStyle,
|
|
114
|
+
activeClassName,
|
|
115
|
+
style,
|
|
116
|
+
className,
|
|
117
|
+
onClick,
|
|
118
|
+
...rest
|
|
119
|
+
}) {
|
|
120
|
+
const ctx = (0, import_react3.useContext)(RouterContext);
|
|
121
|
+
const isActive = ctx?.pathname === to;
|
|
122
|
+
const handleClick = (e) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
onClick?.(e);
|
|
125
|
+
ctx?.navigate(to, { replace });
|
|
126
|
+
};
|
|
127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
128
|
+
"a",
|
|
129
|
+
{
|
|
130
|
+
href: to,
|
|
131
|
+
onClick: handleClick,
|
|
132
|
+
style: { ...style, ...isActive ? activeStyle : {} },
|
|
133
|
+
className: [className, isActive ? activeClassName : ""].filter(Boolean).join(" ") || void 0,
|
|
134
|
+
...rest,
|
|
135
|
+
children
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/Redirect.tsx
|
|
141
|
+
var import_react5 = require("react");
|
|
142
|
+
|
|
143
|
+
// src/hooks.ts
|
|
144
|
+
var import_react4 = require("react");
|
|
145
|
+
function useNavigate() {
|
|
146
|
+
const ctx = (0, import_react4.useContext)(RouterContext);
|
|
147
|
+
if (!ctx) throw new Error("[WeldRouter] useNavigate must be used inside <WeldRouter>");
|
|
148
|
+
return ctx.navigate;
|
|
149
|
+
}
|
|
150
|
+
function useParams() {
|
|
151
|
+
const ctx = (0, import_react4.useContext)(RouterContext);
|
|
152
|
+
if (!ctx) throw new Error("[WeldRouter] useParams must be used inside <WeldRouter>");
|
|
153
|
+
return ctx.params;
|
|
154
|
+
}
|
|
155
|
+
function useLocation() {
|
|
156
|
+
const ctx = (0, import_react4.useContext)(RouterContext);
|
|
157
|
+
if (!ctx) throw new Error("[WeldRouter] useLocation must be used inside <WeldRouter>");
|
|
158
|
+
return { pathname: ctx.pathname, search: ctx.search, hash: ctx.hash };
|
|
159
|
+
}
|
|
160
|
+
function useSearchParams() {
|
|
161
|
+
const ctx = (0, import_react4.useContext)(RouterContext);
|
|
162
|
+
if (!ctx) throw new Error("[WeldRouter] useSearchParams must be used inside <WeldRouter>");
|
|
163
|
+
return new URLSearchParams(ctx.search);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/Redirect.tsx
|
|
167
|
+
function Redirect({ to, replace = true }) {
|
|
168
|
+
const navigate = useNavigate();
|
|
169
|
+
(0, import_react5.useEffect)(() => {
|
|
170
|
+
navigate(to, { replace });
|
|
171
|
+
}, [to, replace, navigate]);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/ProtectedRoute.tsx
|
|
176
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
177
|
+
function ProtectedRoute({
|
|
178
|
+
isAllowed,
|
|
179
|
+
redirectTo = "/login",
|
|
180
|
+
component: Component,
|
|
181
|
+
children
|
|
182
|
+
}) {
|
|
183
|
+
if (!isAllowed) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Redirect, { to: redirectTo });
|
|
184
|
+
if (Component) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Component, {});
|
|
185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
|
|
186
|
+
}
|
|
187
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
188
|
+
0 && (module.exports = {
|
|
189
|
+
Link,
|
|
190
|
+
ProtectedRoute,
|
|
191
|
+
Redirect,
|
|
192
|
+
Route,
|
|
193
|
+
WeldRouter,
|
|
194
|
+
useLocation,
|
|
195
|
+
useNavigate,
|
|
196
|
+
useParams,
|
|
197
|
+
useRouter,
|
|
198
|
+
useSearchParams
|
|
199
|
+
});
|
|
200
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/Route.tsx","../src/Link.tsx","../src/Redirect.tsx","../src/hooks.ts","../src/ProtectedRoute.tsx"],"sourcesContent":["export { WeldRouter } from './context.js'\nexport type { WeldRouterProps, RouterContextValue, RouteParams } from './context.js'\n\nexport { Route } from './Route.js'\nexport type { RouteProps } from './Route.js'\n\nexport { Link } from './Link.js'\nexport type { WeldLinkProps } from './Link.js'\n\nexport { Redirect } from './Redirect.js'\nexport type { RedirectProps } from './Redirect.js'\n\nexport { ProtectedRoute } from './ProtectedRoute.js'\nexport type { ProtectedRouteProps } from './ProtectedRoute.js'\n\nexport { useNavigate, useParams, useLocation, useSearchParams } from './hooks.js'\n\nexport { useRouter } from './context.js'\n","import React, { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'\n\nexport interface RouteParams {\n [key: string]: string\n}\n\nexport interface RouterContextValue {\n pathname: string\n search: string\n hash: string\n params: RouteParams\n navigate: (to: string, options?: { replace?: boolean; state?: unknown }) => void\n back: () => void\n forward: () => void\n}\n\nexport const RouterContext = createContext<RouterContextValue | null>(null)\n\nexport function useRouter(): RouterContextValue {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] Hooks must be used inside <WeldRouter>')\n return ctx\n}\n\nexport interface WeldRouterProps {\n children: ReactNode\n base?: string\n}\n\nexport function WeldRouter({ children, base = '' }: WeldRouterProps) {\n const getLocation = () => ({\n pathname: window.location.pathname.replace(base, '') || '/',\n search: window.location.search,\n hash: window.location.hash,\n })\n\n const [location, setLocation] = useState(getLocation)\n const [params, setParams] = useState<RouteParams>({})\n\n useEffect(() => {\n const handler = () => setLocation(getLocation())\n window.addEventListener('popstate', handler)\n return () => window.removeEventListener('popstate', handler)\n }, [base])\n\n const navigate = useCallback((to: string, options?: { replace?: boolean; state?: unknown }) => {\n const url = base + to\n if (options?.replace) {\n window.history.replaceState(options.state ?? null, '', url)\n } else {\n window.history.pushState(options?.state ?? null, '', url)\n }\n setLocation(getLocation())\n }, [base])\n\n const back = useCallback(() => window.history.back(), [])\n const forward = useCallback(() => window.history.forward(), [])\n\n return (\n <RouterContext.Provider value={{ ...location, params, navigate, back, forward }}>\n {children}\n </RouterContext.Provider>\n )\n}\n","import React, { useContext, useEffect, type ReactNode, type ComponentType } from 'react'\nimport { RouterContext } from './context.js'\n\nexport interface RouteProps {\n path: string\n component?: ComponentType<Record<string, unknown>>\n children?: ReactNode\n /** Exact match only. Default: true */\n exact?: boolean\n}\n\nfunction matchPath(pattern: string, pathname: string): { matched: boolean; params: Record<string, string> } {\n const patternParts = pattern.split('/').filter(Boolean)\n const pathnameParts = pathname.split('/').filter(Boolean)\n\n if (patternParts.length !== pathnameParts.length) {\n return { matched: false, params: {} }\n }\n\n const params: Record<string, string> = {}\n\n for (let i = 0; i < patternParts.length; i++) {\n const pp = patternParts[i]!\n const lp = pathnameParts[i]!\n if (pp.startsWith(':')) {\n params[pp.slice(1)] = decodeURIComponent(lp)\n } else if (pp !== lp) {\n return { matched: false, params: {} }\n }\n }\n\n // Handle root\n if (pattern === '/' && pathname !== '/') return { matched: false, params: {} }\n\n return { matched: true, params }\n}\n\nexport function Route({ path, component: Component, children, exact = true }: RouteProps) {\n const ctx = useContext(RouterContext)\n if (!ctx) return null\n\n const { matched, params } = matchPath(path, ctx.pathname)\n if (!matched) return null\n\n // Inject params into context — we mutate the shared ref\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n ctx.params = params\n })\n\n if (Component) return <Component {...params} />\n return <>{children}</>\n}\n","import React, { useContext, type AnchorHTMLAttributes, type ReactNode } from 'react'\nimport { RouterContext } from './context.js'\n\nexport interface WeldLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n to: string\n children: ReactNode\n replace?: boolean\n /** Style applied when the link matches the current path */\n activeStyle?: React.CSSProperties\n activeClassName?: string\n}\n\nexport function Link({\n to,\n children,\n replace = false,\n activeStyle,\n activeClassName,\n style,\n className,\n onClick,\n ...rest\n}: WeldLinkProps) {\n const ctx = useContext(RouterContext)\n\n const isActive = ctx?.pathname === to\n\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault()\n onClick?.(e)\n ctx?.navigate(to, { replace })\n }\n\n return (\n <a\n href={to}\n onClick={handleClick}\n style={{ ...style, ...(isActive ? activeStyle : {}) }}\n className={[className, isActive ? activeClassName : ''].filter(Boolean).join(' ') || undefined}\n {...rest}\n >\n {children}\n </a>\n )\n}\n","import { useEffect } from 'react'\nimport { useNavigate } from './hooks.js'\n\nexport interface RedirectProps {\n to: string\n replace?: boolean\n}\n\nexport function Redirect({ to, replace = true }: RedirectProps) {\n const navigate = useNavigate()\n useEffect(() => { navigate(to, { replace }) }, [to, replace, navigate])\n return null\n}\n","import { useContext } from 'react'\nimport { RouterContext } from './context.js'\n\nexport function useNavigate() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useNavigate must be used inside <WeldRouter>')\n return ctx.navigate\n}\n\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useParams must be used inside <WeldRouter>')\n return ctx.params as T\n}\n\nexport function useLocation() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useLocation must be used inside <WeldRouter>')\n return { pathname: ctx.pathname, search: ctx.search, hash: ctx.hash }\n}\n\nexport function useSearchParams(): URLSearchParams {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useSearchParams must be used inside <WeldRouter>')\n return new URLSearchParams(ctx.search)\n}\n","import React, { type ReactNode, type ComponentType } from 'react'\nimport { Redirect } from './Redirect.js'\n\nexport interface ProtectedRouteProps {\n /** If false, redirects to `redirectTo` */\n isAllowed: boolean\n redirectTo?: string\n component?: ComponentType<Record<string, unknown>>\n children?: ReactNode\n}\n\nexport function ProtectedRoute({\n isAllowed,\n redirectTo = '/login',\n component: Component,\n children,\n}: ProtectedRouteProps) {\n if (!isAllowed) return <Redirect to={redirectTo} />\n if (Component) return <Component />\n return <>{children}</>\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { ReactNode, ComponentType, AnchorHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
interface RouteParams {
|
|
4
|
+
[key: string]: string;
|
|
5
|
+
}
|
|
6
|
+
interface RouterContextValue {
|
|
7
|
+
pathname: string;
|
|
8
|
+
search: string;
|
|
9
|
+
hash: string;
|
|
10
|
+
params: RouteParams;
|
|
11
|
+
navigate: (to: string, options?: {
|
|
12
|
+
replace?: boolean;
|
|
13
|
+
state?: unknown;
|
|
14
|
+
}) => void;
|
|
15
|
+
back: () => void;
|
|
16
|
+
forward: () => void;
|
|
17
|
+
}
|
|
18
|
+
declare function useRouter(): RouterContextValue;
|
|
19
|
+
interface WeldRouterProps {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
base?: string;
|
|
22
|
+
}
|
|
23
|
+
declare function WeldRouter({ children, base }: WeldRouterProps): React.JSX.Element;
|
|
24
|
+
|
|
25
|
+
interface RouteProps {
|
|
26
|
+
path: string;
|
|
27
|
+
component?: ComponentType<Record<string, unknown>>;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
/** Exact match only. Default: true */
|
|
30
|
+
exact?: boolean;
|
|
31
|
+
}
|
|
32
|
+
declare function Route({ path, component: Component, children, exact }: RouteProps): React.JSX.Element | null;
|
|
33
|
+
|
|
34
|
+
interface WeldLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
35
|
+
to: string;
|
|
36
|
+
children: ReactNode;
|
|
37
|
+
replace?: boolean;
|
|
38
|
+
/** Style applied when the link matches the current path */
|
|
39
|
+
activeStyle?: React.CSSProperties;
|
|
40
|
+
activeClassName?: string;
|
|
41
|
+
}
|
|
42
|
+
declare function Link({ to, children, replace, activeStyle, activeClassName, style, className, onClick, ...rest }: WeldLinkProps): React.JSX.Element;
|
|
43
|
+
|
|
44
|
+
interface RedirectProps {
|
|
45
|
+
to: string;
|
|
46
|
+
replace?: boolean;
|
|
47
|
+
}
|
|
48
|
+
declare function Redirect({ to, replace }: RedirectProps): null;
|
|
49
|
+
|
|
50
|
+
interface ProtectedRouteProps {
|
|
51
|
+
/** If false, redirects to `redirectTo` */
|
|
52
|
+
isAllowed: boolean;
|
|
53
|
+
redirectTo?: string;
|
|
54
|
+
component?: ComponentType<Record<string, unknown>>;
|
|
55
|
+
children?: ReactNode;
|
|
56
|
+
}
|
|
57
|
+
declare function ProtectedRoute({ isAllowed, redirectTo, component: Component, children, }: ProtectedRouteProps): React.JSX.Element;
|
|
58
|
+
|
|
59
|
+
declare function useNavigate(): (to: string, options?: {
|
|
60
|
+
replace?: boolean;
|
|
61
|
+
state?: unknown;
|
|
62
|
+
}) => void;
|
|
63
|
+
declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
|
|
64
|
+
declare function useLocation(): {
|
|
65
|
+
pathname: string;
|
|
66
|
+
search: string;
|
|
67
|
+
hash: string;
|
|
68
|
+
};
|
|
69
|
+
declare function useSearchParams(): URLSearchParams;
|
|
70
|
+
|
|
71
|
+
export { Link, ProtectedRoute, type ProtectedRouteProps, Redirect, type RedirectProps, Route, type RouteParams, type RouteProps, type RouterContextValue, type WeldLinkProps, WeldRouter, type WeldRouterProps, useLocation, useNavigate, useParams, useRouter, useSearchParams };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { ReactNode, ComponentType, AnchorHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
interface RouteParams {
|
|
4
|
+
[key: string]: string;
|
|
5
|
+
}
|
|
6
|
+
interface RouterContextValue {
|
|
7
|
+
pathname: string;
|
|
8
|
+
search: string;
|
|
9
|
+
hash: string;
|
|
10
|
+
params: RouteParams;
|
|
11
|
+
navigate: (to: string, options?: {
|
|
12
|
+
replace?: boolean;
|
|
13
|
+
state?: unknown;
|
|
14
|
+
}) => void;
|
|
15
|
+
back: () => void;
|
|
16
|
+
forward: () => void;
|
|
17
|
+
}
|
|
18
|
+
declare function useRouter(): RouterContextValue;
|
|
19
|
+
interface WeldRouterProps {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
base?: string;
|
|
22
|
+
}
|
|
23
|
+
declare function WeldRouter({ children, base }: WeldRouterProps): React.JSX.Element;
|
|
24
|
+
|
|
25
|
+
interface RouteProps {
|
|
26
|
+
path: string;
|
|
27
|
+
component?: ComponentType<Record<string, unknown>>;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
/** Exact match only. Default: true */
|
|
30
|
+
exact?: boolean;
|
|
31
|
+
}
|
|
32
|
+
declare function Route({ path, component: Component, children, exact }: RouteProps): React.JSX.Element | null;
|
|
33
|
+
|
|
34
|
+
interface WeldLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
35
|
+
to: string;
|
|
36
|
+
children: ReactNode;
|
|
37
|
+
replace?: boolean;
|
|
38
|
+
/** Style applied when the link matches the current path */
|
|
39
|
+
activeStyle?: React.CSSProperties;
|
|
40
|
+
activeClassName?: string;
|
|
41
|
+
}
|
|
42
|
+
declare function Link({ to, children, replace, activeStyle, activeClassName, style, className, onClick, ...rest }: WeldLinkProps): React.JSX.Element;
|
|
43
|
+
|
|
44
|
+
interface RedirectProps {
|
|
45
|
+
to: string;
|
|
46
|
+
replace?: boolean;
|
|
47
|
+
}
|
|
48
|
+
declare function Redirect({ to, replace }: RedirectProps): null;
|
|
49
|
+
|
|
50
|
+
interface ProtectedRouteProps {
|
|
51
|
+
/** If false, redirects to `redirectTo` */
|
|
52
|
+
isAllowed: boolean;
|
|
53
|
+
redirectTo?: string;
|
|
54
|
+
component?: ComponentType<Record<string, unknown>>;
|
|
55
|
+
children?: ReactNode;
|
|
56
|
+
}
|
|
57
|
+
declare function ProtectedRoute({ isAllowed, redirectTo, component: Component, children, }: ProtectedRouteProps): React.JSX.Element;
|
|
58
|
+
|
|
59
|
+
declare function useNavigate(): (to: string, options?: {
|
|
60
|
+
replace?: boolean;
|
|
61
|
+
state?: unknown;
|
|
62
|
+
}) => void;
|
|
63
|
+
declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
|
|
64
|
+
declare function useLocation(): {
|
|
65
|
+
pathname: string;
|
|
66
|
+
search: string;
|
|
67
|
+
hash: string;
|
|
68
|
+
};
|
|
69
|
+
declare function useSearchParams(): URLSearchParams;
|
|
70
|
+
|
|
71
|
+
export { Link, ProtectedRoute, type ProtectedRouteProps, Redirect, type RedirectProps, Route, type RouteParams, type RouteProps, type RouterContextValue, type WeldLinkProps, WeldRouter, type WeldRouterProps, useLocation, useNavigate, useParams, useRouter, useSearchParams };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// src/context.tsx
|
|
2
|
+
import { createContext, useContext, useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
var RouterContext = createContext(null);
|
|
5
|
+
function useRouter() {
|
|
6
|
+
const ctx = useContext(RouterContext);
|
|
7
|
+
if (!ctx) throw new Error("[WeldRouter] Hooks must be used inside <WeldRouter>");
|
|
8
|
+
return ctx;
|
|
9
|
+
}
|
|
10
|
+
function WeldRouter({ children, base = "" }) {
|
|
11
|
+
const getLocation = () => ({
|
|
12
|
+
pathname: window.location.pathname.replace(base, "") || "/",
|
|
13
|
+
search: window.location.search,
|
|
14
|
+
hash: window.location.hash
|
|
15
|
+
});
|
|
16
|
+
const [location, setLocation] = useState(getLocation);
|
|
17
|
+
const [params, setParams] = useState({});
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const handler = () => setLocation(getLocation());
|
|
20
|
+
window.addEventListener("popstate", handler);
|
|
21
|
+
return () => window.removeEventListener("popstate", handler);
|
|
22
|
+
}, [base]);
|
|
23
|
+
const navigate = useCallback((to, options) => {
|
|
24
|
+
const url = base + to;
|
|
25
|
+
if (options?.replace) {
|
|
26
|
+
window.history.replaceState(options.state ?? null, "", url);
|
|
27
|
+
} else {
|
|
28
|
+
window.history.pushState(options?.state ?? null, "", url);
|
|
29
|
+
}
|
|
30
|
+
setLocation(getLocation());
|
|
31
|
+
}, [base]);
|
|
32
|
+
const back = useCallback(() => window.history.back(), []);
|
|
33
|
+
const forward = useCallback(() => window.history.forward(), []);
|
|
34
|
+
return /* @__PURE__ */ jsx(RouterContext.Provider, { value: { ...location, params, navigate, back, forward }, children });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/Route.tsx
|
|
38
|
+
import { useContext as useContext2, useEffect as useEffect2 } from "react";
|
|
39
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
40
|
+
function matchPath(pattern, pathname) {
|
|
41
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
42
|
+
const pathnameParts = pathname.split("/").filter(Boolean);
|
|
43
|
+
if (patternParts.length !== pathnameParts.length) {
|
|
44
|
+
return { matched: false, params: {} };
|
|
45
|
+
}
|
|
46
|
+
const params = {};
|
|
47
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
48
|
+
const pp = patternParts[i];
|
|
49
|
+
const lp = pathnameParts[i];
|
|
50
|
+
if (pp.startsWith(":")) {
|
|
51
|
+
params[pp.slice(1)] = decodeURIComponent(lp);
|
|
52
|
+
} else if (pp !== lp) {
|
|
53
|
+
return { matched: false, params: {} };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (pattern === "/" && pathname !== "/") return { matched: false, params: {} };
|
|
57
|
+
return { matched: true, params };
|
|
58
|
+
}
|
|
59
|
+
function Route({ path, component: Component, children, exact = true }) {
|
|
60
|
+
const ctx = useContext2(RouterContext);
|
|
61
|
+
if (!ctx) return null;
|
|
62
|
+
const { matched, params } = matchPath(path, ctx.pathname);
|
|
63
|
+
if (!matched) return null;
|
|
64
|
+
useEffect2(() => {
|
|
65
|
+
ctx.params = params;
|
|
66
|
+
});
|
|
67
|
+
if (Component) return /* @__PURE__ */ jsx2(Component, { ...params });
|
|
68
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/Link.tsx
|
|
72
|
+
import { useContext as useContext3 } from "react";
|
|
73
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
74
|
+
function Link({
|
|
75
|
+
to,
|
|
76
|
+
children,
|
|
77
|
+
replace = false,
|
|
78
|
+
activeStyle,
|
|
79
|
+
activeClassName,
|
|
80
|
+
style,
|
|
81
|
+
className,
|
|
82
|
+
onClick,
|
|
83
|
+
...rest
|
|
84
|
+
}) {
|
|
85
|
+
const ctx = useContext3(RouterContext);
|
|
86
|
+
const isActive = ctx?.pathname === to;
|
|
87
|
+
const handleClick = (e) => {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
onClick?.(e);
|
|
90
|
+
ctx?.navigate(to, { replace });
|
|
91
|
+
};
|
|
92
|
+
return /* @__PURE__ */ jsx3(
|
|
93
|
+
"a",
|
|
94
|
+
{
|
|
95
|
+
href: to,
|
|
96
|
+
onClick: handleClick,
|
|
97
|
+
style: { ...style, ...isActive ? activeStyle : {} },
|
|
98
|
+
className: [className, isActive ? activeClassName : ""].filter(Boolean).join(" ") || void 0,
|
|
99
|
+
...rest,
|
|
100
|
+
children
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/Redirect.tsx
|
|
106
|
+
import { useEffect as useEffect3 } from "react";
|
|
107
|
+
|
|
108
|
+
// src/hooks.ts
|
|
109
|
+
import { useContext as useContext4 } from "react";
|
|
110
|
+
function useNavigate() {
|
|
111
|
+
const ctx = useContext4(RouterContext);
|
|
112
|
+
if (!ctx) throw new Error("[WeldRouter] useNavigate must be used inside <WeldRouter>");
|
|
113
|
+
return ctx.navigate;
|
|
114
|
+
}
|
|
115
|
+
function useParams() {
|
|
116
|
+
const ctx = useContext4(RouterContext);
|
|
117
|
+
if (!ctx) throw new Error("[WeldRouter] useParams must be used inside <WeldRouter>");
|
|
118
|
+
return ctx.params;
|
|
119
|
+
}
|
|
120
|
+
function useLocation() {
|
|
121
|
+
const ctx = useContext4(RouterContext);
|
|
122
|
+
if (!ctx) throw new Error("[WeldRouter] useLocation must be used inside <WeldRouter>");
|
|
123
|
+
return { pathname: ctx.pathname, search: ctx.search, hash: ctx.hash };
|
|
124
|
+
}
|
|
125
|
+
function useSearchParams() {
|
|
126
|
+
const ctx = useContext4(RouterContext);
|
|
127
|
+
if (!ctx) throw new Error("[WeldRouter] useSearchParams must be used inside <WeldRouter>");
|
|
128
|
+
return new URLSearchParams(ctx.search);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/Redirect.tsx
|
|
132
|
+
function Redirect({ to, replace = true }) {
|
|
133
|
+
const navigate = useNavigate();
|
|
134
|
+
useEffect3(() => {
|
|
135
|
+
navigate(to, { replace });
|
|
136
|
+
}, [to, replace, navigate]);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/ProtectedRoute.tsx
|
|
141
|
+
import { Fragment as Fragment2, jsx as jsx4 } from "react/jsx-runtime";
|
|
142
|
+
function ProtectedRoute({
|
|
143
|
+
isAllowed,
|
|
144
|
+
redirectTo = "/login",
|
|
145
|
+
component: Component,
|
|
146
|
+
children
|
|
147
|
+
}) {
|
|
148
|
+
if (!isAllowed) return /* @__PURE__ */ jsx4(Redirect, { to: redirectTo });
|
|
149
|
+
if (Component) return /* @__PURE__ */ jsx4(Component, {});
|
|
150
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children });
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
Link,
|
|
154
|
+
ProtectedRoute,
|
|
155
|
+
Redirect,
|
|
156
|
+
Route,
|
|
157
|
+
WeldRouter,
|
|
158
|
+
useLocation,
|
|
159
|
+
useNavigate,
|
|
160
|
+
useParams,
|
|
161
|
+
useRouter,
|
|
162
|
+
useSearchParams
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/Route.tsx","../src/Link.tsx","../src/Redirect.tsx","../src/hooks.ts","../src/ProtectedRoute.tsx"],"sourcesContent":["import React, { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'\n\nexport interface RouteParams {\n [key: string]: string\n}\n\nexport interface RouterContextValue {\n pathname: string\n search: string\n hash: string\n params: RouteParams\n navigate: (to: string, options?: { replace?: boolean; state?: unknown }) => void\n back: () => void\n forward: () => void\n}\n\nexport const RouterContext = createContext<RouterContextValue | null>(null)\n\nexport function useRouter(): RouterContextValue {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] Hooks must be used inside <WeldRouter>')\n return ctx\n}\n\nexport interface WeldRouterProps {\n children: ReactNode\n base?: string\n}\n\nexport function WeldRouter({ children, base = '' }: WeldRouterProps) {\n const getLocation = () => ({\n pathname: window.location.pathname.replace(base, '') || '/',\n search: window.location.search,\n hash: window.location.hash,\n })\n\n const [location, setLocation] = useState(getLocation)\n const [params, setParams] = useState<RouteParams>({})\n\n useEffect(() => {\n const handler = () => setLocation(getLocation())\n window.addEventListener('popstate', handler)\n return () => window.removeEventListener('popstate', handler)\n }, [base])\n\n const navigate = useCallback((to: string, options?: { replace?: boolean; state?: unknown }) => {\n const url = base + to\n if (options?.replace) {\n window.history.replaceState(options.state ?? null, '', url)\n } else {\n window.history.pushState(options?.state ?? null, '', url)\n }\n setLocation(getLocation())\n }, [base])\n\n const back = useCallback(() => window.history.back(), [])\n const forward = useCallback(() => window.history.forward(), [])\n\n return (\n <RouterContext.Provider value={{ ...location, params, navigate, back, forward }}>\n {children}\n </RouterContext.Provider>\n )\n}\n","import React, { useContext, useEffect, type ReactNode, type ComponentType } from 'react'\nimport { RouterContext } from './context.js'\n\nexport interface RouteProps {\n path: string\n component?: ComponentType<Record<string, unknown>>\n children?: ReactNode\n /** Exact match only. Default: true */\n exact?: boolean\n}\n\nfunction matchPath(pattern: string, pathname: string): { matched: boolean; params: Record<string, string> } {\n const patternParts = pattern.split('/').filter(Boolean)\n const pathnameParts = pathname.split('/').filter(Boolean)\n\n if (patternParts.length !== pathnameParts.length) {\n return { matched: false, params: {} }\n }\n\n const params: Record<string, string> = {}\n\n for (let i = 0; i < patternParts.length; i++) {\n const pp = patternParts[i]!\n const lp = pathnameParts[i]!\n if (pp.startsWith(':')) {\n params[pp.slice(1)] = decodeURIComponent(lp)\n } else if (pp !== lp) {\n return { matched: false, params: {} }\n }\n }\n\n // Handle root\n if (pattern === '/' && pathname !== '/') return { matched: false, params: {} }\n\n return { matched: true, params }\n}\n\nexport function Route({ path, component: Component, children, exact = true }: RouteProps) {\n const ctx = useContext(RouterContext)\n if (!ctx) return null\n\n const { matched, params } = matchPath(path, ctx.pathname)\n if (!matched) return null\n\n // Inject params into context — we mutate the shared ref\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n ctx.params = params\n })\n\n if (Component) return <Component {...params} />\n return <>{children}</>\n}\n","import React, { useContext, type AnchorHTMLAttributes, type ReactNode } from 'react'\nimport { RouterContext } from './context.js'\n\nexport interface WeldLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n to: string\n children: ReactNode\n replace?: boolean\n /** Style applied when the link matches the current path */\n activeStyle?: React.CSSProperties\n activeClassName?: string\n}\n\nexport function Link({\n to,\n children,\n replace = false,\n activeStyle,\n activeClassName,\n style,\n className,\n onClick,\n ...rest\n}: WeldLinkProps) {\n const ctx = useContext(RouterContext)\n\n const isActive = ctx?.pathname === to\n\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault()\n onClick?.(e)\n ctx?.navigate(to, { replace })\n }\n\n return (\n <a\n href={to}\n onClick={handleClick}\n style={{ ...style, ...(isActive ? activeStyle : {}) }}\n className={[className, isActive ? activeClassName : ''].filter(Boolean).join(' ') || undefined}\n {...rest}\n >\n {children}\n </a>\n )\n}\n","import { useEffect } from 'react'\nimport { useNavigate } from './hooks.js'\n\nexport interface RedirectProps {\n to: string\n replace?: boolean\n}\n\nexport function Redirect({ to, replace = true }: RedirectProps) {\n const navigate = useNavigate()\n useEffect(() => { navigate(to, { replace }) }, [to, replace, navigate])\n return null\n}\n","import { useContext } from 'react'\nimport { RouterContext } from './context.js'\n\nexport function useNavigate() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useNavigate must be used inside <WeldRouter>')\n return ctx.navigate\n}\n\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useParams must be used inside <WeldRouter>')\n return ctx.params as T\n}\n\nexport function useLocation() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useLocation must be used inside <WeldRouter>')\n return { pathname: ctx.pathname, search: ctx.search, hash: ctx.hash }\n}\n\nexport function useSearchParams(): URLSearchParams {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error('[WeldRouter] useSearchParams must be used inside <WeldRouter>')\n return new URLSearchParams(ctx.search)\n}\n","import React, { type ReactNode, type ComponentType } from 'react'\nimport { Redirect } from './Redirect.js'\n\nexport interface ProtectedRouteProps {\n /** If false, redirects to `redirectTo` */\n isAllowed: boolean\n redirectTo?: string\n component?: ComponentType<Record<string, unknown>>\n children?: ReactNode\n}\n\nexport function ProtectedRoute({\n isAllowed,\n redirectTo = '/login',\n component: Component,\n children,\n}: ProtectedRouteProps) {\n if (!isAllowed) return <Redirect to={redirectTo} />\n if (Component) return <Component />\n return <>{children}</>\n}\n"],"mappings":"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","names":["useContext","useEffect","jsx","useContext","useEffect","useContext","jsx","useContext","useEffect","useContext","useContext","useEffect","Fragment","jsx"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weldjs/router",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "WELD client-side router — file-based and declarative SPA routing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"lint": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "pnpm build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@weldjs/core": "^0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "^18.0.0",
|
|
34
|
+
"react": "^18.0.0",
|
|
35
|
+
"tsup": "^8.0.0",
|
|
36
|
+
"typescript": "^5.4.0"
|
|
37
|
+
}
|
|
38
|
+
}
|