@bromscandium/router 1.0.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/LICENSE +21 -0
- package/README.md +91 -0
- package/dist/components.d.ts +129 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +287 -0
- package/dist/components.js.map +1 -0
- package/dist/hooks.d.ts +136 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +167 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +159 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +282 -0
- package/dist/router.js.map +1 -0
- package/package.json +42 -0
- package/src/components.tsx +411 -0
- package/src/hooks.ts +180 -0
- package/src/index.ts +31 -0
- package/src/router.ts +448 -0
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router hooks for components.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import { computed } from '@bromscandium/core';
|
|
6
|
+
import { getRouter } from './router.js';
|
|
7
|
+
/**
|
|
8
|
+
* Returns the router instance.
|
|
9
|
+
* Must be called within a component where a router has been created.
|
|
10
|
+
*
|
|
11
|
+
* @returns The router instance
|
|
12
|
+
* @throws Error if called outside of router context
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* function MyComponent() {
|
|
17
|
+
* const router = useRouter();
|
|
18
|
+
*
|
|
19
|
+
* function handleClick() {
|
|
20
|
+
* router.push('/dashboard');
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* return <button onClick={handleClick}>Go to Dashboard</button>;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function useRouter() {
|
|
28
|
+
const router = getRouter();
|
|
29
|
+
if (!router) {
|
|
30
|
+
throw new Error('useRouter() called outside of router context. ' +
|
|
31
|
+
'Make sure you have created a router with createRouter() and mounted it.');
|
|
32
|
+
}
|
|
33
|
+
return router;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns a reactive reference to the current route location.
|
|
37
|
+
*
|
|
38
|
+
* @returns A ref containing the current RouteLocation
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* function Breadcrumb() {
|
|
43
|
+
* const route = useRoute();
|
|
44
|
+
*
|
|
45
|
+
* return <span>Current path: {route.value.path}</span>;
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function useRoute() {
|
|
50
|
+
const router = useRouter();
|
|
51
|
+
return router.currentRoute;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns a computed ref of the current route parameters.
|
|
55
|
+
*
|
|
56
|
+
* @returns A computed ref containing route params
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* // For route /users/:id
|
|
61
|
+
* function UserProfile() {
|
|
62
|
+
* const params = useParams();
|
|
63
|
+
*
|
|
64
|
+
* return <div>User ID: {params.value.id}</div>;
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function useParams() {
|
|
69
|
+
const route = useRoute();
|
|
70
|
+
return computed(() => route.value.params);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns a computed ref of the current query string parameters.
|
|
74
|
+
*
|
|
75
|
+
* @returns A computed ref containing query params
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* // For URL /search?q=hello
|
|
80
|
+
* function SearchResults() {
|
|
81
|
+
* const query = useQuery();
|
|
82
|
+
*
|
|
83
|
+
* return <div>Searching for: {query.value.q}</div>;
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export function useQuery() {
|
|
88
|
+
const route = useRoute();
|
|
89
|
+
return computed(() => route.value.query);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns a computed ref of the merged metadata from all matched routes.
|
|
93
|
+
*
|
|
94
|
+
* @returns A computed ref containing route metadata
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* function PageTitle() {
|
|
99
|
+
* const meta = useRouteMeta();
|
|
100
|
+
*
|
|
101
|
+
* return <title>{meta.value.title || 'My App'}</title>;
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export function useRouteMeta() {
|
|
106
|
+
const route = useRoute();
|
|
107
|
+
return computed(() => route.value.meta);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns navigation helper functions from the router.
|
|
111
|
+
*
|
|
112
|
+
* @returns An object with push, replace, back, forward, and go methods
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* function Navigation() {
|
|
117
|
+
* const { push, back } = useNavigate();
|
|
118
|
+
*
|
|
119
|
+
* return (
|
|
120
|
+
* <>
|
|
121
|
+
* <button onClick={back}>Back</button>
|
|
122
|
+
* <button onClick={() => push('/home')}>Home</button>
|
|
123
|
+
* </>
|
|
124
|
+
* );
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function useNavigate() {
|
|
129
|
+
const router = useRouter();
|
|
130
|
+
return {
|
|
131
|
+
push: router.push,
|
|
132
|
+
replace: router.replace,
|
|
133
|
+
back: router.back,
|
|
134
|
+
forward: router.forward,
|
|
135
|
+
go: router.go,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Returns a computed boolean indicating if the current route matches a path.
|
|
140
|
+
*
|
|
141
|
+
* @param path - A string path or RegExp to match against
|
|
142
|
+
* @returns A computed ref that is true when the route matches
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* function NavLink({ to, children }) {
|
|
147
|
+
* const isActive = useRouteMatch(to);
|
|
148
|
+
*
|
|
149
|
+
* return (
|
|
150
|
+
* <a href={to} className={isActive.value ? 'active' : ''}>
|
|
151
|
+
* {children}
|
|
152
|
+
* </a>
|
|
153
|
+
* );
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function useRouteMatch(path) {
|
|
158
|
+
const route = useRoute();
|
|
159
|
+
return computed(() => {
|
|
160
|
+
const currentPath = route.value.path;
|
|
161
|
+
if (typeof path === 'string') {
|
|
162
|
+
return currentPath === path || currentPath.startsWith(path + '/');
|
|
163
|
+
}
|
|
164
|
+
return path.test(currentPath);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAoB,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAyB,MAAM,aAAa,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,gDAAgD;YAChD,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,MAAM,CAAC,YAAY,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,EAAE,EAAE,MAAM,CAAC,EAAE;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAAC,IAAqB;IACjD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IAEzB,OAAO,QAAQ,CAAC,GAAG,EAAE;QACnB,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;QAErC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,WAAW,KAAK,IAAI,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createRouter, getRouter, setRouter, type Router, type Route, type RouteLocation, type MatchedRoute, type NavigationTarget, type NavigationGuard, type NavigationHook, } from './router.js';
|
|
2
|
+
export { useRouter, useRoute, useParams, useQuery, useRouteMeta, useNavigate, useRouteMatch, } from './hooks.js';
|
|
3
|
+
export { Link, RouterView, Redirect, Navigate, } from './components.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,SAAS,EACT,SAAS,EACT,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,QAAQ,GACT,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Router package exports
|
|
2
|
+
export { createRouter, getRouter, setRouter, } from './router.js';
|
|
3
|
+
export { useRouter, useRoute, useParams, useQuery, useRouteMeta, useNavigate, useRouteMatch, } from './hooks.js';
|
|
4
|
+
export { Link, RouterView, Redirect, Navigate, } from './components.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yBAAyB;AAEzB,OAAO,EACL,YAAY,EACZ,SAAS,EACT,SAAS,GAQV,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,QAAQ,GACT,MAAM,iBAAiB,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router implementation with file-based routing support.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import { Ref } from '@bromscandium/core';
|
|
6
|
+
/**
|
|
7
|
+
* A route configuration object.
|
|
8
|
+
*/
|
|
9
|
+
export interface Route {
|
|
10
|
+
/** The path pattern to match (supports `:param` and `[param]` syntax) */
|
|
11
|
+
path: string;
|
|
12
|
+
/** The component to render, can be async for lazy loading */
|
|
13
|
+
component: () => Promise<{
|
|
14
|
+
default: any;
|
|
15
|
+
}> | {
|
|
16
|
+
default: any;
|
|
17
|
+
};
|
|
18
|
+
/** Optional layout component to wrap the page */
|
|
19
|
+
layout?: () => Promise<{
|
|
20
|
+
default: any;
|
|
21
|
+
}> | {
|
|
22
|
+
default: any;
|
|
23
|
+
};
|
|
24
|
+
/** Child routes for nested routing */
|
|
25
|
+
children?: Route[];
|
|
26
|
+
/** Arbitrary metadata attached to the route */
|
|
27
|
+
meta?: Record<string, any>;
|
|
28
|
+
/** Named route identifier for programmatic navigation */
|
|
29
|
+
name?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The current location state.
|
|
33
|
+
*/
|
|
34
|
+
export interface RouteLocation {
|
|
35
|
+
/** The current path without query or hash */
|
|
36
|
+
path: string;
|
|
37
|
+
/** Dynamic route parameters extracted from the path */
|
|
38
|
+
params: Record<string, string>;
|
|
39
|
+
/** Query string parameters */
|
|
40
|
+
query: Record<string, string>;
|
|
41
|
+
/** Hash fragment (without #) */
|
|
42
|
+
hash: string;
|
|
43
|
+
/** All matched route records for the current location */
|
|
44
|
+
matched: MatchedRoute[];
|
|
45
|
+
/** Full path including query and hash */
|
|
46
|
+
fullPath: string;
|
|
47
|
+
/** Name of the matched route if defined */
|
|
48
|
+
name?: string;
|
|
49
|
+
/** Merged metadata from all matched routes */
|
|
50
|
+
meta: Record<string, any>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A matched route record with extracted parameters.
|
|
54
|
+
*/
|
|
55
|
+
export interface MatchedRoute {
|
|
56
|
+
/** The route configuration */
|
|
57
|
+
route: Route;
|
|
58
|
+
/** Parameters extracted from the path */
|
|
59
|
+
params: Record<string, string>;
|
|
60
|
+
/** The resolved path for this route */
|
|
61
|
+
path: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The router instance providing navigation and route state.
|
|
65
|
+
*/
|
|
66
|
+
export interface Router {
|
|
67
|
+
/** Reactive reference to the current route location */
|
|
68
|
+
currentRoute: Ref<RouteLocation>;
|
|
69
|
+
/** Navigate to a new location, adding to history */
|
|
70
|
+
push(to: string | NavigationTarget): Promise<void>;
|
|
71
|
+
/** Navigate to a new location, replacing current history entry */
|
|
72
|
+
replace(to: string | NavigationTarget): Promise<void>;
|
|
73
|
+
/** Navigate back in history */
|
|
74
|
+
back(): void;
|
|
75
|
+
/** Navigate forward in history */
|
|
76
|
+
forward(): void;
|
|
77
|
+
/** Navigate by a relative history position */
|
|
78
|
+
go(delta: number): void;
|
|
79
|
+
/** Register a navigation guard called before navigation */
|
|
80
|
+
beforeEach(guard: NavigationGuard): () => void;
|
|
81
|
+
/** Register a hook called after navigation completes */
|
|
82
|
+
afterEach(hook: NavigationHook): () => void;
|
|
83
|
+
/** Returns a promise that resolves when router is ready */
|
|
84
|
+
isReady(): Promise<void>;
|
|
85
|
+
/** The configured routes */
|
|
86
|
+
routes: Route[];
|
|
87
|
+
/** The base path for all routes */
|
|
88
|
+
base: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A navigation target for programmatic navigation.
|
|
92
|
+
*/
|
|
93
|
+
export interface NavigationTarget {
|
|
94
|
+
/** Target path */
|
|
95
|
+
path?: string;
|
|
96
|
+
/** Named route to navigate to */
|
|
97
|
+
name?: string;
|
|
98
|
+
/** Route parameters for dynamic segments */
|
|
99
|
+
params?: Record<string, string>;
|
|
100
|
+
/** Query parameters */
|
|
101
|
+
query?: Record<string, string>;
|
|
102
|
+
/** Hash fragment */
|
|
103
|
+
hash?: string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A navigation guard function that can block or redirect navigation.
|
|
107
|
+
* Return false to cancel, string/NavigationTarget to redirect, or true to proceed.
|
|
108
|
+
*/
|
|
109
|
+
export type NavigationGuard = (to: RouteLocation, from: RouteLocation) => boolean | string | NavigationTarget | Promise<boolean | string | NavigationTarget>;
|
|
110
|
+
/**
|
|
111
|
+
* A hook called after navigation completes.
|
|
112
|
+
*/
|
|
113
|
+
export type NavigationHook = (to: RouteLocation, from: RouteLocation) => void;
|
|
114
|
+
interface RouterOptions {
|
|
115
|
+
/** Route configurations */
|
|
116
|
+
routes: Route[];
|
|
117
|
+
/** Base path prepended to all routes */
|
|
118
|
+
base?: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Sets the current router instance for global access.
|
|
122
|
+
*
|
|
123
|
+
* @param router - The router instance or null to clear
|
|
124
|
+
*/
|
|
125
|
+
export declare function setRouter(router: Router | null): void;
|
|
126
|
+
/**
|
|
127
|
+
* Gets the current router instance.
|
|
128
|
+
*
|
|
129
|
+
* @returns The current router or null if not set
|
|
130
|
+
*/
|
|
131
|
+
export declare function getRouter(): Router | null;
|
|
132
|
+
/**
|
|
133
|
+
* Creates a new router instance with the given configuration.
|
|
134
|
+
*
|
|
135
|
+
* @param options - Router configuration including routes and optional base path
|
|
136
|
+
* @returns A configured router instance
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const router = createRouter({
|
|
141
|
+
* base: '/app',
|
|
142
|
+
* routes: [
|
|
143
|
+
* { path: '/', component: () => import('./pages/Home') },
|
|
144
|
+
* { path: '/users/:id', component: () => import('./pages/User') },
|
|
145
|
+
* ]
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* // Use navigation guards
|
|
149
|
+
* router.beforeEach((to, from) => {
|
|
150
|
+
* if (!isAuthenticated && to.path !== '/login') {
|
|
151
|
+
* return '/login';
|
|
152
|
+
* }
|
|
153
|
+
* return true;
|
|
154
|
+
* });
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function createRouter(options: RouterOptions): Router;
|
|
158
|
+
export {};
|
|
159
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router implementation with file-based routing support.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import { ref } from '@bromscandium/core';
|
|
6
|
+
let currentRouter = null;
|
|
7
|
+
/**
|
|
8
|
+
* Sets the current router instance for global access.
|
|
9
|
+
*
|
|
10
|
+
* @param router - The router instance or null to clear
|
|
11
|
+
*/
|
|
12
|
+
export function setRouter(router) {
|
|
13
|
+
currentRouter = router;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Gets the current router instance.
|
|
17
|
+
*
|
|
18
|
+
* @returns The current router or null if not set
|
|
19
|
+
*/
|
|
20
|
+
export function getRouter() {
|
|
21
|
+
return currentRouter;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new router instance with the given configuration.
|
|
25
|
+
*
|
|
26
|
+
* @param options - Router configuration including routes and optional base path
|
|
27
|
+
* @returns A configured router instance
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const router = createRouter({
|
|
32
|
+
* base: '/app',
|
|
33
|
+
* routes: [
|
|
34
|
+
* { path: '/', component: () => import('./pages/Home') },
|
|
35
|
+
* { path: '/users/:id', component: () => import('./pages/User') },
|
|
36
|
+
* ]
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* // Use navigation guards
|
|
40
|
+
* router.beforeEach((to, from) => {
|
|
41
|
+
* if (!isAuthenticated && to.path !== '/login') {
|
|
42
|
+
* return '/login';
|
|
43
|
+
* }
|
|
44
|
+
* return true;
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function createRouter(options) {
|
|
49
|
+
const { routes, base = '' } = options;
|
|
50
|
+
const currentRoute = ref(createEmptyLocation());
|
|
51
|
+
const beforeGuards = [];
|
|
52
|
+
const afterHooks = [];
|
|
53
|
+
let isReady = false;
|
|
54
|
+
let readyResolve = null;
|
|
55
|
+
const readyPromise = new Promise((resolve) => {
|
|
56
|
+
readyResolve = resolve;
|
|
57
|
+
});
|
|
58
|
+
function createEmptyLocation() {
|
|
59
|
+
return {
|
|
60
|
+
path: '/',
|
|
61
|
+
params: {},
|
|
62
|
+
query: {},
|
|
63
|
+
hash: '',
|
|
64
|
+
matched: [],
|
|
65
|
+
fullPath: '/',
|
|
66
|
+
meta: {},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function parseQuery(search) {
|
|
70
|
+
const query = {};
|
|
71
|
+
if (!search)
|
|
72
|
+
return query;
|
|
73
|
+
const searchParams = new URLSearchParams(search);
|
|
74
|
+
searchParams.forEach((value, key) => {
|
|
75
|
+
query[key] = value;
|
|
76
|
+
});
|
|
77
|
+
return query;
|
|
78
|
+
}
|
|
79
|
+
function stringifyQuery(query) {
|
|
80
|
+
const params = new URLSearchParams();
|
|
81
|
+
for (const [key, value] of Object.entries(query)) {
|
|
82
|
+
if (value != null && value !== '') {
|
|
83
|
+
params.set(key, value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const str = params.toString();
|
|
87
|
+
return str ? `?${str}` : '';
|
|
88
|
+
}
|
|
89
|
+
function matchRoute(path, routes, basePath = '') {
|
|
90
|
+
const matched = [];
|
|
91
|
+
const segments = path.split('/').filter(Boolean);
|
|
92
|
+
function match(routes, segmentIndex, parentPath) {
|
|
93
|
+
for (const route of routes) {
|
|
94
|
+
const routePath = route.path.startsWith('/')
|
|
95
|
+
? route.path
|
|
96
|
+
: `${parentPath}/${route.path}`.replace(/\/+/g, '/');
|
|
97
|
+
const routeSegments = routePath.split('/').filter(Boolean);
|
|
98
|
+
const params = {};
|
|
99
|
+
let matches = true;
|
|
100
|
+
let i = 0;
|
|
101
|
+
for (const routeSeg of routeSegments) {
|
|
102
|
+
const pathSeg = segments[segmentIndex + i];
|
|
103
|
+
if (routeSeg.startsWith(':')) {
|
|
104
|
+
const paramName = routeSeg.slice(1);
|
|
105
|
+
if (pathSeg) {
|
|
106
|
+
params[paramName] = decodeURIComponent(pathSeg);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
matches = false;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (routeSeg.startsWith('[') && routeSeg.endsWith(']')) {
|
|
114
|
+
const paramName = routeSeg.slice(1, -1).replace('...', '');
|
|
115
|
+
if (routeSeg.startsWith('[...')) {
|
|
116
|
+
const remaining = segments.slice(segmentIndex + i);
|
|
117
|
+
params[paramName] = remaining.join('/');
|
|
118
|
+
i += remaining.length;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
else if (pathSeg) {
|
|
122
|
+
params[paramName] = decodeURIComponent(pathSeg);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
matches = false;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (routeSeg !== pathSeg) {
|
|
130
|
+
matches = false;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
const totalSegments = segmentIndex + i;
|
|
136
|
+
const isExactMatch = totalSegments === segments.length;
|
|
137
|
+
const hasChildren = route.children && route.children.length > 0;
|
|
138
|
+
if (matches && (isExactMatch || hasChildren)) {
|
|
139
|
+
matched.push({
|
|
140
|
+
route,
|
|
141
|
+
params,
|
|
142
|
+
path: routePath,
|
|
143
|
+
});
|
|
144
|
+
if (hasChildren && !isExactMatch) {
|
|
145
|
+
match(route.children, totalSegments, routePath);
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
match(routes, 0, basePath);
|
|
153
|
+
return matched;
|
|
154
|
+
}
|
|
155
|
+
function parseLocation(location) {
|
|
156
|
+
const urlPath = location.pathname;
|
|
157
|
+
const path = urlPath.replace(base, '') || '/';
|
|
158
|
+
const query = parseQuery(location.search);
|
|
159
|
+
const hash = location.hash.slice(1);
|
|
160
|
+
const matched = matchRoute(path, routes, '');
|
|
161
|
+
const params = {};
|
|
162
|
+
const meta = {};
|
|
163
|
+
matched.forEach(m => {
|
|
164
|
+
Object.assign(params, m.params);
|
|
165
|
+
Object.assign(meta, m.route.meta || {});
|
|
166
|
+
});
|
|
167
|
+
const fullPath = path + stringifyQuery(query) + (hash ? `#${hash}` : '');
|
|
168
|
+
const name = matched[matched.length - 1]?.route.name;
|
|
169
|
+
return { path, params, query, hash, matched, fullPath, name, meta };
|
|
170
|
+
}
|
|
171
|
+
function resolveTarget(target) {
|
|
172
|
+
if (typeof target === 'string') {
|
|
173
|
+
return target;
|
|
174
|
+
}
|
|
175
|
+
let path = target.path || '/';
|
|
176
|
+
if (target.name) {
|
|
177
|
+
const findRoute = (routes, name) => {
|
|
178
|
+
for (const route of routes) {
|
|
179
|
+
if (route.name === name)
|
|
180
|
+
return route;
|
|
181
|
+
if (route.children) {
|
|
182
|
+
const found = findRoute(route.children, name);
|
|
183
|
+
if (found)
|
|
184
|
+
return found;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
};
|
|
189
|
+
const route = findRoute(routes, target.name);
|
|
190
|
+
if (route) {
|
|
191
|
+
path = route.path;
|
|
192
|
+
if (target.params) {
|
|
193
|
+
for (const [key, value] of Object.entries(target.params)) {
|
|
194
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
195
|
+
path = path.replace(`[${key}]`, encodeURIComponent(value));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (target.query) {
|
|
201
|
+
path += stringifyQuery(target.query);
|
|
202
|
+
}
|
|
203
|
+
if (target.hash) {
|
|
204
|
+
path += target.hash.startsWith('#') ? target.hash : `#${target.hash}`;
|
|
205
|
+
}
|
|
206
|
+
return path;
|
|
207
|
+
}
|
|
208
|
+
async function navigate(to, replace = false) {
|
|
209
|
+
const resolvedPath = resolveTarget(to);
|
|
210
|
+
const url = new URL(resolvedPath, window.location.origin);
|
|
211
|
+
if (base && !url.pathname.startsWith(base)) {
|
|
212
|
+
url.pathname = base + url.pathname;
|
|
213
|
+
}
|
|
214
|
+
const newLocation = parseLocation({
|
|
215
|
+
pathname: url.pathname,
|
|
216
|
+
search: url.search,
|
|
217
|
+
hash: url.hash,
|
|
218
|
+
});
|
|
219
|
+
const from = currentRoute.value;
|
|
220
|
+
for (const guard of beforeGuards) {
|
|
221
|
+
const result = await guard(newLocation, from);
|
|
222
|
+
if (result === false) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (typeof result === 'string' || (typeof result === 'object' && result !== null)) {
|
|
226
|
+
await navigate(result, replace);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (replace) {
|
|
231
|
+
window.history.replaceState(null, '', url.href);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
window.history.pushState(null, '', url.href);
|
|
235
|
+
}
|
|
236
|
+
currentRoute.value = newLocation;
|
|
237
|
+
afterHooks.forEach(hook => hook(newLocation, from));
|
|
238
|
+
}
|
|
239
|
+
window.addEventListener('popstate', () => {
|
|
240
|
+
const newLocation = parseLocation(window.location);
|
|
241
|
+
const from = currentRoute.value;
|
|
242
|
+
currentRoute.value = newLocation;
|
|
243
|
+
afterHooks.forEach(hook => hook(newLocation, from));
|
|
244
|
+
});
|
|
245
|
+
const router = {
|
|
246
|
+
currentRoute,
|
|
247
|
+
routes,
|
|
248
|
+
base,
|
|
249
|
+
push: (to) => navigate(to, false),
|
|
250
|
+
replace: (to) => navigate(to, true),
|
|
251
|
+
back: () => window.history.back(),
|
|
252
|
+
forward: () => window.history.forward(),
|
|
253
|
+
go: (delta) => window.history.go(delta),
|
|
254
|
+
beforeEach(guard) {
|
|
255
|
+
beforeGuards.push(guard);
|
|
256
|
+
return () => {
|
|
257
|
+
const index = beforeGuards.indexOf(guard);
|
|
258
|
+
if (index > -1)
|
|
259
|
+
beforeGuards.splice(index, 1);
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
afterEach(hook) {
|
|
263
|
+
afterHooks.push(hook);
|
|
264
|
+
return () => {
|
|
265
|
+
const index = afterHooks.indexOf(hook);
|
|
266
|
+
if (index > -1)
|
|
267
|
+
afterHooks.splice(index, 1);
|
|
268
|
+
};
|
|
269
|
+
},
|
|
270
|
+
isReady() {
|
|
271
|
+
if (isReady)
|
|
272
|
+
return Promise.resolve();
|
|
273
|
+
return readyPromise;
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
currentRoute.value = parseLocation(window.location);
|
|
277
|
+
isReady = true;
|
|
278
|
+
readyResolve?.();
|
|
279
|
+
setRouter(router);
|
|
280
|
+
return router;
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|