@esmx/router 3.0.0-rc.103
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 +77 -0
- package/README.zh-CN.md +158 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.mjs +64 -0
- package/dist/increment-id.d.ts +7 -0
- package/dist/increment-id.mjs +16 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.mjs +13 -0
- package/dist/location.d.ts +22 -0
- package/dist/location.mjs +64 -0
- package/dist/matcher.d.ts +4 -0
- package/dist/matcher.mjs +46 -0
- package/dist/micro-app.d.ts +18 -0
- package/dist/micro-app.mjs +85 -0
- package/dist/navigation.d.ts +45 -0
- package/dist/navigation.mjs +153 -0
- package/dist/options.d.ts +4 -0
- package/dist/options.mjs +94 -0
- package/dist/route-task.d.ts +40 -0
- package/dist/route-task.mjs +77 -0
- package/dist/route-transition.d.ts +53 -0
- package/dist/route-transition.mjs +356 -0
- package/dist/route.d.ts +77 -0
- package/dist/route.mjs +223 -0
- package/dist/router-link.d.ts +10 -0
- package/dist/router-link.mjs +139 -0
- package/dist/router.d.ts +122 -0
- package/dist/router.mjs +355 -0
- package/dist/scroll.d.ts +33 -0
- package/dist/scroll.mjs +49 -0
- package/dist/types.d.ts +282 -0
- package/dist/types.mjs +18 -0
- package/dist/util.d.ts +27 -0
- package/dist/util.mjs +67 -0
- package/package.json +62 -0
- package/src/error.ts +84 -0
- package/src/increment-id.ts +12 -0
- package/src/index.ts +67 -0
- package/src/location.ts +124 -0
- package/src/matcher.ts +68 -0
- package/src/micro-app.ts +101 -0
- package/src/navigation.ts +202 -0
- package/src/options.ts +135 -0
- package/src/route-task.ts +102 -0
- package/src/route-transition.ts +472 -0
- package/src/route.ts +335 -0
- package/src/router-link.ts +238 -0
- package/src/router.ts +395 -0
- package/src/scroll.ts +106 -0
- package/src/types.ts +381 -0
- package/src/util.ts +133 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { Route } from "./route.mjs";
|
|
5
|
+
import {
|
|
6
|
+
createRouteTask,
|
|
7
|
+
RouteTaskController
|
|
8
|
+
} from "./route-task.mjs";
|
|
9
|
+
import {
|
|
10
|
+
getSavedScrollPosition,
|
|
11
|
+
saveScrollPosition,
|
|
12
|
+
scrollToPosition
|
|
13
|
+
} from "./scroll.mjs";
|
|
14
|
+
import { RouteType } from "./types.mjs";
|
|
15
|
+
import {
|
|
16
|
+
isBrowser,
|
|
17
|
+
isRouteMatched,
|
|
18
|
+
isUrlEqual,
|
|
19
|
+
isValidConfirmHookResult,
|
|
20
|
+
removeFromArray
|
|
21
|
+
} from "./util.mjs";
|
|
22
|
+
export const ROUTE_TRANSITION_HOOKS = {
|
|
23
|
+
async fallback(to, from, router) {
|
|
24
|
+
if (to.matched.length === 0) {
|
|
25
|
+
return router.parsedOptions.fallback;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
async override(to, from, router) {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
const result = await ((_b = (_a = to.config) == null ? void 0 : _a.override) == null ? void 0 : _b.call(_a, to, from, router));
|
|
31
|
+
if (isValidConfirmHookResult(result)) {
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async asyncComponent(to, from, router) {
|
|
36
|
+
await Promise.all(
|
|
37
|
+
to.matched.map(async (matched) => {
|
|
38
|
+
const { asyncComponent, component } = matched;
|
|
39
|
+
if (!component && typeof asyncComponent === "function") {
|
|
40
|
+
try {
|
|
41
|
+
const result = await asyncComponent();
|
|
42
|
+
matched.component = result;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Async component '".concat(matched.compilePath, "' is not a valid component. Original error: ").concat(error.message)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
},
|
|
53
|
+
async beforeLeave(to, from, router) {
|
|
54
|
+
if (!(from == null ? void 0 : from.matched.length)) return;
|
|
55
|
+
const leavingRoutes = from.matched.filter(
|
|
56
|
+
(fromRoute) => !to.matched.some((toRoute) => toRoute === fromRoute)
|
|
57
|
+
);
|
|
58
|
+
for (let i = leavingRoutes.length - 1; i >= 0; i--) {
|
|
59
|
+
const route = leavingRoutes[i];
|
|
60
|
+
if (route.beforeLeave) {
|
|
61
|
+
const result = await route.beforeLeave(to, from, router);
|
|
62
|
+
if (isValidConfirmHookResult(result)) {
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
async beforeEnter(to, from, router) {
|
|
69
|
+
if (!to.matched.length) return;
|
|
70
|
+
const enteringRoutes = to.matched.filter(
|
|
71
|
+
(toRoute) => !(from == null ? void 0 : from.matched.some((fromRoute) => fromRoute === toRoute))
|
|
72
|
+
);
|
|
73
|
+
for (const route of enteringRoutes) {
|
|
74
|
+
if (route.beforeEnter) {
|
|
75
|
+
const result = await route.beforeEnter(to, from, router);
|
|
76
|
+
if (isValidConfirmHookResult(result)) {
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
async beforeUpdate(to, from, router) {
|
|
83
|
+
if (!isRouteMatched(to, from, "route")) return;
|
|
84
|
+
if (!from || to.matched.length !== from.matched.length) return;
|
|
85
|
+
const isSameRouteSet = to.matched.every(
|
|
86
|
+
(toRoute, index) => toRoute === from.matched[index]
|
|
87
|
+
);
|
|
88
|
+
if (!isSameRouteSet) return;
|
|
89
|
+
if (!isRouteMatched(to, from, "exact")) {
|
|
90
|
+
for (const route of to.matched) {
|
|
91
|
+
if (route.beforeUpdate) {
|
|
92
|
+
const result = await route.beforeUpdate(to, from, router);
|
|
93
|
+
if (isValidConfirmHookResult(result)) {
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
async beforeEach(to, from, router) {
|
|
101
|
+
const transition = router.transition;
|
|
102
|
+
for (const guard of transition.guards.beforeEach) {
|
|
103
|
+
const result = await guard(to, from, router);
|
|
104
|
+
if (isValidConfirmHookResult(result)) {
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
async confirm(to, from, router) {
|
|
110
|
+
if (to.confirm) {
|
|
111
|
+
const result = await to.confirm(to, from, router);
|
|
112
|
+
if (isValidConfirmHookResult(result)) {
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (isBrowser && "scrollRestoration" in window.history)
|
|
117
|
+
window.history.scrollRestoration = "manual";
|
|
118
|
+
if (from && isBrowser && !router.isLayer)
|
|
119
|
+
switch (to.type) {
|
|
120
|
+
case RouteType.push:
|
|
121
|
+
case RouteType.replace: {
|
|
122
|
+
if (!to.keepScrollPosition) {
|
|
123
|
+
saveScrollPosition(from.url.href);
|
|
124
|
+
scrollToPosition({ left: 0, top: 0 });
|
|
125
|
+
} else {
|
|
126
|
+
to.applyNavigationState({
|
|
127
|
+
__keepScrollPosition: to.keepScrollPosition
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case RouteType.go:
|
|
133
|
+
case RouteType.forward:
|
|
134
|
+
case RouteType.back:
|
|
135
|
+
// for popstate
|
|
136
|
+
case RouteType.unknown: {
|
|
137
|
+
saveScrollPosition(from.url.href);
|
|
138
|
+
setTimeout(async () => {
|
|
139
|
+
const state = window.history.state;
|
|
140
|
+
if (state == null ? void 0 : state.__keepScrollPosition) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const savedPosition = getSavedScrollPosition(
|
|
144
|
+
to.url.href,
|
|
145
|
+
{ left: 0, top: 0 }
|
|
146
|
+
);
|
|
147
|
+
if (!savedPosition) return;
|
|
148
|
+
await router.parsedOptions.nextTick();
|
|
149
|
+
scrollToPosition(savedPosition);
|
|
150
|
+
});
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
switch (to.type) {
|
|
155
|
+
case RouteType.push:
|
|
156
|
+
return ROUTE_TYPE_HANDLERS.push;
|
|
157
|
+
case RouteType.replace:
|
|
158
|
+
return ROUTE_TYPE_HANDLERS.replace;
|
|
159
|
+
case RouteType.restartApp:
|
|
160
|
+
return ROUTE_TYPE_HANDLERS.restartApp;
|
|
161
|
+
case RouteType.pushWindow:
|
|
162
|
+
return ROUTE_TYPE_HANDLERS.pushWindow;
|
|
163
|
+
case RouteType.replaceWindow:
|
|
164
|
+
return ROUTE_TYPE_HANDLERS.replaceWindow;
|
|
165
|
+
case RouteType.pushLayer:
|
|
166
|
+
return ROUTE_TYPE_HANDLERS.pushLayer;
|
|
167
|
+
default:
|
|
168
|
+
return ROUTE_TYPE_HANDLERS.default;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
export const ROUTE_TYPE_HANDLERS = {
|
|
173
|
+
push(to, from, router) {
|
|
174
|
+
router.transition.route = to;
|
|
175
|
+
router.microApp._update(router);
|
|
176
|
+
if (!isUrlEqual(to.url, from == null ? void 0 : from.url)) {
|
|
177
|
+
const newState = router.navigation.push(to.state, to.url);
|
|
178
|
+
to.applyNavigationState(newState);
|
|
179
|
+
} else {
|
|
180
|
+
const newState = router.navigation.replace(to.state, to.url);
|
|
181
|
+
to.applyNavigationState(newState);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
replace(to, from, router) {
|
|
185
|
+
router.transition.route = to;
|
|
186
|
+
router.microApp._update(router);
|
|
187
|
+
const newState = router.navigation.replace(to.state, to.url);
|
|
188
|
+
to.applyNavigationState(newState);
|
|
189
|
+
},
|
|
190
|
+
restartApp(to, from, router) {
|
|
191
|
+
router.transition.route = to;
|
|
192
|
+
router.microApp._update(router, true);
|
|
193
|
+
const newState = router.navigation.replace(to.state, to.url);
|
|
194
|
+
to.applyNavigationState(newState);
|
|
195
|
+
},
|
|
196
|
+
pushWindow(to, from, router) {
|
|
197
|
+
return router.parsedOptions.fallback(to, from, router);
|
|
198
|
+
},
|
|
199
|
+
replaceWindow(to, from, router) {
|
|
200
|
+
return router.parsedOptions.fallback(to, from, router);
|
|
201
|
+
},
|
|
202
|
+
async pushLayer(to, from, router) {
|
|
203
|
+
const { promise } = await router.createLayer(to);
|
|
204
|
+
return promise;
|
|
205
|
+
},
|
|
206
|
+
default(to, from, router) {
|
|
207
|
+
router.transition.route = to;
|
|
208
|
+
router.microApp._update(router);
|
|
209
|
+
if (!isUrlEqual(to.url, from == null ? void 0 : from.url)) {
|
|
210
|
+
const newState = router.navigation.replace(to.state, to.url);
|
|
211
|
+
to.applyNavigationState(newState);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const ROUTE_TRANSITION_PIPELINE = {
|
|
216
|
+
[RouteType.push]: [
|
|
217
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
218
|
+
ROUTE_TRANSITION_HOOKS.override,
|
|
219
|
+
ROUTE_TRANSITION_HOOKS.beforeLeave,
|
|
220
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
221
|
+
ROUTE_TRANSITION_HOOKS.beforeUpdate,
|
|
222
|
+
ROUTE_TRANSITION_HOOKS.beforeEnter,
|
|
223
|
+
ROUTE_TRANSITION_HOOKS.asyncComponent,
|
|
224
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
225
|
+
],
|
|
226
|
+
[RouteType.replace]: [
|
|
227
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
228
|
+
ROUTE_TRANSITION_HOOKS.override,
|
|
229
|
+
ROUTE_TRANSITION_HOOKS.beforeLeave,
|
|
230
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
231
|
+
ROUTE_TRANSITION_HOOKS.beforeUpdate,
|
|
232
|
+
ROUTE_TRANSITION_HOOKS.beforeEnter,
|
|
233
|
+
ROUTE_TRANSITION_HOOKS.asyncComponent,
|
|
234
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
235
|
+
],
|
|
236
|
+
[RouteType.pushWindow]: [
|
|
237
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
238
|
+
ROUTE_TRANSITION_HOOKS.override,
|
|
239
|
+
// ROUTE_TRANSITION_HOOKS.beforeLeave
|
|
240
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
241
|
+
// ROUTE_TRANSITION_HOOKS.beforeUpdate
|
|
242
|
+
// ROUTE_TRANSITION_HOOKS.beforeEnter
|
|
243
|
+
// ROUTE_TRANSITION_HOOKS.asyncComponent
|
|
244
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
245
|
+
],
|
|
246
|
+
[RouteType.replaceWindow]: [
|
|
247
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
248
|
+
ROUTE_TRANSITION_HOOKS.override,
|
|
249
|
+
ROUTE_TRANSITION_HOOKS.beforeLeave,
|
|
250
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
251
|
+
// ROUTE_TRANSITION_HOOKS.beforeUpdate
|
|
252
|
+
// ROUTE_TRANSITION_HOOKS.beforeEnter
|
|
253
|
+
// ROUTE_TRANSITION_HOOKS.asyncComponent
|
|
254
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
255
|
+
],
|
|
256
|
+
[RouteType.pushLayer]: [
|
|
257
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
258
|
+
ROUTE_TRANSITION_HOOKS.override,
|
|
259
|
+
// ROUTE_TRANSITION_HOOKS.beforeLeave
|
|
260
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
261
|
+
// ROUTE_TRANSITION_HOOKS.beforeUpdate
|
|
262
|
+
// ROUTE_TRANSITION_HOOKS.beforeEnter
|
|
263
|
+
// ROUTE_TRANSITION_HOOKS.asyncComponent
|
|
264
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
265
|
+
],
|
|
266
|
+
[RouteType.restartApp]: [
|
|
267
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
268
|
+
// ROUTE_TRANSITION_HOOKS.override,
|
|
269
|
+
ROUTE_TRANSITION_HOOKS.beforeLeave,
|
|
270
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
271
|
+
ROUTE_TRANSITION_HOOKS.beforeUpdate,
|
|
272
|
+
ROUTE_TRANSITION_HOOKS.beforeEnter,
|
|
273
|
+
ROUTE_TRANSITION_HOOKS.asyncComponent,
|
|
274
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
275
|
+
],
|
|
276
|
+
[RouteType.unknown]: [
|
|
277
|
+
ROUTE_TRANSITION_HOOKS.fallback,
|
|
278
|
+
// ROUTE_TRANSITION_HOOKS.override,
|
|
279
|
+
ROUTE_TRANSITION_HOOKS.beforeLeave,
|
|
280
|
+
ROUTE_TRANSITION_HOOKS.beforeEach,
|
|
281
|
+
ROUTE_TRANSITION_HOOKS.beforeUpdate,
|
|
282
|
+
ROUTE_TRANSITION_HOOKS.beforeEnter,
|
|
283
|
+
ROUTE_TRANSITION_HOOKS.asyncComponent,
|
|
284
|
+
ROUTE_TRANSITION_HOOKS.confirm
|
|
285
|
+
]
|
|
286
|
+
};
|
|
287
|
+
export class RouteTransition {
|
|
288
|
+
constructor(router) {
|
|
289
|
+
__publicField(this, "router");
|
|
290
|
+
__publicField(this, "route", null);
|
|
291
|
+
// Task controller for the current transition.
|
|
292
|
+
__publicField(this, "_controller", null);
|
|
293
|
+
// Guard arrays, responsible for storing navigation guards.
|
|
294
|
+
__publicField(this, "guards", {
|
|
295
|
+
beforeEach: [],
|
|
296
|
+
afterEach: []
|
|
297
|
+
});
|
|
298
|
+
this.router = router;
|
|
299
|
+
}
|
|
300
|
+
beforeEach(guard) {
|
|
301
|
+
this.guards.beforeEach.push(guard);
|
|
302
|
+
return () => {
|
|
303
|
+
removeFromArray(this.guards.beforeEach, guard);
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
afterEach(guard) {
|
|
307
|
+
this.guards.afterEach.push(guard);
|
|
308
|
+
return () => {
|
|
309
|
+
removeFromArray(this.guards.afterEach, guard);
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
destroy() {
|
|
313
|
+
var _a;
|
|
314
|
+
(_a = this._controller) == null ? void 0 : _a.abort();
|
|
315
|
+
this._controller = null;
|
|
316
|
+
}
|
|
317
|
+
async to(toType, toInput) {
|
|
318
|
+
var _a;
|
|
319
|
+
const from = this.route;
|
|
320
|
+
const to = await this._runTask(
|
|
321
|
+
new Route({
|
|
322
|
+
options: this.router.parsedOptions,
|
|
323
|
+
toType,
|
|
324
|
+
toInput,
|
|
325
|
+
from: (_a = from == null ? void 0 : from.url) != null ? _a : null
|
|
326
|
+
}),
|
|
327
|
+
from
|
|
328
|
+
);
|
|
329
|
+
if (typeof to.handle === "function") {
|
|
330
|
+
to.handleResult = await to.handle(to, from, this.router);
|
|
331
|
+
}
|
|
332
|
+
if (to.handle) {
|
|
333
|
+
for (const guard of this.guards.afterEach) {
|
|
334
|
+
guard(to, from, this.router);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return to;
|
|
338
|
+
}
|
|
339
|
+
async _runTask(to, from) {
|
|
340
|
+
var _a;
|
|
341
|
+
(_a = this._controller) == null ? void 0 : _a.abort();
|
|
342
|
+
this._controller = new RouteTaskController();
|
|
343
|
+
const taskFunctions = ROUTE_TRANSITION_PIPELINE[to.type] || ROUTE_TRANSITION_PIPELINE[RouteType.unknown];
|
|
344
|
+
const tasks = taskFunctions.map((taskFn) => ({
|
|
345
|
+
name: taskFn.name,
|
|
346
|
+
task: taskFn
|
|
347
|
+
}));
|
|
348
|
+
return createRouteTask({
|
|
349
|
+
to,
|
|
350
|
+
from,
|
|
351
|
+
tasks,
|
|
352
|
+
controller: this._controller,
|
|
353
|
+
router: this.router
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
package/dist/route.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { type RouteConfirmHook, type RouteHandleHook, type RouteHandleResult, type RouteLayerOptions, type RouteLocationInput, type RouteMatchResult, type RouteMeta, type RouteOptions, type RouteParsedConfig, type RouteState, RouteType } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for non-enumerable properties in Route class
|
|
5
|
+
* These properties will be hidden during object traversal and serialization
|
|
6
|
+
*/
|
|
7
|
+
export declare const NON_ENUMERABLE_PROPERTIES: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Append user-provided parameters to URL path
|
|
10
|
+
* @param match Route matching result
|
|
11
|
+
* @param toInput User-provided route location object
|
|
12
|
+
* @param base Base URL
|
|
13
|
+
* @param to Current parsed URL object
|
|
14
|
+
*/
|
|
15
|
+
export declare function applyRouteParams(match: RouteMatchResult, toInput: RouteLocationInput, base: URL, to: URL): void;
|
|
16
|
+
/**
|
|
17
|
+
* Route class provides complete route object functionality
|
|
18
|
+
*/
|
|
19
|
+
export declare class Route {
|
|
20
|
+
private _handled;
|
|
21
|
+
private _handle;
|
|
22
|
+
private _handleResult;
|
|
23
|
+
private readonly _options;
|
|
24
|
+
readonly statusCode: number | null;
|
|
25
|
+
readonly state: RouteState;
|
|
26
|
+
readonly keepScrollPosition: boolean;
|
|
27
|
+
/** Custom confirm handler that overrides default route-transition confirm logic */
|
|
28
|
+
readonly confirm: RouteConfirmHook | null;
|
|
29
|
+
/** Layer configuration for layer routes */
|
|
30
|
+
readonly layer: RouteLayerOptions | null;
|
|
31
|
+
readonly type: RouteType;
|
|
32
|
+
readonly req: IncomingMessage | null;
|
|
33
|
+
readonly res: ServerResponse | null;
|
|
34
|
+
readonly context: Record<string | symbol, any>;
|
|
35
|
+
readonly url: URL;
|
|
36
|
+
readonly path: string;
|
|
37
|
+
readonly fullPath: string;
|
|
38
|
+
readonly hash: string;
|
|
39
|
+
readonly params: Record<string, string>;
|
|
40
|
+
readonly paramsArray: Record<string, string[]>;
|
|
41
|
+
readonly query: Record<string, string | undefined>;
|
|
42
|
+
readonly queryArray: Record<string, string[] | undefined>;
|
|
43
|
+
readonly meta: RouteMeta;
|
|
44
|
+
readonly matched: readonly RouteParsedConfig[];
|
|
45
|
+
readonly config: RouteParsedConfig | null;
|
|
46
|
+
/** @deprecated Use `url.pathname` instead. */
|
|
47
|
+
get pathname(): string;
|
|
48
|
+
/** @deprecated Use `url.href` instead. */
|
|
49
|
+
get href(): string;
|
|
50
|
+
constructor(routeOptions?: Partial<RouteOptions>);
|
|
51
|
+
get isPush(): boolean;
|
|
52
|
+
get handle(): RouteHandleHook | null;
|
|
53
|
+
set handle(val: RouteHandleHook | null);
|
|
54
|
+
get handleResult(): RouteHandleResult | null;
|
|
55
|
+
set handleResult(val: RouteHandleResult | null);
|
|
56
|
+
/**
|
|
57
|
+
* Set handle function with validation logic wrapper
|
|
58
|
+
*/
|
|
59
|
+
setHandle(val: RouteHandleHook | null): void;
|
|
60
|
+
/**
|
|
61
|
+
* Apply navigation-generated state to current route
|
|
62
|
+
* Used by route handlers to add system state like pageId
|
|
63
|
+
* @param navigationState Navigation-generated state to apply
|
|
64
|
+
*/
|
|
65
|
+
applyNavigationState(navigationState: Partial<RouteState>): void;
|
|
66
|
+
/**
|
|
67
|
+
* Sync all properties of current route to target route object
|
|
68
|
+
* Used for route object updates in reactive systems
|
|
69
|
+
* @param targetRoute Target route object
|
|
70
|
+
*/
|
|
71
|
+
syncTo(targetRoute: Route): void;
|
|
72
|
+
/**
|
|
73
|
+
* Clone current route instance
|
|
74
|
+
* Returns a new Route instance with same configuration and state
|
|
75
|
+
*/
|
|
76
|
+
clone(): Route;
|
|
77
|
+
}
|
package/dist/route.mjs
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { parseLocation, resolveRouteLocationInput } from "./location.mjs";
|
|
5
|
+
import { parsedOptions } from "./options.mjs";
|
|
6
|
+
import {
|
|
7
|
+
RouteType
|
|
8
|
+
} from "./types.mjs";
|
|
9
|
+
import { decodeParams, isNonEmptyPlainObject, isPlainObject } from "./util.mjs";
|
|
10
|
+
export const NON_ENUMERABLE_PROPERTIES = [
|
|
11
|
+
// Private fields - internal implementation details
|
|
12
|
+
"_handled",
|
|
13
|
+
"_handle",
|
|
14
|
+
"_handleResult",
|
|
15
|
+
"_options",
|
|
16
|
+
// SSR-specific properties - meaningless in client environment
|
|
17
|
+
"req",
|
|
18
|
+
"res",
|
|
19
|
+
// Internal context - used by framework internally
|
|
20
|
+
"context",
|
|
21
|
+
// Status code - internal status information
|
|
22
|
+
"statusCode",
|
|
23
|
+
// Route behavior overrides - framework internal logic
|
|
24
|
+
"confirm",
|
|
25
|
+
// Layer configuration - used for layer routes
|
|
26
|
+
"layer"
|
|
27
|
+
];
|
|
28
|
+
export function applyRouteParams(match, toInput, base, to) {
|
|
29
|
+
if (!isPlainObject(toInput) || !isNonEmptyPlainObject(toInput.params) || !match.matches.length) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const lastMatch = match.matches[match.matches.length - 1];
|
|
33
|
+
const current = to.pathname.split("/");
|
|
34
|
+
const next = new URL(
|
|
35
|
+
lastMatch.compile(toInput.params).substring(1),
|
|
36
|
+
base
|
|
37
|
+
).pathname.split("/");
|
|
38
|
+
next.forEach((item, index) => {
|
|
39
|
+
current[index] = item || current[index];
|
|
40
|
+
});
|
|
41
|
+
to.pathname = current.join("/");
|
|
42
|
+
Object.assign(match.params, toInput.params);
|
|
43
|
+
}
|
|
44
|
+
export class Route {
|
|
45
|
+
constructor(routeOptions = {}) {
|
|
46
|
+
// Private fields for handle validation
|
|
47
|
+
__publicField(this, "_handled", false);
|
|
48
|
+
__publicField(this, "_handle", null);
|
|
49
|
+
__publicField(this, "_handleResult", null);
|
|
50
|
+
__publicField(this, "_options");
|
|
51
|
+
// Public properties
|
|
52
|
+
__publicField(this, "statusCode", null);
|
|
53
|
+
__publicField(this, "state");
|
|
54
|
+
__publicField(this, "keepScrollPosition");
|
|
55
|
+
/** Custom confirm handler that overrides default route-transition confirm logic */
|
|
56
|
+
__publicField(this, "confirm");
|
|
57
|
+
/** Layer configuration for layer routes */
|
|
58
|
+
__publicField(this, "layer");
|
|
59
|
+
// Read-only properties
|
|
60
|
+
__publicField(this, "type");
|
|
61
|
+
__publicField(this, "req");
|
|
62
|
+
__publicField(this, "res");
|
|
63
|
+
__publicField(this, "context");
|
|
64
|
+
__publicField(this, "url");
|
|
65
|
+
__publicField(this, "path");
|
|
66
|
+
__publicField(this, "fullPath");
|
|
67
|
+
__publicField(this, "hash");
|
|
68
|
+
__publicField(this, "params", {});
|
|
69
|
+
__publicField(this, "paramsArray", {});
|
|
70
|
+
__publicField(this, "query", {});
|
|
71
|
+
__publicField(this, "queryArray", {});
|
|
72
|
+
__publicField(this, "meta");
|
|
73
|
+
__publicField(this, "matched");
|
|
74
|
+
__publicField(this, "config");
|
|
75
|
+
var _a;
|
|
76
|
+
const {
|
|
77
|
+
toType = RouteType.push,
|
|
78
|
+
from = null,
|
|
79
|
+
options = parsedOptions()
|
|
80
|
+
} = routeOptions;
|
|
81
|
+
this._options = options;
|
|
82
|
+
this.type = toType;
|
|
83
|
+
this.req = options.req;
|
|
84
|
+
this.res = options.res;
|
|
85
|
+
this.context = options.context;
|
|
86
|
+
const base = options.base;
|
|
87
|
+
const toInput = resolveRouteLocationInput(routeOptions.toInput, from);
|
|
88
|
+
const to = options.normalizeURL(parseLocation(toInput, base), from);
|
|
89
|
+
let match = null;
|
|
90
|
+
if (to.origin === base.origin && to.pathname.startsWith(base.pathname)) {
|
|
91
|
+
const isLayer = toType === RouteType.pushLayer || options.layer;
|
|
92
|
+
match = options.matcher(to, base, (config) => {
|
|
93
|
+
if (isLayer) {
|
|
94
|
+
return config.layer !== false;
|
|
95
|
+
}
|
|
96
|
+
return config.layer !== true;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (match) {
|
|
100
|
+
applyRouteParams(match, toInput, base, to);
|
|
101
|
+
const decodedParams = decodeParams(match.params);
|
|
102
|
+
for (const key in decodedParams) {
|
|
103
|
+
const value = decodedParams[key];
|
|
104
|
+
if (Array.isArray(value)) {
|
|
105
|
+
this.params[key] = value[0] || "";
|
|
106
|
+
this.paramsArray[key] = value;
|
|
107
|
+
} else {
|
|
108
|
+
this.params[key] = value;
|
|
109
|
+
this.paramsArray[key] = [value];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.url = to;
|
|
114
|
+
this.path = match ? to.pathname.substring(base.pathname.length - 1) : to.pathname;
|
|
115
|
+
this.fullPath = (match ? this.path : to.pathname) + to.search + to.hash;
|
|
116
|
+
this.matched = match ? match.matches : Object.freeze([]);
|
|
117
|
+
this.keepScrollPosition = Boolean(toInput.keepScrollPosition);
|
|
118
|
+
this.confirm = toInput.confirm || null;
|
|
119
|
+
this.layer = toType === RouteType.pushLayer && toInput.layer ? toInput.layer : null;
|
|
120
|
+
this.config = this.matched.length > 0 ? this.matched[this.matched.length - 1] : null;
|
|
121
|
+
this.meta = ((_a = this.config) == null ? void 0 : _a.meta) || {};
|
|
122
|
+
const state = {};
|
|
123
|
+
if (toInput.state) {
|
|
124
|
+
Object.assign(state, toInput.state);
|
|
125
|
+
}
|
|
126
|
+
this.state = state;
|
|
127
|
+
for (const key of new Set(to.searchParams.keys())) {
|
|
128
|
+
this.query[key] = to.searchParams.get(key);
|
|
129
|
+
this.queryArray[key] = to.searchParams.getAll(key);
|
|
130
|
+
}
|
|
131
|
+
this.hash = to.hash;
|
|
132
|
+
if (typeof toInput.statusCode === "number") {
|
|
133
|
+
this.statusCode = toInput.statusCode;
|
|
134
|
+
}
|
|
135
|
+
for (const property of NON_ENUMERABLE_PROPERTIES) {
|
|
136
|
+
Object.defineProperty(this, property, { enumerable: false });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** @deprecated Use `url.pathname` instead. */
|
|
140
|
+
get pathname() {
|
|
141
|
+
return this.url.pathname;
|
|
142
|
+
}
|
|
143
|
+
/** @deprecated Use `url.href` instead. */
|
|
144
|
+
get href() {
|
|
145
|
+
return this.url.href;
|
|
146
|
+
}
|
|
147
|
+
get isPush() {
|
|
148
|
+
return this.type.startsWith("push");
|
|
149
|
+
}
|
|
150
|
+
// handle related getter/setter
|
|
151
|
+
get handle() {
|
|
152
|
+
return this._handle;
|
|
153
|
+
}
|
|
154
|
+
set handle(val) {
|
|
155
|
+
this.setHandle(val);
|
|
156
|
+
}
|
|
157
|
+
get handleResult() {
|
|
158
|
+
return this._handleResult;
|
|
159
|
+
}
|
|
160
|
+
set handleResult(val) {
|
|
161
|
+
this._handleResult = val;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Set handle function with validation logic wrapper
|
|
165
|
+
*/
|
|
166
|
+
setHandle(val) {
|
|
167
|
+
if (typeof val !== "function") {
|
|
168
|
+
this._handle = null;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const self = this;
|
|
172
|
+
this._handle = function handle(to, from, router) {
|
|
173
|
+
if (self._handled) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
"Route handle hook can only be called once per navigation"
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
self._handled = true;
|
|
179
|
+
return val.call(this, to, from, router);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Apply navigation-generated state to current route
|
|
184
|
+
* Used by route handlers to add system state like pageId
|
|
185
|
+
* @param navigationState Navigation-generated state to apply
|
|
186
|
+
*/
|
|
187
|
+
applyNavigationState(navigationState) {
|
|
188
|
+
Object.assign(this.state, navigationState);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Sync all properties of current route to target route object
|
|
192
|
+
* Used for route object updates in reactive systems
|
|
193
|
+
* @param targetRoute Target route object
|
|
194
|
+
*/
|
|
195
|
+
syncTo(targetRoute) {
|
|
196
|
+
Object.assign(targetRoute, this);
|
|
197
|
+
for (const property of NON_ENUMERABLE_PROPERTIES) {
|
|
198
|
+
if (!(property in this && property in targetRoute)) continue;
|
|
199
|
+
const value = Reflect.get(this, property);
|
|
200
|
+
Reflect.set(targetRoute, property, value);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Clone current route instance
|
|
205
|
+
* Returns a new Route instance with same configuration and state
|
|
206
|
+
*/
|
|
207
|
+
clone() {
|
|
208
|
+
const toInput = {
|
|
209
|
+
path: this.fullPath,
|
|
210
|
+
state: { ...this.state },
|
|
211
|
+
...this.confirm && { confirm: this.confirm },
|
|
212
|
+
...this.layer && { layer: this.layer },
|
|
213
|
+
...this.statusCode !== null && { statusCode: this.statusCode }
|
|
214
|
+
};
|
|
215
|
+
const options = this._options;
|
|
216
|
+
const clonedRoute = new Route({
|
|
217
|
+
options,
|
|
218
|
+
toType: this.type,
|
|
219
|
+
toInput
|
|
220
|
+
});
|
|
221
|
+
return clonedRoute;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Router } from './router';
|
|
2
|
+
import type { RouterLinkProps, RouterLinkResolved } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Framework-agnostic link resolver
|
|
5
|
+
*
|
|
6
|
+
* @param router Router instance
|
|
7
|
+
* @param props Link configuration
|
|
8
|
+
* @returns Resolution result
|
|
9
|
+
*/
|
|
10
|
+
export declare function createLinkResolver(router: Router, props: RouterLinkProps): RouterLinkResolved;
|