@esmx/router-vue 3.0.0-rc.17 → 3.0.0-rc.20
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 +1 -1
- package/README.md +563 -0
- package/README.zh-CN.md +563 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.mjs +11 -4
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.mjs +206 -0
- package/dist/plugin.d.ts +61 -11
- package/dist/plugin.mjs +32 -16
- package/dist/plugin.test.d.ts +1 -0
- package/dist/plugin.test.mjs +436 -0
- package/dist/router-link.d.ts +202 -0
- package/dist/router-link.mjs +84 -0
- package/dist/router-link.test.d.ts +1 -0
- package/dist/router-link.test.mjs +456 -0
- package/dist/router-view.d.ts +31 -0
- package/dist/router-view.mjs +17 -0
- package/dist/router-view.test.d.ts +1 -0
- package/dist/router-view.test.mjs +459 -0
- package/dist/use.d.ts +198 -3
- package/dist/use.mjs +75 -9
- package/dist/use.test.d.ts +1 -0
- package/dist/use.test.mjs +461 -0
- package/dist/util.d.ts +7 -0
- package/dist/util.mjs +24 -0
- package/dist/util.test.d.ts +1 -0
- package/dist/util.test.mjs +319 -0
- package/dist/vue2.d.ts +13 -0
- package/dist/vue2.mjs +0 -0
- package/dist/vue3.d.ts +13 -0
- package/dist/vue3.mjs +0 -0
- package/package.json +31 -14
- package/src/index.test.ts +263 -0
- package/src/index.ts +16 -4
- package/src/plugin.test.ts +574 -0
- package/src/plugin.ts +92 -31
- package/src/router-link.test.ts +569 -0
- package/src/router-link.ts +148 -0
- package/src/router-view.test.ts +599 -0
- package/src/router-view.ts +62 -0
- package/src/use.test.ts +616 -0
- package/src/use.ts +307 -11
- package/src/util.test.ts +418 -0
- package/src/util.ts +32 -0
- package/src/vue2.ts +16 -0
- package/src/vue3.ts +15 -0
- package/dist/link.d.ts +0 -101
- package/dist/link.mjs +0 -103
- package/dist/symbols.d.ts +0 -3
- package/dist/symbols.mjs +0 -3
- package/dist/view.d.ts +0 -21
- package/dist/view.mjs +0 -75
- package/src/link.ts +0 -177
- package/src/symbols.ts +0 -8
- package/src/view.ts +0 -95
package/dist/use.mjs
CHANGED
|
@@ -1,15 +1,81 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
getCurrentInstance,
|
|
4
|
+
inject,
|
|
5
|
+
onBeforeUnmount,
|
|
6
|
+
provide,
|
|
7
|
+
ref
|
|
8
|
+
} from "vue";
|
|
9
|
+
import { createSymbolProperty } from "./util.mjs";
|
|
10
|
+
const ROUTER_CONTEXT_KEY = Symbol("router-context");
|
|
11
|
+
const ROUTER_INJECT_KEY = Symbol("router-inject");
|
|
12
|
+
const ERROR_MESSAGES = {
|
|
13
|
+
SETUP_ONLY: (fnName) => `[@esmx/router-vue] ${fnName}() can only be called during setup()`,
|
|
14
|
+
CONTEXT_NOT_FOUND: "[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component."
|
|
15
|
+
};
|
|
16
|
+
const routerContextProperty = createSymbolProperty(ROUTER_CONTEXT_KEY);
|
|
17
|
+
function getCurrentProxy(functionName) {
|
|
18
|
+
const instance = getCurrentInstance();
|
|
19
|
+
if (!instance || !instance.proxy) {
|
|
20
|
+
throw new Error(ERROR_MESSAGES.SETUP_ONLY(functionName));
|
|
8
21
|
}
|
|
22
|
+
return instance.proxy;
|
|
23
|
+
}
|
|
24
|
+
function findRouterContext(vm) {
|
|
25
|
+
if (!vm) {
|
|
26
|
+
vm = getCurrentProxy("findRouterContext");
|
|
27
|
+
}
|
|
28
|
+
let context = routerContextProperty.get(vm);
|
|
29
|
+
if (context) {
|
|
30
|
+
return context;
|
|
31
|
+
}
|
|
32
|
+
let current = vm.$parent;
|
|
33
|
+
while (current) {
|
|
34
|
+
context = routerContextProperty.get(current);
|
|
35
|
+
if (context) {
|
|
36
|
+
routerContextProperty.set(vm, context);
|
|
37
|
+
return context;
|
|
38
|
+
}
|
|
39
|
+
current = current.$parent;
|
|
40
|
+
}
|
|
41
|
+
throw new Error(ERROR_MESSAGES.CONTEXT_NOT_FOUND);
|
|
42
|
+
}
|
|
43
|
+
export function getRouter(instance) {
|
|
44
|
+
return findRouterContext(instance).router;
|
|
45
|
+
}
|
|
46
|
+
export function getRoute(instance) {
|
|
47
|
+
return findRouterContext(instance).route.value;
|
|
48
|
+
}
|
|
49
|
+
function useRouterContext(functionName) {
|
|
50
|
+
const injectedContext = inject(ROUTER_INJECT_KEY);
|
|
51
|
+
if (injectedContext) {
|
|
52
|
+
return injectedContext;
|
|
53
|
+
}
|
|
54
|
+
const proxy = getCurrentProxy(functionName);
|
|
55
|
+
return findRouterContext(proxy);
|
|
9
56
|
}
|
|
10
57
|
export function useRouter() {
|
|
11
|
-
return
|
|
58
|
+
return useRouterContext("useRouter").router;
|
|
12
59
|
}
|
|
13
60
|
export function useRoute() {
|
|
14
|
-
return
|
|
61
|
+
return useRouterContext("useRoute").route.value;
|
|
62
|
+
}
|
|
63
|
+
export function useProvideRouter(router) {
|
|
64
|
+
const proxy = getCurrentProxy("useProvideRouter");
|
|
65
|
+
const context = {
|
|
66
|
+
router,
|
|
67
|
+
route: ref(router.route)
|
|
68
|
+
};
|
|
69
|
+
provide(ROUTER_INJECT_KEY, context);
|
|
70
|
+
routerContextProperty.set(proxy, context);
|
|
71
|
+
const unwatch = router.afterEach((to) => {
|
|
72
|
+
if (router.route === to) {
|
|
73
|
+
to.syncTo(context.route.value);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
onBeforeUnmount(unwatch);
|
|
77
|
+
}
|
|
78
|
+
export function useLink(props) {
|
|
79
|
+
const router = useRouter();
|
|
80
|
+
return computed(() => router.resolveLink(props));
|
|
15
81
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import { Router, RouterMode } from "@esmx/router";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
createApp,
|
|
5
|
+
defineComponent,
|
|
6
|
+
getCurrentInstance,
|
|
7
|
+
h,
|
|
8
|
+
nextTick
|
|
9
|
+
} from "vue";
|
|
10
|
+
import {
|
|
11
|
+
getRoute,
|
|
12
|
+
getRouter,
|
|
13
|
+
useLink,
|
|
14
|
+
useProvideRouter,
|
|
15
|
+
useRoute,
|
|
16
|
+
useRouter
|
|
17
|
+
} from "./use.mjs";
|
|
18
|
+
describe("use.ts - Vue Router Integration", () => {
|
|
19
|
+
let router;
|
|
20
|
+
let testContainer;
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
testContainer = document.createElement("div");
|
|
23
|
+
testContainer.id = "test-app";
|
|
24
|
+
document.body.appendChild(testContainer);
|
|
25
|
+
const routes = [
|
|
26
|
+
{
|
|
27
|
+
path: "/",
|
|
28
|
+
component: defineComponent({ template: "<div>Home</div>" }),
|
|
29
|
+
meta: { title: "Home" }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: "/about",
|
|
33
|
+
component: defineComponent({ template: "<div>About</div>" }),
|
|
34
|
+
meta: { title: "About" }
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
router = new Router({
|
|
38
|
+
root: "#test-app",
|
|
39
|
+
routes,
|
|
40
|
+
mode: RouterMode.memory,
|
|
41
|
+
base: new URL("http://localhost:3000/")
|
|
42
|
+
});
|
|
43
|
+
await router.replace("/");
|
|
44
|
+
});
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
if (testContainer.parentNode) {
|
|
47
|
+
testContainer.parentNode.removeChild(testContainer);
|
|
48
|
+
}
|
|
49
|
+
if (router) {
|
|
50
|
+
router.destroy();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
describe("Error Handling - Context Not Found", () => {
|
|
54
|
+
const contextNotFoundError = "[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component.";
|
|
55
|
+
const contextErrorTestCases = [
|
|
56
|
+
{
|
|
57
|
+
name: "getRouter called without router context",
|
|
58
|
+
test: () => {
|
|
59
|
+
const app = createApp({ template: "<div>Test</div>" });
|
|
60
|
+
const vm = app.mount(testContainer);
|
|
61
|
+
const result = () => getRouter(vm);
|
|
62
|
+
app.unmount();
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "getRoute called without router context",
|
|
68
|
+
test: () => {
|
|
69
|
+
const app = createApp({ template: "<div>Test</div>" });
|
|
70
|
+
const vm = app.mount(testContainer);
|
|
71
|
+
const result = () => getRoute(vm);
|
|
72
|
+
app.unmount();
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
contextErrorTestCases.forEach(({ name, test }) => {
|
|
78
|
+
it(`should throw error when ${name}`, () => {
|
|
79
|
+
expect(test()).toThrow(contextNotFoundError);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
const compositionContextErrorTestCases = [
|
|
83
|
+
{
|
|
84
|
+
name: "useRouter called without router context",
|
|
85
|
+
setupFn: () => {
|
|
86
|
+
expect(() => useRouter()).toThrow(contextNotFoundError);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "useRoute called without router context",
|
|
91
|
+
setupFn: () => {
|
|
92
|
+
expect(() => useRoute()).toThrow(contextNotFoundError);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "useLink called without router context",
|
|
97
|
+
setupFn: () => {
|
|
98
|
+
expect(
|
|
99
|
+
() => useLink({
|
|
100
|
+
to: "/about",
|
|
101
|
+
type: "push",
|
|
102
|
+
exact: "include"
|
|
103
|
+
})
|
|
104
|
+
).toThrow(contextNotFoundError);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
];
|
|
108
|
+
compositionContextErrorTestCases.forEach(({ name, setupFn }) => {
|
|
109
|
+
it(`should throw error when ${name}`, () => {
|
|
110
|
+
const TestComponent = defineComponent({
|
|
111
|
+
setup() {
|
|
112
|
+
setupFn();
|
|
113
|
+
return () => "<div>Test</div>";
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
const app = createApp(TestComponent);
|
|
117
|
+
app.mount(testContainer);
|
|
118
|
+
app.unmount();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("Error Handling - Setup Only", () => {
|
|
123
|
+
const setupOnlyTestCases = [
|
|
124
|
+
{
|
|
125
|
+
name: "useRouter called outside setup()",
|
|
126
|
+
fn: () => useRouter(),
|
|
127
|
+
expectedError: "[@esmx/router-vue] useRouter() can only be called during setup()"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "useRoute called outside setup()",
|
|
131
|
+
fn: () => useRoute(),
|
|
132
|
+
expectedError: "[@esmx/router-vue] useRoute() can only be called during setup()"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "useLink called outside setup()",
|
|
136
|
+
fn: () => useLink({
|
|
137
|
+
to: "/about",
|
|
138
|
+
type: "push",
|
|
139
|
+
exact: "include"
|
|
140
|
+
}),
|
|
141
|
+
expectedError: "[@esmx/router-vue] useRouter() can only be called during setup()"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "useProvideRouter called outside setup()",
|
|
145
|
+
fn: () => useProvideRouter(router),
|
|
146
|
+
expectedError: "[@esmx/router-vue] useProvideRouter() can only be called during setup()"
|
|
147
|
+
}
|
|
148
|
+
];
|
|
149
|
+
setupOnlyTestCases.forEach(({ name, fn, expectedError }) => {
|
|
150
|
+
it(`should throw error when ${name}`, () => {
|
|
151
|
+
expect(fn).toThrow(expectedError);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
describe("Basic Functionality", () => {
|
|
156
|
+
it("should provide router context and return router instance from getRouter", async () => {
|
|
157
|
+
let routerInstance = null;
|
|
158
|
+
const app = createApp({
|
|
159
|
+
setup() {
|
|
160
|
+
useProvideRouter(router);
|
|
161
|
+
return () => "<div>App</div>";
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
const vm = app.mount(testContainer);
|
|
165
|
+
routerInstance = getRouter(vm);
|
|
166
|
+
expect(routerInstance).toBe(router);
|
|
167
|
+
expect(routerInstance).toBeInstanceOf(Router);
|
|
168
|
+
app.unmount();
|
|
169
|
+
});
|
|
170
|
+
it("should provide router context and return current route from getRoute", async () => {
|
|
171
|
+
let currentRoute = null;
|
|
172
|
+
const app = createApp({
|
|
173
|
+
setup() {
|
|
174
|
+
useProvideRouter(router);
|
|
175
|
+
return () => "<div>App</div>";
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
const vm = app.mount(testContainer);
|
|
179
|
+
currentRoute = getRoute(vm);
|
|
180
|
+
expect(currentRoute).toBeTruthy();
|
|
181
|
+
expect(currentRoute.path).toBe("/");
|
|
182
|
+
expect(currentRoute.meta.title).toBe("Home");
|
|
183
|
+
app.unmount();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe("Setup() Support - useRouter in setup()", () => {
|
|
187
|
+
it("should allow useRouter to work in setup() via provide/inject", async () => {
|
|
188
|
+
let routerInstance = null;
|
|
189
|
+
let childRoute = null;
|
|
190
|
+
const ChildComponent = defineComponent({
|
|
191
|
+
name: "ChildComponent",
|
|
192
|
+
setup() {
|
|
193
|
+
routerInstance = useRouter();
|
|
194
|
+
childRoute = useRoute();
|
|
195
|
+
expect(routerInstance).toBe(router);
|
|
196
|
+
expect(childRoute.path).toBe("/");
|
|
197
|
+
return () => h("div", "Child Component");
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
const ParentComponent = defineComponent({
|
|
201
|
+
name: "ParentComponent",
|
|
202
|
+
components: { ChildComponent },
|
|
203
|
+
setup() {
|
|
204
|
+
useProvideRouter(router);
|
|
205
|
+
return () => h(ChildComponent);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
const app = createApp(ParentComponent);
|
|
209
|
+
app.mount(testContainer);
|
|
210
|
+
await nextTick();
|
|
211
|
+
expect(routerInstance).toBe(router);
|
|
212
|
+
expect(childRoute).toBeTruthy();
|
|
213
|
+
expect(childRoute.path).toBe("/");
|
|
214
|
+
app.unmount();
|
|
215
|
+
});
|
|
216
|
+
it("should work with nested components in setup()", async () => {
|
|
217
|
+
let deepChildRouter = null;
|
|
218
|
+
const DeepChildComponent = defineComponent({
|
|
219
|
+
name: "DeepChildComponent",
|
|
220
|
+
setup() {
|
|
221
|
+
deepChildRouter = useRouter();
|
|
222
|
+
expect(deepChildRouter).toBe(router);
|
|
223
|
+
return () => h("div", "Deep Child");
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
const MiddleComponent = defineComponent({
|
|
227
|
+
name: "MiddleComponent",
|
|
228
|
+
components: { DeepChildComponent },
|
|
229
|
+
setup() {
|
|
230
|
+
return () => h(DeepChildComponent);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
const TopComponent = defineComponent({
|
|
234
|
+
name: "TopComponent",
|
|
235
|
+
components: { MiddleComponent },
|
|
236
|
+
setup() {
|
|
237
|
+
useProvideRouter(router);
|
|
238
|
+
return () => h(MiddleComponent);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const app = createApp(TopComponent);
|
|
242
|
+
app.mount(testContainer);
|
|
243
|
+
await nextTick();
|
|
244
|
+
expect(deepChildRouter).toBe(router);
|
|
245
|
+
app.unmount();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
describe("Component Hierarchy Context Finding - Investigation", () => {
|
|
249
|
+
it("should investigate component hierarchy traversal with logging", async () => {
|
|
250
|
+
let childRouterResult = null;
|
|
251
|
+
let parentVmInstance = null;
|
|
252
|
+
let childVmInstance = null;
|
|
253
|
+
const ChildComponent = defineComponent({
|
|
254
|
+
name: "ChildComponent",
|
|
255
|
+
setup(_, { expose }) {
|
|
256
|
+
const instance = getCurrentInstance();
|
|
257
|
+
childVmInstance = (instance == null ? void 0 : instance.proxy) || null;
|
|
258
|
+
try {
|
|
259
|
+
childRouterResult = useRouter();
|
|
260
|
+
} catch (error) {
|
|
261
|
+
expect(error.message).toContain(
|
|
262
|
+
"Router context not found"
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
expose({ childVmInstance });
|
|
266
|
+
return () => "<div>Child Component</div>";
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
const ParentComponent = defineComponent({
|
|
270
|
+
name: "ParentComponent",
|
|
271
|
+
components: { ChildComponent },
|
|
272
|
+
setup(_, { expose }) {
|
|
273
|
+
const instance = getCurrentInstance();
|
|
274
|
+
parentVmInstance = (instance == null ? void 0 : instance.proxy) || null;
|
|
275
|
+
useProvideRouter(router);
|
|
276
|
+
expose({ parentVmInstance });
|
|
277
|
+
return () => h(ChildComponent);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
const app = createApp(ParentComponent);
|
|
281
|
+
const mountedApp = app.mount(testContainer);
|
|
282
|
+
await nextTick();
|
|
283
|
+
if (childVmInstance && parentVmInstance) {
|
|
284
|
+
const parentHasContext = !!parentVmInstance[Symbol.for("router-context")] || Object.getOwnPropertySymbols(parentVmInstance).some(
|
|
285
|
+
(sym) => sym.toString().includes("router-context")
|
|
286
|
+
);
|
|
287
|
+
expect(parentHasContext).toBe(true);
|
|
288
|
+
}
|
|
289
|
+
if (childRouterResult) {
|
|
290
|
+
expect(childRouterResult).toBe(router);
|
|
291
|
+
}
|
|
292
|
+
app.unmount();
|
|
293
|
+
});
|
|
294
|
+
it("should investigate direct getRouter call with component instances", async () => {
|
|
295
|
+
let parentInstance = null;
|
|
296
|
+
let childInstance = null;
|
|
297
|
+
const ChildComponent = defineComponent({
|
|
298
|
+
name: "ChildComponent",
|
|
299
|
+
setup() {
|
|
300
|
+
const instance = getCurrentInstance();
|
|
301
|
+
childInstance = (instance == null ? void 0 : instance.proxy) || null;
|
|
302
|
+
return () => "<div>Child</div>";
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
const ParentComponent = defineComponent({
|
|
306
|
+
name: "ParentComponent",
|
|
307
|
+
components: { ChildComponent },
|
|
308
|
+
setup() {
|
|
309
|
+
const instance = getCurrentInstance();
|
|
310
|
+
parentInstance = (instance == null ? void 0 : instance.proxy) || null;
|
|
311
|
+
useProvideRouter(router);
|
|
312
|
+
return () => h(ChildComponent);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
const app = createApp(ParentComponent);
|
|
316
|
+
app.mount(testContainer);
|
|
317
|
+
await nextTick();
|
|
318
|
+
if (childInstance && parentInstance) {
|
|
319
|
+
try {
|
|
320
|
+
const routerFromChild = getRouter(childInstance);
|
|
321
|
+
expect(routerFromChild).toBe(router);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
expect(error.message).toContain(
|
|
324
|
+
"Router context not found"
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
app.unmount();
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
describe("Navigation", () => {
|
|
332
|
+
it("should handle router navigation correctly", async () => {
|
|
333
|
+
const app = createApp({
|
|
334
|
+
setup() {
|
|
335
|
+
useProvideRouter(router);
|
|
336
|
+
return () => "<div>App</div>";
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
app.mount(testContainer);
|
|
340
|
+
expect(router.route.path).toBe("/");
|
|
341
|
+
expect(router.route.meta.title).toBe("Home");
|
|
342
|
+
await router.push("/about");
|
|
343
|
+
expect(router.route.path).toBe("/about");
|
|
344
|
+
expect(router.route.meta.title).toBe("About");
|
|
345
|
+
app.unmount();
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
describe("Composition API Integration", () => {
|
|
349
|
+
it("should work with composition API functions in same component", async () => {
|
|
350
|
+
let compositionRouter = null;
|
|
351
|
+
let compositionRoute = null;
|
|
352
|
+
let linkResolver = null;
|
|
353
|
+
const TestComponent = defineComponent({
|
|
354
|
+
setup() {
|
|
355
|
+
useProvideRouter(router);
|
|
356
|
+
compositionRouter = useRouter();
|
|
357
|
+
compositionRoute = useRoute();
|
|
358
|
+
linkResolver = useLink({
|
|
359
|
+
to: "/about",
|
|
360
|
+
type: "push",
|
|
361
|
+
exact: "include"
|
|
362
|
+
});
|
|
363
|
+
return () => "<div>Test Component</div>";
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
const app = createApp(TestComponent);
|
|
367
|
+
app.mount(testContainer);
|
|
368
|
+
await nextTick();
|
|
369
|
+
expect(compositionRouter).toBe(router);
|
|
370
|
+
expect(compositionRoute).toBeTruthy();
|
|
371
|
+
expect(compositionRoute.path).toBe("/");
|
|
372
|
+
expect(compositionRoute.meta.title).toBe("Home");
|
|
373
|
+
expect(linkResolver).toBeTruthy();
|
|
374
|
+
expect(linkResolver.value).toBeTruthy();
|
|
375
|
+
const link = linkResolver.value;
|
|
376
|
+
expect(link).toHaveProperty("attributes");
|
|
377
|
+
expect(link).toHaveProperty("getEventHandlers");
|
|
378
|
+
expect(link).toHaveProperty("isActive");
|
|
379
|
+
app.unmount();
|
|
380
|
+
});
|
|
381
|
+
it("should handle route updates reactively", async () => {
|
|
382
|
+
let routeRef = null;
|
|
383
|
+
const TestComponent = defineComponent({
|
|
384
|
+
setup() {
|
|
385
|
+
useProvideRouter(router);
|
|
386
|
+
const route = useRoute();
|
|
387
|
+
routeRef = route;
|
|
388
|
+
return () => `<div>Current: ${route.path}</div>`;
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
const app = createApp(TestComponent);
|
|
392
|
+
app.mount(testContainer);
|
|
393
|
+
await nextTick();
|
|
394
|
+
expect(routeRef).toBeTruthy();
|
|
395
|
+
expect(routeRef.path).toBe("/");
|
|
396
|
+
await router.push("/about");
|
|
397
|
+
await nextTick();
|
|
398
|
+
expect(routeRef.path).toBe("/about");
|
|
399
|
+
app.unmount();
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
describe("Deep Component Hierarchy", () => {
|
|
403
|
+
it("should cover deep component hierarchy traversal (multi-level parent chain)", async () => {
|
|
404
|
+
let childRouterResult = null;
|
|
405
|
+
const ChildComponent = defineComponent({
|
|
406
|
+
name: "ChildComponent",
|
|
407
|
+
setup() {
|
|
408
|
+
return () => h("div", "Deep Child");
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
const ParentComponent = defineComponent({
|
|
412
|
+
name: "ParentComponent",
|
|
413
|
+
setup() {
|
|
414
|
+
return () => h(ChildComponent);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
const GrandParentComponent = defineComponent({
|
|
418
|
+
name: "GrandParentComponent",
|
|
419
|
+
setup() {
|
|
420
|
+
useProvideRouter(router);
|
|
421
|
+
return () => h(ParentComponent);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
const app = createApp(GrandParentComponent);
|
|
425
|
+
const mountedApp = app.mount(testContainer);
|
|
426
|
+
await nextTick();
|
|
427
|
+
const deepChildInstance = mountedApp;
|
|
428
|
+
const mockDeepChild = {
|
|
429
|
+
$parent: {
|
|
430
|
+
// This is the middle parent (no router context)
|
|
431
|
+
$parent: mountedApp
|
|
432
|
+
// This is the grandparent (has router context)
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
childRouterResult = getRouter(mockDeepChild);
|
|
436
|
+
expect(childRouterResult).toBe(router);
|
|
437
|
+
expect(childRouterResult).toBeInstanceOf(Router);
|
|
438
|
+
app.unmount();
|
|
439
|
+
});
|
|
440
|
+
it("should handle component hierarchy traversal with manual parent chain setup", () => {
|
|
441
|
+
const app = createApp({
|
|
442
|
+
setup() {
|
|
443
|
+
useProvideRouter(router);
|
|
444
|
+
return () => h("div", "Root");
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
const rootInstance = app.mount(testContainer);
|
|
448
|
+
const leafInstance = {
|
|
449
|
+
$parent: {
|
|
450
|
+
// Middle level - no router context
|
|
451
|
+
$parent: rootInstance
|
|
452
|
+
// Root level - has router context
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
const foundRouter = getRouter(leafInstance);
|
|
456
|
+
expect(foundRouter).toBe(router);
|
|
457
|
+
expect(foundRouter).toBeInstanceOf(Router);
|
|
458
|
+
app.unmount();
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
});
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const isVue3: boolean;
|
|
2
|
+
export declare function createSymbolProperty<T>(symbol: symbol): {
|
|
3
|
+
readonly set: (instance: any, value: T) => void;
|
|
4
|
+
readonly get: (instance: any) => T | undefined;
|
|
5
|
+
};
|
|
6
|
+
export declare function isESModule(obj: unknown): obj is Record<string | symbol, any>;
|
|
7
|
+
export declare function resolveComponent(component: unknown): unknown;
|
package/dist/util.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { version } from "vue";
|
|
2
|
+
export const isVue3 = version.startsWith("3.");
|
|
3
|
+
export function createSymbolProperty(symbol) {
|
|
4
|
+
return {
|
|
5
|
+
set(instance, value) {
|
|
6
|
+
instance[symbol] = value;
|
|
7
|
+
},
|
|
8
|
+
get(instance) {
|
|
9
|
+
return symbol in instance ? instance[symbol] : void 0;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function isESModule(obj) {
|
|
14
|
+
if (!obj || typeof obj !== "object") return false;
|
|
15
|
+
const module = obj;
|
|
16
|
+
return Boolean(module.__esModule) || module[Symbol.toStringTag] === "Module";
|
|
17
|
+
}
|
|
18
|
+
export function resolveComponent(component) {
|
|
19
|
+
if (!component) return null;
|
|
20
|
+
if (isESModule(component)) {
|
|
21
|
+
return component.default || component;
|
|
22
|
+
}
|
|
23
|
+
return component;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|