@esmx/router-vue 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 +570 -0
- package/README.zh-CN.md +570 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +13 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.mjs +216 -0
- package/dist/plugin.d.ts +61 -0
- package/dist/plugin.mjs +41 -0
- package/dist/plugin.test.d.ts +1 -0
- package/dist/plugin.test.mjs +631 -0
- package/dist/router-link.d.ts +220 -0
- package/dist/router-link.mjs +119 -0
- package/dist/router-link.test.d.ts +1 -0
- package/dist/router-link.test.mjs +663 -0
- package/dist/router-view.d.ts +31 -0
- package/dist/router-view.mjs +15 -0
- package/dist/router-view.test.d.ts +1 -0
- package/dist/router-view.test.mjs +676 -0
- package/dist/run-with-context.test.d.ts +1 -0
- package/dist/run-with-context.test.mjs +57 -0
- package/dist/use.d.ts +260 -0
- package/dist/use.mjs +125 -0
- package/dist/use.test.d.ts +1 -0
- package/dist/use.test.mjs +381 -0
- package/dist/util.d.ts +20 -0
- package/dist/util.mjs +49 -0
- package/dist/util.test.d.ts +4 -0
- package/dist/util.test.mjs +604 -0
- package/dist/vue2.d.ts +15 -0
- package/dist/vue2.mjs +0 -0
- package/dist/vue3.d.ts +13 -0
- package/dist/vue3.mjs +0 -0
- package/package.json +85 -0
- package/src/index.test.ts +273 -0
- package/src/index.ts +15 -0
- package/src/plugin.test.ts +812 -0
- package/src/plugin.ts +107 -0
- package/src/router-link.test.ts +830 -0
- package/src/router-link.ts +172 -0
- package/src/router-view.test.ts +840 -0
- package/src/router-view.ts +59 -0
- package/src/run-with-context.test.ts +64 -0
- package/src/use.test.ts +484 -0
- package/src/use.ts +416 -0
- package/src/util.test.ts +760 -0
- package/src/util.ts +85 -0
- package/src/vue2.ts +18 -0
- package/src/vue3.ts +15 -0
package/src/use.ts
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import type { Route, Router, RouterLinkProps } from '@esmx/router';
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
getCurrentInstance,
|
|
5
|
+
inject,
|
|
6
|
+
onBeforeUnmount,
|
|
7
|
+
provide,
|
|
8
|
+
ref
|
|
9
|
+
} from 'vue';
|
|
10
|
+
import {
|
|
11
|
+
createDependentProxy,
|
|
12
|
+
createSymbolProperty,
|
|
13
|
+
defineRouterProperties,
|
|
14
|
+
isVue2
|
|
15
|
+
} from './util';
|
|
16
|
+
|
|
17
|
+
export interface VueInstance {
|
|
18
|
+
$parent?: VueInstance | null;
|
|
19
|
+
$root?: VueInstance | null;
|
|
20
|
+
$children?: VueInstance[] | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface RouterContext {
|
|
24
|
+
router: Router;
|
|
25
|
+
route: Route;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ROUTER_CONTEXT_KEY = Symbol('router-context');
|
|
29
|
+
const ROUTER_INJECT_KEY = Symbol('router-inject');
|
|
30
|
+
const ROUTER_VIEW_DEPTH_KEY = Symbol('router-view-depth');
|
|
31
|
+
|
|
32
|
+
const routerContextProperty =
|
|
33
|
+
createSymbolProperty<RouterContext>(ROUTER_CONTEXT_KEY);
|
|
34
|
+
|
|
35
|
+
const routerViewDepthProperty = createSymbolProperty<number>(
|
|
36
|
+
ROUTER_VIEW_DEPTH_KEY
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function getCurrentProxy(): VueInstance {
|
|
40
|
+
const instance = getCurrentInstance();
|
|
41
|
+
if (!instance || !instance.proxy) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
'[@esmx/router-vue] Must be used within setup() or other composition functions'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return instance.proxy;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function findRouterContext(vm: VueInstance): RouterContext {
|
|
50
|
+
let context = routerContextProperty.get(vm);
|
|
51
|
+
if (context) {
|
|
52
|
+
return context;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let current = vm.$parent;
|
|
56
|
+
while (current) {
|
|
57
|
+
context = routerContextProperty.get(current);
|
|
58
|
+
if (context) {
|
|
59
|
+
routerContextProperty.set(vm, context);
|
|
60
|
+
return context;
|
|
61
|
+
}
|
|
62
|
+
current = current.$parent;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw new Error(
|
|
66
|
+
'[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component.'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get router instance from a Vue component instance.
|
|
72
|
+
* This is a lower-level function used internally by useRouter().
|
|
73
|
+
* Use this in Options API, use useRouter() in Composition API.
|
|
74
|
+
*
|
|
75
|
+
* @param instance - Vue component instance
|
|
76
|
+
* @returns Router instance
|
|
77
|
+
* @throws {Error} If router context is not found
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Options API usage
|
|
82
|
+
* import { defineComponent } from 'vue';
|
|
83
|
+
* import { getRouter } from '@esmx/router-vue';
|
|
84
|
+
*
|
|
85
|
+
* export default defineComponent({
|
|
86
|
+
* mounted() {
|
|
87
|
+
* const router = getRouter(this);
|
|
88
|
+
* router.push('/dashboard');
|
|
89
|
+
* },
|
|
90
|
+
* methods: {
|
|
91
|
+
* handleNavigation() {
|
|
92
|
+
* const router = getRouter(this);
|
|
93
|
+
* router.replace('/profile');
|
|
94
|
+
* }
|
|
95
|
+
* }
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function getRouter(instance: VueInstance): Router {
|
|
100
|
+
return findRouterContext(instance).router;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get current route from a Vue component instance.
|
|
105
|
+
* This is a lower-level function used internally by useRoute().
|
|
106
|
+
* Use this in Options API, use useRoute() in Composition API.
|
|
107
|
+
*
|
|
108
|
+
* @param instance - Vue component instance
|
|
109
|
+
* @returns Current route object
|
|
110
|
+
* @throws {Error} If router context is not found
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* // Options API usage
|
|
115
|
+
* import { defineComponent } from 'vue';
|
|
116
|
+
* import { getRoute } from '@esmx/router-vue';
|
|
117
|
+
*
|
|
118
|
+
* export default defineComponent({
|
|
119
|
+
* computed: {
|
|
120
|
+
* routeInfo() {
|
|
121
|
+
* const route = getRoute(this);
|
|
122
|
+
* return {
|
|
123
|
+
* path: route.path,
|
|
124
|
+
* params: route.params,
|
|
125
|
+
* query: route.query
|
|
126
|
+
* };
|
|
127
|
+
* }
|
|
128
|
+
* }
|
|
129
|
+
* });
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function getRoute(instance: VueInstance): Route {
|
|
133
|
+
return findRouterContext(instance).route;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get router context using the optimal method available.
|
|
138
|
+
* First tries provide/inject (works in setup), then falls back to hierarchy traversal.
|
|
139
|
+
*/
|
|
140
|
+
function useRouterContext(): RouterContext {
|
|
141
|
+
// First try to get context from provide/inject (works in setup)
|
|
142
|
+
const injectedContext = inject<RouterContext>(ROUTER_INJECT_KEY);
|
|
143
|
+
if (injectedContext) {
|
|
144
|
+
return injectedContext;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Fallback to component hierarchy traversal (works after mount)
|
|
148
|
+
const proxy = getCurrentProxy();
|
|
149
|
+
return findRouterContext(proxy);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get the router instance in a Vue component.
|
|
154
|
+
* Must be called within setup() or other composition functions.
|
|
155
|
+
* Use this in Composition API, use getRouter() in Options API.
|
|
156
|
+
*
|
|
157
|
+
* @returns Router instance for navigation and route management
|
|
158
|
+
* @throws {Error} If called outside setup() or router context not found
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```vue
|
|
162
|
+
* <script setup lang="ts">
|
|
163
|
+
* import { useRouter } from '@esmx/router-vue';
|
|
164
|
+
*
|
|
165
|
+
* const router = useRouter();
|
|
166
|
+
*
|
|
167
|
+
* const navigateToHome = () => {
|
|
168
|
+
* router.push('/home');
|
|
169
|
+
* };
|
|
170
|
+
*
|
|
171
|
+
* const goBack = () => {
|
|
172
|
+
* router.back();
|
|
173
|
+
* };
|
|
174
|
+
*
|
|
175
|
+
* const navigateWithQuery = () => {
|
|
176
|
+
* router.push({
|
|
177
|
+
* path: '/search',
|
|
178
|
+
* query: { q: 'vue router', page: '1' }
|
|
179
|
+
* });
|
|
180
|
+
* };
|
|
181
|
+
* </script>
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
export function useRouter(): Router {
|
|
185
|
+
return useRouterContext().router;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get the current route information in a Vue component.
|
|
190
|
+
* Returns a reactive reference that automatically updates when the route changes.
|
|
191
|
+
* Must be called within setup() or other composition functions.
|
|
192
|
+
* Use this in Composition API, use getRoute() in Options API.
|
|
193
|
+
*
|
|
194
|
+
* @returns Current route object with path, params, query, etc.
|
|
195
|
+
* @throws {Error} If called outside setup() or router context not found
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```vue
|
|
199
|
+
* <template>
|
|
200
|
+
* <div>
|
|
201
|
+
* <h1>{{ route.meta?.title || 'Page' }}</h1>
|
|
202
|
+
* <p>Path: {{ route.path }}</p>
|
|
203
|
+
* <p>Params: {{ JSON.stringify(route.params) }}</p>
|
|
204
|
+
* <p>Query: {{ JSON.stringify(route.query) }}</p>
|
|
205
|
+
* </div>
|
|
206
|
+
* </template>
|
|
207
|
+
*
|
|
208
|
+
* <script setup lang="ts">
|
|
209
|
+
* import { useRoute } from '@esmx/router-vue';
|
|
210
|
+
* import { watch } from 'vue';
|
|
211
|
+
*
|
|
212
|
+
* const route = useRoute();
|
|
213
|
+
*
|
|
214
|
+
* watch(() => route.path, (newPath) => {
|
|
215
|
+
* console.log('Route changed to:', newPath);
|
|
216
|
+
* });
|
|
217
|
+
* </script>
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export function useRoute(): Route {
|
|
221
|
+
return useRouterContext().route;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Provide router context to child components.
|
|
226
|
+
* This must be called in a parent component to make the router available
|
|
227
|
+
* to child components via useRouter() and useRoute().
|
|
228
|
+
*
|
|
229
|
+
* @param router - Router instance to provide to child components
|
|
230
|
+
* @throws {Error} If called outside setup()
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* // Vue 3 usage
|
|
235
|
+
* import { createApp } from 'vue';
|
|
236
|
+
* import { Router } from '@esmx/router';
|
|
237
|
+
* import { useProvideRouter } from '@esmx/router-vue';
|
|
238
|
+
*
|
|
239
|
+
* const routes = [
|
|
240
|
+
* { path: '/', component: () => import('./Home.vue') },
|
|
241
|
+
* { path: '/about', component: () => import('./About.vue') }
|
|
242
|
+
* ];
|
|
243
|
+
*
|
|
244
|
+
* const router = new Router({ routes });
|
|
245
|
+
* const app = createApp({
|
|
246
|
+
* setup() {
|
|
247
|
+
* useProvideRouter(router);
|
|
248
|
+
* }
|
|
249
|
+
* });
|
|
250
|
+
* app.mount('#app');
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
export function useProvideRouter(router: Router): void {
|
|
254
|
+
const proxy = getCurrentProxy();
|
|
255
|
+
|
|
256
|
+
const dep = ref(0);
|
|
257
|
+
|
|
258
|
+
const proxiedRouter = createDependentProxy(router, dep);
|
|
259
|
+
const proxiedRoute = createDependentProxy(router.route, dep);
|
|
260
|
+
|
|
261
|
+
const context: RouterContext = {
|
|
262
|
+
router: proxiedRouter,
|
|
263
|
+
route: proxiedRoute
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
provide(ROUTER_INJECT_KEY, context);
|
|
267
|
+
routerContextProperty.set(proxy, context);
|
|
268
|
+
|
|
269
|
+
if (!isVue2) {
|
|
270
|
+
const app = getCurrentInstance()!.appContext.app;
|
|
271
|
+
defineRouterProperties(
|
|
272
|
+
app.config.globalProperties,
|
|
273
|
+
() => proxiedRouter,
|
|
274
|
+
() => proxiedRoute,
|
|
275
|
+
true
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const unwatch = router.afterEach((to: Route) => {
|
|
280
|
+
if (router.route === to) {
|
|
281
|
+
to.syncTo(proxiedRoute);
|
|
282
|
+
dep.value++;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
onBeforeUnmount(unwatch);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get the current RouterView depth in nested routing scenarios.
|
|
291
|
+
* Returns the depth of the current RouterView component in the component tree.
|
|
292
|
+
* Useful for advanced routing scenarios where you need to know the nesting level.
|
|
293
|
+
*
|
|
294
|
+
* @param isRender - Whether this is used in a RouterView component that needs to provide depth for children (default: false)
|
|
295
|
+
* @returns Current RouterView depth (0 for root level, 1 for first nested level, etc.)
|
|
296
|
+
* @throws {Error} If called outside setup()
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```vue
|
|
300
|
+
* <template>
|
|
301
|
+
* <div>
|
|
302
|
+
* <p>Current RouterView depth: {{ depth }}</p>
|
|
303
|
+
* <RouterView />
|
|
304
|
+
* </div>
|
|
305
|
+
* </template>
|
|
306
|
+
*
|
|
307
|
+
* <script setup lang="ts">
|
|
308
|
+
* import { useRouterViewDepth } from '@esmx/router-vue';
|
|
309
|
+
*
|
|
310
|
+
* // Get current depth without providing for children
|
|
311
|
+
* const depth = useRouterViewDepth();
|
|
312
|
+
* console.log('Current RouterView depth:', depth); // 0, 1, 2, etc.
|
|
313
|
+
*
|
|
314
|
+
* // Get current depth and provide depth + 1 for children (used in RouterView component)
|
|
315
|
+
* const depth = useRouterViewDepth(true);
|
|
316
|
+
* </script>
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function _useRouterViewDepth(isRender?: boolean): number {
|
|
320
|
+
const depth = inject(ROUTER_VIEW_DEPTH_KEY, 0);
|
|
321
|
+
|
|
322
|
+
if (isRender) {
|
|
323
|
+
provide(ROUTER_VIEW_DEPTH_KEY, depth + 1);
|
|
324
|
+
const proxy = getCurrentProxy();
|
|
325
|
+
routerViewDepthProperty.set(proxy, depth + 1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return depth;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Get the current RouterView depth in nested routing scenarios.
|
|
332
|
+
* Returns the depth of the current RouterView component in the component tree.
|
|
333
|
+
* Useful for advanced routing scenarios where you need to know the nesting level.
|
|
334
|
+
*
|
|
335
|
+
* @returns Current RouterView depth (0 for root level, 1 for first nested level, etc.)
|
|
336
|
+
* @throws {Error} If called outside setup()
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```vue
|
|
340
|
+
* <template>
|
|
341
|
+
* <div>
|
|
342
|
+
* <p>Current RouterView depth: {{ depth }}</p>
|
|
343
|
+
* <RouterView />
|
|
344
|
+
* </div>
|
|
345
|
+
* </template>
|
|
346
|
+
*
|
|
347
|
+
* <script setup lang="ts">
|
|
348
|
+
* import { useRouterViewDepth } from '@esmx/router-vue';
|
|
349
|
+
*
|
|
350
|
+
* // Get current depth without providing for children
|
|
351
|
+
* const depth = useRouterViewDepth();
|
|
352
|
+
* console.log('Current RouterView depth:', depth); // 0, 1, 2, etc.
|
|
353
|
+
* </script>
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
export function useRouterViewDepth(): number {
|
|
357
|
+
return _useRouterViewDepth();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Get injected RouterView depth from a Vue instance's ancestors.
|
|
362
|
+
* Traverses parent chain to find the value provided under ROUTER_VIEW_DEPTH_KEY.
|
|
363
|
+
*
|
|
364
|
+
* @param instance - Vue component instance to start from
|
|
365
|
+
* @returns Injected RouterView depth value from nearest ancestor
|
|
366
|
+
* @throws {Error} If no ancestor provided ROUTER_VIEW_DEPTH_KEY
|
|
367
|
+
*/
|
|
368
|
+
export function getRouterViewDepth(instance: VueInstance): number {
|
|
369
|
+
let current = instance.$parent;
|
|
370
|
+
while (current) {
|
|
371
|
+
const value = routerViewDepthProperty.get(current);
|
|
372
|
+
if (typeof value === 'number') return value;
|
|
373
|
+
current = current.$parent;
|
|
374
|
+
}
|
|
375
|
+
throw new Error(
|
|
376
|
+
'[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components.'
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Create reactive link helpers for navigation elements.
|
|
382
|
+
* Returns computed properties for link attributes, classes, and event handlers.
|
|
383
|
+
*
|
|
384
|
+
* @param props - RouterLink properties configuration
|
|
385
|
+
* @returns Computed link resolver with attributes and event handlers
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```vue
|
|
389
|
+
* <template>
|
|
390
|
+
* <a
|
|
391
|
+
* v-bind="link.attributes"
|
|
392
|
+
* v-on="link.createEventHandlers()"
|
|
393
|
+
* :class="{ active: link.isActive }"
|
|
394
|
+
* >
|
|
395
|
+
* Home
|
|
396
|
+
* </a>
|
|
397
|
+
* </template>
|
|
398
|
+
*
|
|
399
|
+
* <script setup lang="ts">
|
|
400
|
+
* import { useLink } from '@esmx/router-vue';
|
|
401
|
+
*
|
|
402
|
+
* const link = useLink({
|
|
403
|
+
* to: '/home',
|
|
404
|
+
* type: 'push',
|
|
405
|
+
* exact: 'include'
|
|
406
|
+
* }).value;
|
|
407
|
+
* </script>
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
export function useLink(props: RouterLinkProps) {
|
|
411
|
+
const router = useRouter();
|
|
412
|
+
|
|
413
|
+
return computed(() => {
|
|
414
|
+
return router.resolveLink(props);
|
|
415
|
+
});
|
|
416
|
+
}
|