@esmx/router-vue 3.0.0-rc.70 → 3.0.0-rc.71
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/README.md +13 -6
- package/README.zh-CN.md +12 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.test.mjs +30 -20
- package/dist/plugin.mjs +3 -0
- package/dist/plugin.test.mjs +3 -3
- package/dist/router-link.test.mjs +1 -1
- package/dist/router-view.test.mjs +6 -6
- package/dist/use.d.ts +9 -0
- package/dist/use.mjs +29 -13
- package/dist/use.test.mjs +220 -3
- package/package.json +7 -7
- package/src/index.test.ts +32 -22
- package/src/index.ts +1 -0
- package/src/plugin.test.ts +3 -3
- package/src/plugin.ts +4 -0
- package/src/router-link.test.ts +1 -1
- package/src/router-view.test.ts +6 -6
- package/src/use.test.ts +265 -3
- package/src/use.ts +39 -20
package/package.json
CHANGED
|
@@ -50,19 +50,19 @@
|
|
|
50
50
|
"vue": "^3.5.0 || ^2.7.0"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@esmx/router": "3.0.0-rc.
|
|
53
|
+
"@esmx/router": "3.0.0-rc.71"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@biomejs/biome": "1.9.4",
|
|
57
|
-
"@types/node": "^24.
|
|
57
|
+
"@types/node": "^24.10.0",
|
|
58
58
|
"@vitest/coverage-v8": "3.2.4",
|
|
59
|
-
"typescript": "5.9.
|
|
60
|
-
"unbuild": "3.6.
|
|
59
|
+
"typescript": "5.9.3",
|
|
60
|
+
"unbuild": "3.6.1",
|
|
61
61
|
"vitest": "3.2.4",
|
|
62
|
-
"vue": "3.5.
|
|
62
|
+
"vue": "3.5.23",
|
|
63
63
|
"vue2": "npm:vue@2.7.16"
|
|
64
64
|
},
|
|
65
|
-
"version": "3.0.0-rc.
|
|
65
|
+
"version": "3.0.0-rc.71",
|
|
66
66
|
"type": "module",
|
|
67
67
|
"private": false,
|
|
68
68
|
"exports": {
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"template",
|
|
82
82
|
"public"
|
|
83
83
|
],
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "1616c7a1f820387d4d14bac0babd42356f6f7f33"
|
|
85
85
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -25,6 +25,16 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
25
25
|
expect(RouterVueModule.useLink).toBeDefined();
|
|
26
26
|
expect(typeof RouterVueModule.useLink).toBe('function');
|
|
27
27
|
});
|
|
28
|
+
|
|
29
|
+
it('should export useRouterViewDepth function', () => {
|
|
30
|
+
expect(RouterVueModule.useRouterViewDepth).toBeDefined();
|
|
31
|
+
expect(typeof RouterVueModule.useRouterViewDepth).toBe('function');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should export getRouterViewDepth function', () => {
|
|
35
|
+
expect(RouterVueModule.getRouterViewDepth).toBeDefined();
|
|
36
|
+
expect(typeof RouterVueModule.getRouterViewDepth).toBe('function');
|
|
37
|
+
});
|
|
28
38
|
});
|
|
29
39
|
|
|
30
40
|
describe('Options API Exports', () => {
|
|
@@ -74,6 +84,7 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
74
84
|
'useProvideRouter',
|
|
75
85
|
'useLink',
|
|
76
86
|
'useRouterViewDepth',
|
|
87
|
+
'getRouterViewDepth',
|
|
77
88
|
// Options API
|
|
78
89
|
'getRouter',
|
|
79
90
|
'getRoute',
|
|
@@ -100,6 +111,7 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
100
111
|
'useProvideRouter',
|
|
101
112
|
'useLink',
|
|
102
113
|
'useRouterViewDepth',
|
|
114
|
+
'getRouterViewDepth',
|
|
103
115
|
'getRouter',
|
|
104
116
|
'getRoute',
|
|
105
117
|
'RouterLink',
|
|
@@ -121,11 +133,15 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
121
133
|
// These should throw expected errors when called without proper context
|
|
122
134
|
expect(() => {
|
|
123
135
|
RouterVueModule.useRouter();
|
|
124
|
-
}).toThrow(
|
|
136
|
+
}).toThrow(
|
|
137
|
+
'[@esmx/router-vue] Must be used within setup() or other composition functions'
|
|
138
|
+
);
|
|
125
139
|
|
|
126
140
|
expect(() => {
|
|
127
141
|
RouterVueModule.useRoute();
|
|
128
|
-
}).toThrow(
|
|
142
|
+
}).toThrow(
|
|
143
|
+
'[@esmx/router-vue] Must be used within setup() or other composition functions'
|
|
144
|
+
);
|
|
129
145
|
|
|
130
146
|
expect(() => {
|
|
131
147
|
RouterVueModule.useLink({
|
|
@@ -133,31 +149,23 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
133
149
|
type: 'push',
|
|
134
150
|
exact: 'include'
|
|
135
151
|
});
|
|
136
|
-
}).toThrow(
|
|
152
|
+
}).toThrow(
|
|
153
|
+
'[@esmx/router-vue] Must be used within setup() or other composition functions'
|
|
154
|
+
);
|
|
137
155
|
});
|
|
138
156
|
|
|
139
157
|
it('should have correct function signatures for Options API', () => {
|
|
140
158
|
expect(() => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
expect((error as Error).message).toContain(
|
|
146
|
-
'Router context not found'
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
}).not.toThrow();
|
|
159
|
+
RouterVueModule.getRouter({} as Record<string, unknown>);
|
|
160
|
+
}).toThrow(
|
|
161
|
+
'[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component.'
|
|
162
|
+
);
|
|
150
163
|
|
|
151
164
|
expect(() => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
expect((error as Error).message).toContain(
|
|
157
|
-
'Router context not found'
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
}).not.toThrow();
|
|
165
|
+
RouterVueModule.getRoute({} as Record<string, unknown>);
|
|
166
|
+
}).toThrow(
|
|
167
|
+
'[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component.'
|
|
168
|
+
);
|
|
161
169
|
});
|
|
162
170
|
});
|
|
163
171
|
|
|
@@ -200,7 +208,7 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
200
208
|
// Test plugin install signature - should throw for null input
|
|
201
209
|
expect(() => {
|
|
202
210
|
RouterPlugin.install(null);
|
|
203
|
-
}).toThrow();
|
|
211
|
+
}).toThrow('[@esmx/router-vue] Invalid Vue app instance');
|
|
204
212
|
});
|
|
205
213
|
});
|
|
206
214
|
|
|
@@ -221,6 +229,8 @@ describe('index.ts - Package Entry Point', () => {
|
|
|
221
229
|
'useRoute',
|
|
222
230
|
'useProvideRouter',
|
|
223
231
|
'useLink',
|
|
232
|
+
'useRouterViewDepth',
|
|
233
|
+
'getRouterViewDepth',
|
|
224
234
|
'getRouter',
|
|
225
235
|
'getRoute'
|
|
226
236
|
];
|
package/src/index.ts
CHANGED
package/src/plugin.test.ts
CHANGED
|
@@ -45,7 +45,7 @@ describe('plugin.ts - RouterPlugin', () => {
|
|
|
45
45
|
router = new Router({
|
|
46
46
|
mode: RouterMode.memory,
|
|
47
47
|
routes,
|
|
48
|
-
base: new URL('http://localhost:
|
|
48
|
+
base: new URL('http://localhost:8000/')
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
await router.replace('/');
|
|
@@ -88,13 +88,13 @@ describe('plugin.ts - RouterPlugin', () => {
|
|
|
88
88
|
it('should throw error for null app instance', () => {
|
|
89
89
|
expect(() => {
|
|
90
90
|
RouterPlugin.install(null);
|
|
91
|
-
}).toThrow();
|
|
91
|
+
}).toThrow('[@esmx/router-vue] Invalid Vue app instance');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
it('should throw error for undefined app instance', () => {
|
|
95
95
|
expect(() => {
|
|
96
96
|
RouterPlugin.install(undefined);
|
|
97
|
-
}).toThrow();
|
|
97
|
+
}).toThrow('[@esmx/router-vue] Invalid Vue app instance');
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
100
|
|
package/src/plugin.ts
CHANGED
|
@@ -71,6 +71,10 @@ export const RouterPlugin = {
|
|
|
71
71
|
* @param app Vue application instance (Vue 3) or Vue constructor (Vue 2)
|
|
72
72
|
*/
|
|
73
73
|
install(app: unknown): void {
|
|
74
|
+
if (!app) {
|
|
75
|
+
throw new Error('[@esmx/router-vue] Invalid Vue app instance');
|
|
76
|
+
}
|
|
77
|
+
|
|
74
78
|
const vueApp = app as VueApp;
|
|
75
79
|
const target = vueApp.config?.globalProperties || vueApp.prototype;
|
|
76
80
|
|
package/src/router-link.test.ts
CHANGED
|
@@ -52,7 +52,7 @@ describe('router-link.ts - RouterLink Component', () => {
|
|
|
52
52
|
root: '#test-app',
|
|
53
53
|
routes,
|
|
54
54
|
mode: RouterMode.memory,
|
|
55
|
-
base: new URL('http://localhost:
|
|
55
|
+
base: new URL('http://localhost:8000/')
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
// Initialize router and wait for it to be ready
|
package/src/router-view.test.ts
CHANGED
|
@@ -80,7 +80,7 @@ describe('router-view.ts - RouterView Component', () => {
|
|
|
80
80
|
root: '#test-app',
|
|
81
81
|
routes,
|
|
82
82
|
mode: RouterMode.memory,
|
|
83
|
-
base: new URL('http://localhost:
|
|
83
|
+
base: new URL('http://localhost:8000/')
|
|
84
84
|
});
|
|
85
85
|
|
|
86
86
|
// Initialize router to root path and wait for it to be ready
|
|
@@ -201,7 +201,7 @@ describe('router-view.ts - RouterView Component', () => {
|
|
|
201
201
|
root: '#test-app',
|
|
202
202
|
routes,
|
|
203
203
|
mode: RouterMode.memory,
|
|
204
|
-
base: new URL('http://localhost:
|
|
204
|
+
base: new URL('http://localhost:8000/')
|
|
205
205
|
});
|
|
206
206
|
|
|
207
207
|
// Initialize the router and wait for it to be ready
|
|
@@ -334,7 +334,7 @@ describe('router-view.ts - RouterView Component', () => {
|
|
|
334
334
|
root: '#test-app',
|
|
335
335
|
routes: nestedRoutes,
|
|
336
336
|
mode: RouterMode.memory,
|
|
337
|
-
base: new URL('http://localhost:
|
|
337
|
+
base: new URL('http://localhost:8000/')
|
|
338
338
|
});
|
|
339
339
|
|
|
340
340
|
// Initialize the router and wait for it to be ready
|
|
@@ -411,7 +411,7 @@ describe('router-view.ts - RouterView Component', () => {
|
|
|
411
411
|
root: '#test-app',
|
|
412
412
|
routes: routesWithNull,
|
|
413
413
|
mode: RouterMode.memory,
|
|
414
|
-
base: new URL('http://localhost:
|
|
414
|
+
base: new URL('http://localhost:8000/')
|
|
415
415
|
});
|
|
416
416
|
|
|
417
417
|
// Initialize the router and wait for it to be ready
|
|
@@ -451,7 +451,7 @@ describe('router-view.ts - RouterView Component', () => {
|
|
|
451
451
|
}
|
|
452
452
|
],
|
|
453
453
|
mode: RouterMode.memory,
|
|
454
|
-
base: new URL('http://localhost:
|
|
454
|
+
base: new URL('http://localhost:8000/')
|
|
455
455
|
});
|
|
456
456
|
|
|
457
457
|
// Initialize router with root path
|
|
@@ -504,7 +504,7 @@ describe('router-view.ts - RouterView Component', () => {
|
|
|
504
504
|
root: '#test-app',
|
|
505
505
|
routes: malformedRoutes,
|
|
506
506
|
mode: RouterMode.memory,
|
|
507
|
-
base: new URL('http://localhost:
|
|
507
|
+
base: new URL('http://localhost:8000/')
|
|
508
508
|
});
|
|
509
509
|
|
|
510
510
|
// Initialize the router and wait for it to be ready
|
package/src/use.test.ts
CHANGED
|
@@ -4,8 +4,15 @@ import { type Route, Router, RouterMode } from '@esmx/router';
|
|
|
4
4
|
*/
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
6
|
import { nextTick } from 'vue';
|
|
7
|
-
import { createApp, h } from 'vue';
|
|
8
|
-
import {
|
|
7
|
+
import { createApp, defineComponent, getCurrentInstance, h } from 'vue';
|
|
8
|
+
import { RouterView } from './router-view';
|
|
9
|
+
import {
|
|
10
|
+
getRouterViewDepth,
|
|
11
|
+
useProvideRouter,
|
|
12
|
+
useRoute,
|
|
13
|
+
useRouter,
|
|
14
|
+
useRouterViewDepth
|
|
15
|
+
} from './use';
|
|
9
16
|
|
|
10
17
|
describe('Router Vue Integration', () => {
|
|
11
18
|
let app: ReturnType<typeof createApp>;
|
|
@@ -22,7 +29,7 @@ describe('Router Vue Integration', () => {
|
|
|
22
29
|
{ path: '/user/:id', component: {} },
|
|
23
30
|
{ path: '/new-path', component: {} }
|
|
24
31
|
],
|
|
25
|
-
base: new URL('http://localhost:
|
|
32
|
+
base: new URL('http://localhost:8000/')
|
|
26
33
|
});
|
|
27
34
|
|
|
28
35
|
// Ensure navigation to initial route is complete
|
|
@@ -214,4 +221,259 @@ describe('Router Vue Integration', () => {
|
|
|
214
221
|
expect(childRoute?.path).toBe('/new-path');
|
|
215
222
|
});
|
|
216
223
|
});
|
|
224
|
+
|
|
225
|
+
describe('RouterView Depth', () => {
|
|
226
|
+
it('should get depth in single RouterView', async () => {
|
|
227
|
+
let observedDepth: number | undefined;
|
|
228
|
+
|
|
229
|
+
const LeafProbe = defineComponent({
|
|
230
|
+
setup() {
|
|
231
|
+
const p = getCurrentInstance()!.proxy as any;
|
|
232
|
+
observedDepth = getRouterViewDepth(p);
|
|
233
|
+
return () => h('div');
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const Level1 = defineComponent({
|
|
238
|
+
setup() {
|
|
239
|
+
return () => h('div', [h(LeafProbe)]);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
router = new Router({
|
|
244
|
+
mode: RouterMode.memory,
|
|
245
|
+
routes: [{ path: '/level1', component: Level1 }],
|
|
246
|
+
base: new URL('http://localhost:8000/')
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await router.replace('/level1');
|
|
250
|
+
|
|
251
|
+
const TestApp = defineComponent({
|
|
252
|
+
setup() {
|
|
253
|
+
useProvideRouter(router);
|
|
254
|
+
return () => h('div', [h(RouterView)]);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
app = createApp(TestApp);
|
|
259
|
+
app.mount('#app');
|
|
260
|
+
await nextTick();
|
|
261
|
+
|
|
262
|
+
expect(observedDepth).toBe(1);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should get depth in nested RouterView', async () => {
|
|
266
|
+
let observedDepth: number | undefined;
|
|
267
|
+
|
|
268
|
+
const LeafProbe = defineComponent({
|
|
269
|
+
setup() {
|
|
270
|
+
const p = getCurrentInstance()!.proxy as any;
|
|
271
|
+
observedDepth = getRouterViewDepth(p);
|
|
272
|
+
return () => h('div');
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const Level1 = defineComponent({
|
|
277
|
+
setup() {
|
|
278
|
+
return () => h('div', [h(RouterView)]);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const Leaf = defineComponent({
|
|
283
|
+
setup() {
|
|
284
|
+
return () => h('div', [h(LeafProbe)]);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
router = new Router({
|
|
289
|
+
mode: RouterMode.memory,
|
|
290
|
+
routes: [
|
|
291
|
+
{
|
|
292
|
+
path: '/level1',
|
|
293
|
+
component: Level1,
|
|
294
|
+
children: [{ path: 'leaf', component: Leaf }]
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
base: new URL('http://localhost:8000/')
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await router.replace('/level1/leaf');
|
|
301
|
+
|
|
302
|
+
const TestApp = defineComponent({
|
|
303
|
+
setup() {
|
|
304
|
+
useProvideRouter(router);
|
|
305
|
+
return () => h('div', [h(RouterView)]);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
app = createApp(TestApp);
|
|
310
|
+
app.mount('#app');
|
|
311
|
+
await nextTick();
|
|
312
|
+
|
|
313
|
+
expect(observedDepth).toBe(2);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should get depth in double-nested RouterViews', async () => {
|
|
317
|
+
let observedDepth: number | undefined;
|
|
318
|
+
|
|
319
|
+
const LeafProbe = defineComponent({
|
|
320
|
+
setup() {
|
|
321
|
+
const p = getCurrentInstance()!.proxy as any;
|
|
322
|
+
observedDepth = getRouterViewDepth(p);
|
|
323
|
+
return () => h('div');
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const Level1 = defineComponent({
|
|
328
|
+
setup() {
|
|
329
|
+
return () => h('div', [h(RouterView)]);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const Level2 = defineComponent({
|
|
334
|
+
setup() {
|
|
335
|
+
return () => h('div', [h(RouterView)]);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const Leaf = defineComponent({
|
|
340
|
+
setup() {
|
|
341
|
+
return () => h('div', [h(LeafProbe)]);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
router = new Router({
|
|
346
|
+
mode: RouterMode.memory,
|
|
347
|
+
routes: [
|
|
348
|
+
{
|
|
349
|
+
path: '/level1',
|
|
350
|
+
component: Level1,
|
|
351
|
+
children: [
|
|
352
|
+
{
|
|
353
|
+
path: 'level2',
|
|
354
|
+
component: Level2,
|
|
355
|
+
children: [{ path: 'leaf', component: Leaf }]
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
],
|
|
360
|
+
base: new URL('http://localhost:8000/')
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
await router.replace('/level1/level2/leaf');
|
|
364
|
+
|
|
365
|
+
const TestApp = defineComponent({
|
|
366
|
+
setup() {
|
|
367
|
+
useProvideRouter(router);
|
|
368
|
+
return () => h('div', [h(RouterView)]);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
app = createApp(TestApp);
|
|
373
|
+
app.mount('#app');
|
|
374
|
+
await nextTick();
|
|
375
|
+
|
|
376
|
+
expect(observedDepth).toBe(3);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should throw when no RouterView ancestor exists', async () => {
|
|
380
|
+
let callDepth: (() => void) | undefined;
|
|
381
|
+
|
|
382
|
+
const Probe = defineComponent({
|
|
383
|
+
setup() {
|
|
384
|
+
const p = getCurrentInstance()!.proxy as any;
|
|
385
|
+
callDepth = () => getRouterViewDepth(p);
|
|
386
|
+
return () => h('div');
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const TestApp = defineComponent({
|
|
391
|
+
setup() {
|
|
392
|
+
useProvideRouter(router);
|
|
393
|
+
return () => h(Probe);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
app = createApp(TestApp);
|
|
398
|
+
app.mount('#app');
|
|
399
|
+
await nextTick();
|
|
400
|
+
|
|
401
|
+
expect(() => callDepth!()).toThrow(
|
|
402
|
+
new Error(
|
|
403
|
+
'[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components.'
|
|
404
|
+
)
|
|
405
|
+
);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('should return 0 for useRouterViewDepth without RouterView', async () => {
|
|
409
|
+
let observed = -1;
|
|
410
|
+
|
|
411
|
+
const Probe = defineComponent({
|
|
412
|
+
setup() {
|
|
413
|
+
observed = useRouterViewDepth();
|
|
414
|
+
return () => h('div');
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const TestApp = defineComponent({
|
|
419
|
+
setup() {
|
|
420
|
+
useProvideRouter(router);
|
|
421
|
+
return () => h(Probe);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
app = createApp(TestApp);
|
|
426
|
+
app.mount('#app');
|
|
427
|
+
await nextTick();
|
|
428
|
+
|
|
429
|
+
expect(observed).toBe(0);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('should reflect depth via useRouterViewDepth at each level', async () => {
|
|
433
|
+
let level1Depth = -1;
|
|
434
|
+
let level2Depth = -1;
|
|
435
|
+
|
|
436
|
+
const Level2 = defineComponent({
|
|
437
|
+
setup() {
|
|
438
|
+
level2Depth = useRouterViewDepth();
|
|
439
|
+
return () => h('div');
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const Level1 = defineComponent({
|
|
444
|
+
setup() {
|
|
445
|
+
level1Depth = useRouterViewDepth();
|
|
446
|
+
return () => h('div', [h(RouterView)]);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
router = new Router({
|
|
451
|
+
mode: RouterMode.memory,
|
|
452
|
+
routes: [
|
|
453
|
+
{
|
|
454
|
+
path: '/level1',
|
|
455
|
+
component: Level1,
|
|
456
|
+
children: [{ path: 'level2', component: Level2 }]
|
|
457
|
+
}
|
|
458
|
+
],
|
|
459
|
+
base: new URL('http://localhost:8000/')
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
await router.replace('/level1/level2');
|
|
463
|
+
|
|
464
|
+
const TestApp = defineComponent({
|
|
465
|
+
setup() {
|
|
466
|
+
useProvideRouter(router);
|
|
467
|
+
return () => h('div', [h(RouterView)]);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
app = createApp(TestApp);
|
|
472
|
+
app.mount('#app');
|
|
473
|
+
await nextTick();
|
|
474
|
+
|
|
475
|
+
expect(level1Depth).toBe(1);
|
|
476
|
+
expect(level2Depth).toBe(2);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
217
479
|
});
|
package/src/use.ts
CHANGED
|
@@ -24,21 +24,19 @@ const ROUTER_CONTEXT_KEY = Symbol('router-context');
|
|
|
24
24
|
const ROUTER_INJECT_KEY = Symbol('router-inject');
|
|
25
25
|
const ROUTER_VIEW_DEPTH_KEY = Symbol('router-view-depth');
|
|
26
26
|
|
|
27
|
-
const ERROR_MESSAGES = {
|
|
28
|
-
SETUP_ONLY: (fnName: string) =>
|
|
29
|
-
`[@esmx/router-vue] ${fnName}() can only be called during setup()`,
|
|
30
|
-
CONTEXT_NOT_FOUND:
|
|
31
|
-
'[@esmx/router-vue] Router context not found. ' +
|
|
32
|
-
'Please ensure useProvideRouter() is called in a parent component.'
|
|
33
|
-
} as const;
|
|
34
|
-
|
|
35
27
|
const routerContextProperty =
|
|
36
28
|
createSymbolProperty<RouterContext>(ROUTER_CONTEXT_KEY);
|
|
37
29
|
|
|
38
|
-
|
|
30
|
+
const routerViewDepthProperty = createSymbolProperty<number>(
|
|
31
|
+
ROUTER_VIEW_DEPTH_KEY
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
function getCurrentProxy(): VueInstance {
|
|
39
35
|
const instance = getCurrentInstance();
|
|
40
36
|
if (!instance || !instance.proxy) {
|
|
41
|
-
throw new Error(
|
|
37
|
+
throw new Error(
|
|
38
|
+
'[@esmx/router-vue] Must be used within setup() or other composition functions'
|
|
39
|
+
);
|
|
42
40
|
}
|
|
43
41
|
return instance.proxy;
|
|
44
42
|
}
|
|
@@ -46,7 +44,7 @@ function getCurrentProxy(functionName: string): VueInstance {
|
|
|
46
44
|
function findRouterContext(vm?: VueInstance): RouterContext {
|
|
47
45
|
// If no vm provided, try to get current instance
|
|
48
46
|
if (!vm) {
|
|
49
|
-
vm = getCurrentProxy(
|
|
47
|
+
vm = getCurrentProxy();
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
let context = routerContextProperty.get(vm);
|
|
@@ -64,7 +62,9 @@ function findRouterContext(vm?: VueInstance): RouterContext {
|
|
|
64
62
|
current = current.$parent;
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
throw new Error(
|
|
65
|
+
throw new Error(
|
|
66
|
+
'[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component.'
|
|
67
|
+
);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -143,7 +143,7 @@ export function getRoute(instance?: VueInstance): Route {
|
|
|
143
143
|
* Get router context using the optimal method available.
|
|
144
144
|
* First tries provide/inject (works in setup), then falls back to hierarchy traversal.
|
|
145
145
|
*/
|
|
146
|
-
function useRouterContext(
|
|
146
|
+
function useRouterContext(): RouterContext {
|
|
147
147
|
// First try to get context from provide/inject (works in setup)
|
|
148
148
|
const injectedContext = inject<RouterContext>(ROUTER_INJECT_KEY);
|
|
149
149
|
if (injectedContext) {
|
|
@@ -151,7 +151,7 @@ function useRouterContext(functionName: string): RouterContext {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// Fallback to component hierarchy traversal (works after mount)
|
|
154
|
-
const proxy = getCurrentProxy(
|
|
154
|
+
const proxy = getCurrentProxy();
|
|
155
155
|
return findRouterContext(proxy);
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -188,7 +188,7 @@ function useRouterContext(functionName: string): RouterContext {
|
|
|
188
188
|
* ```
|
|
189
189
|
*/
|
|
190
190
|
export function useRouter(): Router {
|
|
191
|
-
return useRouterContext(
|
|
191
|
+
return useRouterContext().router;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
@@ -224,7 +224,7 @@ export function useRouter(): Router {
|
|
|
224
224
|
* ```
|
|
225
225
|
*/
|
|
226
226
|
export function useRoute(): Route {
|
|
227
|
-
return useRouterContext(
|
|
227
|
+
return useRouterContext().route;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/**
|
|
@@ -257,7 +257,7 @@ export function useRoute(): Route {
|
|
|
257
257
|
* ```
|
|
258
258
|
*/
|
|
259
259
|
export function useProvideRouter(router: Router): void {
|
|
260
|
-
const proxy = getCurrentProxy(
|
|
260
|
+
const proxy = getCurrentProxy();
|
|
261
261
|
|
|
262
262
|
const dep = ref(0);
|
|
263
263
|
|
|
@@ -313,13 +313,12 @@ export function useProvideRouter(router: Router): void {
|
|
|
313
313
|
* ```
|
|
314
314
|
*/
|
|
315
315
|
export function _useRouterViewDepth(isRender?: boolean): number {
|
|
316
|
-
// Get current RouterView depth from parent RouterView (if any)
|
|
317
|
-
// Default to 0 if no parent RouterView is found
|
|
318
316
|
const depth = inject(ROUTER_VIEW_DEPTH_KEY, 0);
|
|
319
317
|
|
|
320
318
|
if (isRender) {
|
|
321
|
-
// Provide depth + 1 to child RouterView components
|
|
322
319
|
provide(ROUTER_VIEW_DEPTH_KEY, depth + 1);
|
|
320
|
+
const proxy = getCurrentProxy();
|
|
321
|
+
routerViewDepthProperty.set(proxy, depth + 1);
|
|
323
322
|
}
|
|
324
323
|
|
|
325
324
|
return depth;
|
|
@@ -354,6 +353,26 @@ export function useRouterViewDepth(): number {
|
|
|
354
353
|
return _useRouterViewDepth();
|
|
355
354
|
}
|
|
356
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Get injected RouterView depth from a Vue instance's ancestors.
|
|
358
|
+
* Traverses parent chain to find the value provided under ROUTER_VIEW_DEPTH_KEY.
|
|
359
|
+
*
|
|
360
|
+
* @param instance - Vue component instance to start from
|
|
361
|
+
* @returns Injected RouterView depth value from nearest ancestor
|
|
362
|
+
* @throws {Error} If no ancestor provided ROUTER_VIEW_DEPTH_KEY
|
|
363
|
+
*/
|
|
364
|
+
export function getRouterViewDepth(instance: VueInstance): number {
|
|
365
|
+
let current = instance.$parent;
|
|
366
|
+
while (current) {
|
|
367
|
+
const value = routerViewDepthProperty.get(current);
|
|
368
|
+
if (typeof value === 'number') return value;
|
|
369
|
+
current = current.$parent;
|
|
370
|
+
}
|
|
371
|
+
throw new Error(
|
|
372
|
+
'[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components.'
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
357
376
|
/**
|
|
358
377
|
* Create reactive link helpers for navigation elements.
|
|
359
378
|
* Returns computed properties for link attributes, classes, and event handlers.
|