@ktjs/router 0.19.0 → 0.31.9

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.mjs CHANGED
@@ -1,92 +1,4 @@
1
- // Cached native methods for performance optimization
2
-
3
- // Error handling utilities
4
- const $throw = (message) => {
5
- throw new Error('@ktjs/shared: ' + message);
6
- };
7
-
8
- // String manipulation utilities
9
- /**
10
- * Default empty function
11
- */
12
- const $emptyFn = (() => true);
13
-
14
- /**
15
- * Normalize path by joining parts and ensuring leading slash
16
- */
17
- const normalizePath = (...paths) => {
18
- const p = paths
19
- .map((p) => p.split('/'))
20
- .flat()
21
- .filter(Boolean);
22
- return '/' + p.join('/');
23
- };
24
- /**
25
- * Parse query string into object
26
- */
27
- const parseQuery = (queryString) => {
28
- const query = {};
29
- if (!queryString || queryString === '?') {
30
- return query;
31
- }
32
- const params = queryString.replace(/^\?/, '').split('&');
33
- for (const param of params) {
34
- const [key, value] = param.split('=');
35
- if (key) {
36
- query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
37
- }
38
- }
39
- return query;
40
- };
41
- /**
42
- * Build query string from object
43
- */
44
- const buildQuery = (query) => {
45
- const keys = Object.keys(query);
46
- if (keys.length === 0)
47
- return '';
48
- const params = keys.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`).join('&');
49
- return `?${params}`;
50
- };
51
- /**
52
- * Substitute params into path pattern
53
- * @example '/user/:id' + {id: '123'} => '/user/123'
54
- */
55
- const emplaceParams = (path, params) => {
56
- let result = path;
57
- for (const key in params) {
58
- result = result.replace(`:${key}`, params[key]);
59
- }
60
- return result;
61
- };
62
- /**
63
- * Extract dynamic params from path using pattern
64
- * @example pattern: '/user/:id', path: '/user/123' => {id: '123'}
65
- */
66
- const extractParams = (pattern, path) => {
67
- const params = {};
68
- const patternParts = pattern.split('/');
69
- const pathParts = path.split('/');
70
- if (patternParts.length !== pathParts.length) {
71
- return null;
72
- }
73
- for (let i = 0; i < patternParts.length; i++) {
74
- const patternPart = patternParts[i];
75
- const pathPart = pathParts[i];
76
- if (patternPart.startsWith(':')) {
77
- const paramName = patternPart.slice(1);
78
- params[paramName] = pathPart;
79
- }
80
- else if (patternPart !== pathPart) {
81
- return null;
82
- }
83
- }
84
- return params;
85
- };
86
-
87
- // Shared utilities and cached native methods for kt.js framework
88
- // Re-export all utilities
89
- Object.defineProperty(window, '@ktjs/shared', { value: '0.19.0' });
1
+ import { normalizePath, extractParams, $emptyFn, parseQuery, emplaceParams, buildQuery } from '@ktjs/shared';
90
2
 
91
3
  /**
92
4
  * Route matcher for finding matching routes and extracting params
@@ -97,7 +9,7 @@ const createMatcher = (routes) => {
97
9
  const route = routes[i];
98
10
  if (route.name !== undefined) {
99
11
  if (route.name in nameMap) {
100
- $throw(`Duplicate route name detected: '${route.name}'`);
12
+ throw new Error(`[@ktjs/router error] Duplicate route name detected: '${route.name}'`);
101
13
  }
102
14
  nameMap[route.name] = route;
103
15
  }
@@ -239,7 +151,7 @@ const createRouter = ({ beforeEach = $emptyFn, afterEach = $emptyFn, onNotFound
239
151
  if (options.name) {
240
152
  targetRoute = findByName(options.name);
241
153
  if (!targetRoute) {
242
- $throw(`Route not found: ${options.name}`);
154
+ throw new Error(`[@ktjs/router error] Route not found: ${options.name}`);
243
155
  }
244
156
  targetPath = targetRoute.path;
245
157
  }
@@ -248,7 +160,7 @@ const createRouter = ({ beforeEach = $emptyFn, afterEach = $emptyFn, onNotFound
248
160
  targetRoute = match(targetPath)?.route;
249
161
  }
250
162
  else {
251
- $throw(`Either path or name must be provided`);
163
+ throw new Error(`[@ktjs/router error] Either path or name must be provided`);
252
164
  }
253
165
  // Substitute params
254
166
  if (options.params) {
@@ -437,3 +349,4 @@ const createRouter = ({ beforeEach = $emptyFn, afterEach = $emptyFn, onNotFound
437
349
  };
438
350
 
439
351
  export { KTRouter, createRouter };
352
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/core/matcher.ts","../src/core/kt-router.ts","../src/core/index.ts"],"sourcesContent":["import type { RouteConfig, RouteMatch } from '../types/router.js';\nimport { extractParams, normalizePath } from '@ktjs/shared';\n\n/**\n * Route matcher for finding matching routes and extracting params\n */\nexport const createMatcher = (routes: RouteConfig[]) => {\n const nameMap: Record<string, RouteConfig> = {};\n\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i];\n if (route.name !== undefined) {\n if (route.name in nameMap) {\n $throw(`Duplicate route name detected: '${route.name}'`);\n }\n nameMap[route.name] = route;\n }\n }\n\n /**\n * Find route by name\n */\n const findByName = (name: string): RouteConfig | null => {\n return nameMap[name] ?? null;\n };\n\n /**\n * Match path against all routes\n */\n const match = (path: string): RouteMatch | null => {\n const normalizedPath = normalizePath(path);\n\n // Try exact match first\n for (const route of routes) {\n if (route.path === normalizedPath) {\n return {\n route,\n params: {},\n result: getMatchedChain(route),\n };\n }\n }\n\n // Try dynamic routes\n for (const route of routes) {\n if (route.path.includes(':')) {\n const params = extractParams(route.path, normalizedPath);\n if (params) {\n return {\n route,\n params,\n result: getMatchedChain(route),\n };\n }\n }\n }\n\n return null;\n };\n\n /**\n * Get chain of matched routes (for nested routes)\n * - parent roots ahead\n */\n const getMatchedChain = (route: RouteConfig): RouteConfig[] => {\n const matched: RouteConfig[] = [route];\n const path = route.path;\n\n // Find parent routes by path prefix matching\n for (let i = 0; i < routes.length; i++) {\n const r = routes[i];\n if (r !== route && path.startsWith(r.path) && path !== r.path) {\n matched.push(r);\n }\n }\n\n return matched.reverse();\n };\n\n return {\n findByName,\n match,\n };\n};\n","import { JSX } from '@ktjs/core/jsx-runtime';\nimport type { Router } from '../types/router.js';\n\n/**\n * Create a router view container that automatically renders route components\n */\nexport function KTRouter({ router }: { router: Router }): JSX.Element {\n const view = document.createElement('kt-router-view');\n router.setRouterView(view);\n return view as JSX.Element;\n}\n","import { $emptyFn, buildQuery, normalizePath, parseQuery, emplaceParams } from '@ktjs/shared';\n\nimport type { Router, RouterConfig, RouteContext, NavOptions, RawRouteConfig, RouteConfig } from '../types/router.js';\nimport { GuardLevel } from './consts.js';\nimport { createMatcher } from './matcher.js';\n\n/**\n * Create a new router instance\n */\nexport const createRouter = ({\n beforeEach = $emptyFn,\n afterEach = $emptyFn,\n onNotFound = $emptyFn,\n onError = $emptyFn,\n prefix = '',\n routes: rawRoutes,\n}: RouterConfig): Router => {\n // # private values\n const routes: RouteConfig[] = [];\n const history: RouteContext[] = [];\n let routerView: HTMLElement | null = null;\n let current: RouteContext | null = null;\n\n // # methods\n const normalize = (rawRoutes: RawRouteConfig[], parentPath: string): RouteConfig[] =>\n rawRoutes.map((route) => {\n const path = normalizePath(parentPath, route.path);\n const normalized = {\n path: prefix + path,\n name: route.name ?? '',\n meta: route.meta ?? {},\n beforeEnter: route.beforeEnter ?? $emptyFn,\n after: route.after ?? $emptyFn,\n children: route.children ? normalize(route.children, path) : [],\n component: route.component,\n };\n\n // directly push the normalized route to the list\n // avoid flatten them again\n routes.push(normalized);\n return normalized;\n });\n\n const guard = async (\n to: RouteContext,\n from: RouteContext | null,\n guardLevel: GuardLevel,\n ): Promise<{ continue: boolean; redirectTo?: NavOptions }> => {\n try {\n if (guardLevel === GuardLevel.None) {\n return { continue: true };\n }\n\n if (guardLevel & GuardLevel.Global) {\n const result = await beforeEach(to, from);\n if (result === false) {\n return { continue: false };\n }\n if (typeof result === 'string') {\n return { continue: false, redirectTo: { path: result } };\n }\n if (result && typeof result === 'object') {\n return { continue: false, redirectTo: result };\n }\n }\n\n if (guardLevel & GuardLevel.Route) {\n const targetRoute = to.matched[to.matched.length - 1];\n const result = await targetRoute.beforeEnter(to);\n if (result === false) {\n return { continue: false };\n }\n if (typeof result === 'string') {\n return { continue: false, redirectTo: { path: result } };\n }\n if (result && typeof result === 'object') {\n return { continue: false, redirectTo: result };\n }\n }\n\n return { continue: true };\n } catch (error) {\n onError(error as Error);\n return { continue: false };\n }\n };\n\n const navigatePrepare = (options: NavOptions) => {\n // Resolve target route\n let targetPath: string;\n let targetRoute;\n\n if (options.name) {\n targetRoute = findByName(options.name);\n if (!targetRoute) {\n $throw(`Route not found: ${options.name}`);\n }\n targetPath = targetRoute.path;\n } else if (options.path) {\n targetPath = normalizePath(options.path);\n targetRoute = match(targetPath)?.route;\n } else {\n $throw(`Either path or name must be provided`);\n }\n\n // Substitute params\n if (options.params) {\n targetPath = emplaceParams(targetPath, options.params);\n }\n\n // Match final path\n const matched = match(targetPath);\n if (!matched) {\n onNotFound(targetPath);\n return null;\n }\n\n // Build route context\n const queryString = options.query ? buildQuery(options.query) : '';\n const fullPath = targetPath + queryString;\n\n const to: RouteContext = {\n path: targetPath,\n name: matched.route.name,\n params: { ...matched.params, ...(options.params ?? {}) },\n query: options.query ?? {},\n meta: matched.route.meta ?? {},\n matched: matched.result,\n };\n\n return {\n guardLevel: options.guardLevel ?? GuardLevel.Default,\n replace: options.replace ?? false,\n to,\n fullPath,\n };\n };\n\n const navigate = async (options: NavOptions, redirectCount = 0): Promise<boolean> => {\n try {\n // Prevent infinite redirect loop\n if (redirectCount > 10) {\n onError(new Error('Maximum redirect count exceeded'));\n return false;\n }\n\n const prep = navigatePrepare(options);\n if (!prep) {\n return false;\n }\n\n const { guardLevel, replace, to, fullPath } = prep;\n\n const guardResult = await guard(to, current, guardLevel);\n if (!guardResult.continue) {\n // Check if there's a redirect\n if (guardResult.redirectTo) {\n return await navigate(guardResult.redirectTo, redirectCount + 1);\n }\n return false;\n }\n\n // ---- Guards passed ----\n\n const hashUrl = '#' + fullPath;\n if (replace) {\n window.location.replace(hashUrl);\n } else {\n window.location.hash = fullPath;\n }\n\n current = to;\n if (replace) {\n if (history.length > 0) {\n history[history.length - 1] = to;\n } else {\n history.push(to);\n }\n } else {\n history.push(to);\n }\n\n // Render component if routerView exists\n if (routerView && to.matched.length > 0) {\n const route = to.matched[to.matched.length - 1];\n if (route.component) {\n const element = await route.component();\n routerView.innerHTML = '';\n routerView.appendChild(element);\n }\n }\n\n executeAfterHooks(to, history[history.length - 2] ?? null);\n return true;\n } catch (error) {\n onError(error as Error);\n return false;\n }\n };\n\n const executeAfterHooks = async (to: RouteContext, from: RouteContext | null): Promise<void> => {\n const targetRoute = to.matched[to.matched.length - 1];\n await targetRoute.after(to);\n await afterEach(to, from);\n };\n\n /**\n * Normalize navigation argument\n */\n const normalizeLocation = (loc: string | NavOptions): NavOptions => {\n if (typeof loc !== 'string') {\n return loc;\n }\n\n const [path, queryString] = loc.split('?');\n return {\n path,\n query: queryString ? parseQuery(queryString) : undefined,\n };\n };\n\n // # register events\n window.addEventListener('hashchange', () => {\n const hash = window.location.hash.slice(1);\n const [path] = hash.split('?');\n const normalizedPath = normalizePath(path);\n if (current && current.path === normalizedPath) {\n return;\n }\n // render route for new hash without adding extra history entry\n const matched = match(normalizedPath);\n if (!matched) {\n onNotFound(normalizedPath);\n return;\n }\n const queryString = window.location.hash.slice(1).split('?')[1];\n const to: RouteContext = {\n path: normalizedPath,\n name: matched.route.name,\n params: matched.params,\n query: queryString ? parseQuery(queryString) : {},\n meta: matched.route.meta ?? {},\n matched: matched.result,\n };\n\n // apply without modifying browser history\n current = to;\n history.push(to);\n\n if (routerView && to.matched.length > 0) {\n const route = to.matched[to.matched.length - 1];\n if (route.component) {\n const element = route.component();\n if (element instanceof Promise) {\n element.then((el) => {\n routerView!.innerHTML = '';\n routerView!.appendChild(el);\n });\n } else {\n routerView.innerHTML = '';\n routerView.appendChild(element);\n }\n }\n }\n\n executeAfterHooks(to, history[history.length - 2] ?? null);\n });\n\n // # initialize\n const instance: Router = {\n get current() {\n return current;\n },\n\n get history() {\n return history.concat();\n },\n\n setRouterView(view: HTMLElement) {\n routerView = view;\n },\n\n push(location: string | NavOptions): boolean | Promise<boolean> {\n const options = normalizeLocation(location);\n return navigate(options);\n },\n\n silentPush(location: string | NavOptions): boolean | Promise<boolean> {\n const options = normalizeLocation(location);\n return navigate({ ...options, guardLevel: GuardLevel.Route });\n },\n\n replace(location: string | NavOptions): boolean | Promise<boolean> {\n const options = normalizeLocation(location);\n return navigate({ ...options, replace: true });\n },\n\n back() {\n window.history.back();\n },\n\n forward() {\n window.history.forward();\n },\n };\n normalize(rawRoutes, '/');\n const { findByName, match } = createMatcher(routes);\n const currentHash = window.location.hash.slice(1);\n if (currentHash) {\n instance.push(currentHash);\n }\n\n return instance;\n};\n\nexport { GuardLevel };\nexport { KTRouter } from './kt-router.js';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@ktjs/router",
3
- "version": "0.19.0",
3
+ "version": "0.31.9",
4
4
  "description": "Router for kt.js - client-side routing with navigation guards",
5
+ "description_zh": "kt.js 的路由库,支持前端路由与导航守卫。",
5
6
  "type": "module",
6
7
  "module": "./dist/index.mjs",
7
8
  "types": "./dist/index.d.ts",
@@ -31,11 +32,12 @@
31
32
  "directory": "packages/router"
32
33
  },
33
34
  "dependencies": {
34
- "@ktjs/core": "0.19.0",
35
- "@ktjs/shared": "0.19.0"
35
+ "@ktjs/core": "^*",
36
+ "@ktjs/shared": "^*"
36
37
  },
37
38
  "scripts": {
38
39
  "build": "rollup -c rollup.config.mjs",
39
- "dev": "rollup -c rollup.config.mjs -w"
40
+ "dev": "rollup -c rollup.config.mjs -w",
41
+ "test": "vitest run"
40
42
  }
41
43
  }
@@ -1,447 +0,0 @@
1
- var __ktjs_router__ = (function (exports) {
2
- 'use strict';
3
-
4
- // Cached native methods for performance optimization
5
-
6
- // Error handling utilities
7
- const $throw = (message) => {
8
- throw new Error('@ktjs/shared: ' + message);
9
- };
10
-
11
- // String manipulation utilities
12
- /**
13
- * Default empty function
14
- */
15
- const $emptyFn = (() => true);
16
-
17
- /**
18
- * Normalize path by joining parts and ensuring leading slash
19
- */
20
- const normalizePath = (...paths) => {
21
- const p = paths
22
- .map((p) => p.split('/'))
23
- .flat()
24
- .filter(Boolean);
25
- return '/' + p.join('/');
26
- };
27
- /**
28
- * Parse query string into object
29
- */
30
- const parseQuery = (queryString) => {
31
- const query = {};
32
- if (!queryString || queryString === '?') {
33
- return query;
34
- }
35
- const params = queryString.replace(/^\?/, '').split('&');
36
- for (const param of params) {
37
- const [key, value] = param.split('=');
38
- if (key) {
39
- query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
40
- }
41
- }
42
- return query;
43
- };
44
- /**
45
- * Build query string from object
46
- */
47
- const buildQuery = (query) => {
48
- const keys = Object.keys(query);
49
- if (keys.length === 0)
50
- return '';
51
- const params = keys.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`).join('&');
52
- return `?${params}`;
53
- };
54
- /**
55
- * Substitute params into path pattern
56
- * @example '/user/:id' + {id: '123'} => '/user/123'
57
- */
58
- const emplaceParams = (path, params) => {
59
- let result = path;
60
- for (const key in params) {
61
- result = result.replace(`:${key}`, params[key]);
62
- }
63
- return result;
64
- };
65
- /**
66
- * Extract dynamic params from path using pattern
67
- * @example pattern: '/user/:id', path: '/user/123' => {id: '123'}
68
- */
69
- const extractParams = (pattern, path) => {
70
- const params = {};
71
- const patternParts = pattern.split('/');
72
- const pathParts = path.split('/');
73
- if (patternParts.length !== pathParts.length) {
74
- return null;
75
- }
76
- for (let i = 0; i < patternParts.length; i++) {
77
- const patternPart = patternParts[i];
78
- const pathPart = pathParts[i];
79
- if (patternPart.startsWith(':')) {
80
- const paramName = patternPart.slice(1);
81
- params[paramName] = pathPart;
82
- }
83
- else if (patternPart !== pathPart) {
84
- return null;
85
- }
86
- }
87
- return params;
88
- };
89
-
90
- // Shared utilities and cached native methods for kt.js framework
91
- // Re-export all utilities
92
- Object.defineProperty(window, '@ktjs/shared', { value: '0.19.0' });
93
-
94
- /**
95
- * Route matcher for finding matching routes and extracting params
96
- */
97
- const createMatcher = (routes) => {
98
- const nameMap = {};
99
- for (let i = 0; i < routes.length; i++) {
100
- const route = routes[i];
101
- if (route.name !== undefined) {
102
- if (route.name in nameMap) {
103
- $throw(`Duplicate route name detected: '${route.name}'`);
104
- }
105
- nameMap[route.name] = route;
106
- }
107
- }
108
- /**
109
- * Find route by name
110
- */
111
- const findByName = (name) => {
112
- return nameMap[name] ?? null;
113
- };
114
- /**
115
- * Match path against all routes
116
- */
117
- const match = (path) => {
118
- const normalizedPath = normalizePath(path);
119
- // Try exact match first
120
- for (const route of routes) {
121
- if (route.path === normalizedPath) {
122
- return {
123
- route,
124
- params: {},
125
- result: getMatchedChain(route),
126
- };
127
- }
128
- }
129
- // Try dynamic routes
130
- for (const route of routes) {
131
- if (route.path.includes(':')) {
132
- const params = extractParams(route.path, normalizedPath);
133
- if (params) {
134
- return {
135
- route,
136
- params,
137
- result: getMatchedChain(route),
138
- };
139
- }
140
- }
141
- }
142
- return null;
143
- };
144
- /**
145
- * Get chain of matched routes (for nested routes)
146
- * - parent roots ahead
147
- */
148
- const getMatchedChain = (route) => {
149
- const matched = [route];
150
- const path = route.path;
151
- // Find parent routes by path prefix matching
152
- for (let i = 0; i < routes.length; i++) {
153
- const r = routes[i];
154
- if (r !== route && path.startsWith(r.path) && path !== r.path) {
155
- matched.push(r);
156
- }
157
- }
158
- return matched.reverse();
159
- };
160
- return {
161
- findByName,
162
- match,
163
- };
164
- };
165
-
166
- /**
167
- * Create a router view container that automatically renders route components
168
- */
169
- function KTRouter({ router }) {
170
- const view = document.createElement('kt-router-view');
171
- router.setRouterView(view);
172
- return view;
173
- }
174
-
175
- /**
176
- * Create a new router instance
177
- */
178
- const createRouter = ({ beforeEach = $emptyFn, afterEach = $emptyFn, onNotFound = $emptyFn, onError = $emptyFn, prefix = '', routes: rawRoutes, }) => {
179
- // # private values
180
- const routes = [];
181
- const history = [];
182
- let routerView = null;
183
- let current = null;
184
- // # methods
185
- const normalize = (rawRoutes, parentPath) => rawRoutes.map((route) => {
186
- const path = normalizePath(parentPath, route.path);
187
- const normalized = {
188
- path: prefix + path,
189
- name: route.name ?? '',
190
- meta: route.meta ?? {},
191
- beforeEnter: route.beforeEnter ?? $emptyFn,
192
- after: route.after ?? $emptyFn,
193
- children: route.children ? normalize(route.children, path) : [],
194
- component: route.component,
195
- };
196
- // directly push the normalized route to the list
197
- // avoid flatten them again
198
- routes.push(normalized);
199
- return normalized;
200
- });
201
- const guard = async (to, from, guardLevel) => {
202
- try {
203
- if (guardLevel === 0 /* GuardLevel.None */) {
204
- return { continue: true };
205
- }
206
- if (guardLevel & 1 /* GuardLevel.Global */) {
207
- const result = await beforeEach(to, from);
208
- if (result === false) {
209
- return { continue: false };
210
- }
211
- if (typeof result === 'string') {
212
- return { continue: false, redirectTo: { path: result } };
213
- }
214
- if (result && typeof result === 'object') {
215
- return { continue: false, redirectTo: result };
216
- }
217
- }
218
- if (guardLevel & 2 /* GuardLevel.Route */) {
219
- const targetRoute = to.matched[to.matched.length - 1];
220
- const result = await targetRoute.beforeEnter(to);
221
- if (result === false) {
222
- return { continue: false };
223
- }
224
- if (typeof result === 'string') {
225
- return { continue: false, redirectTo: { path: result } };
226
- }
227
- if (result && typeof result === 'object') {
228
- return { continue: false, redirectTo: result };
229
- }
230
- }
231
- return { continue: true };
232
- }
233
- catch (error) {
234
- onError(error);
235
- return { continue: false };
236
- }
237
- };
238
- const navigatePrepare = (options) => {
239
- // Resolve target route
240
- let targetPath;
241
- let targetRoute;
242
- if (options.name) {
243
- targetRoute = findByName(options.name);
244
- if (!targetRoute) {
245
- $throw(`Route not found: ${options.name}`);
246
- }
247
- targetPath = targetRoute.path;
248
- }
249
- else if (options.path) {
250
- targetPath = normalizePath(options.path);
251
- targetRoute = match(targetPath)?.route;
252
- }
253
- else {
254
- $throw(`Either path or name must be provided`);
255
- }
256
- // Substitute params
257
- if (options.params) {
258
- targetPath = emplaceParams(targetPath, options.params);
259
- }
260
- // Match final path
261
- const matched = match(targetPath);
262
- if (!matched) {
263
- onNotFound(targetPath);
264
- return null;
265
- }
266
- // Build route context
267
- const queryString = options.query ? buildQuery(options.query) : '';
268
- const fullPath = targetPath + queryString;
269
- const to = {
270
- path: targetPath,
271
- name: matched.route.name,
272
- params: { ...matched.params, ...(options.params ?? {}) },
273
- query: options.query ?? {},
274
- meta: matched.route.meta ?? {},
275
- matched: matched.result,
276
- };
277
- return {
278
- guardLevel: options.guardLevel ?? 15 /* GuardLevel.Default */,
279
- replace: options.replace ?? false,
280
- to,
281
- fullPath,
282
- };
283
- };
284
- const navigate = async (options, redirectCount = 0) => {
285
- try {
286
- // Prevent infinite redirect loop
287
- if (redirectCount > 10) {
288
- onError(new Error('Maximum redirect count exceeded'));
289
- return false;
290
- }
291
- const prep = navigatePrepare(options);
292
- if (!prep) {
293
- return false;
294
- }
295
- const { guardLevel, replace, to, fullPath } = prep;
296
- const guardResult = await guard(to, current, guardLevel);
297
- if (!guardResult.continue) {
298
- // Check if there's a redirect
299
- if (guardResult.redirectTo) {
300
- return await navigate(guardResult.redirectTo, redirectCount + 1);
301
- }
302
- return false;
303
- }
304
- // ---- Guards passed ----
305
- const hashUrl = '#' + fullPath;
306
- if (replace) {
307
- window.location.replace(hashUrl);
308
- }
309
- else {
310
- window.location.hash = fullPath;
311
- }
312
- current = to;
313
- if (replace) {
314
- if (history.length > 0) {
315
- history[history.length - 1] = to;
316
- }
317
- else {
318
- history.push(to);
319
- }
320
- }
321
- else {
322
- history.push(to);
323
- }
324
- // Render component if routerView exists
325
- if (routerView && to.matched.length > 0) {
326
- const route = to.matched[to.matched.length - 1];
327
- if (route.component) {
328
- const element = await route.component();
329
- routerView.innerHTML = '';
330
- routerView.appendChild(element);
331
- }
332
- }
333
- executeAfterHooks(to, history[history.length - 2] ?? null);
334
- return true;
335
- }
336
- catch (error) {
337
- onError(error);
338
- return false;
339
- }
340
- };
341
- const executeAfterHooks = async (to, from) => {
342
- const targetRoute = to.matched[to.matched.length - 1];
343
- await targetRoute.after(to);
344
- await afterEach(to, from);
345
- };
346
- /**
347
- * Normalize navigation argument
348
- */
349
- const normalizeLocation = (loc) => {
350
- if (typeof loc !== 'string') {
351
- return loc;
352
- }
353
- const [path, queryString] = loc.split('?');
354
- return {
355
- path,
356
- query: queryString ? parseQuery(queryString) : undefined,
357
- };
358
- };
359
- // # register events
360
- window.addEventListener('hashchange', () => {
361
- const hash = window.location.hash.slice(1);
362
- const [path] = hash.split('?');
363
- const normalizedPath = normalizePath(path);
364
- if (current && current.path === normalizedPath) {
365
- return;
366
- }
367
- // render route for new hash without adding extra history entry
368
- const matched = match(normalizedPath);
369
- if (!matched) {
370
- onNotFound(normalizedPath);
371
- return;
372
- }
373
- const queryString = window.location.hash.slice(1).split('?')[1];
374
- const to = {
375
- path: normalizedPath,
376
- name: matched.route.name,
377
- params: matched.params,
378
- query: queryString ? parseQuery(queryString) : {},
379
- meta: matched.route.meta ?? {},
380
- matched: matched.result,
381
- };
382
- // apply without modifying browser history
383
- current = to;
384
- history.push(to);
385
- if (routerView && to.matched.length > 0) {
386
- const route = to.matched[to.matched.length - 1];
387
- if (route.component) {
388
- const element = route.component();
389
- if (element instanceof Promise) {
390
- element.then((el) => {
391
- routerView.innerHTML = '';
392
- routerView.appendChild(el);
393
- });
394
- }
395
- else {
396
- routerView.innerHTML = '';
397
- routerView.appendChild(element);
398
- }
399
- }
400
- }
401
- executeAfterHooks(to, history[history.length - 2] ?? null);
402
- });
403
- // # initialize
404
- const instance = {
405
- get current() {
406
- return current;
407
- },
408
- get history() {
409
- return history.concat();
410
- },
411
- setRouterView(view) {
412
- routerView = view;
413
- },
414
- push(location) {
415
- const options = normalizeLocation(location);
416
- return navigate(options);
417
- },
418
- silentPush(location) {
419
- const options = normalizeLocation(location);
420
- return navigate({ ...options, guardLevel: 2 /* GuardLevel.Route */ });
421
- },
422
- replace(location) {
423
- const options = normalizeLocation(location);
424
- return navigate({ ...options, replace: true });
425
- },
426
- back() {
427
- window.history.back();
428
- },
429
- forward() {
430
- window.history.forward();
431
- },
432
- };
433
- normalize(rawRoutes, '/');
434
- const { findByName, match } = createMatcher(routes);
435
- const currentHash = window.location.hash.slice(1);
436
- if (currentHash) {
437
- instance.push(currentHash);
438
- }
439
- return instance;
440
- };
441
-
442
- exports.KTRouter = KTRouter;
443
- exports.createRouter = createRouter;
444
-
445
- return exports;
446
-
447
- })({});