@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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +570 -0
  3. package/README.zh-CN.md +570 -0
  4. package/dist/index.d.ts +6 -0
  5. package/dist/index.mjs +13 -0
  6. package/dist/index.test.d.ts +1 -0
  7. package/dist/index.test.mjs +216 -0
  8. package/dist/plugin.d.ts +61 -0
  9. package/dist/plugin.mjs +41 -0
  10. package/dist/plugin.test.d.ts +1 -0
  11. package/dist/plugin.test.mjs +631 -0
  12. package/dist/router-link.d.ts +220 -0
  13. package/dist/router-link.mjs +119 -0
  14. package/dist/router-link.test.d.ts +1 -0
  15. package/dist/router-link.test.mjs +663 -0
  16. package/dist/router-view.d.ts +31 -0
  17. package/dist/router-view.mjs +15 -0
  18. package/dist/router-view.test.d.ts +1 -0
  19. package/dist/router-view.test.mjs +676 -0
  20. package/dist/run-with-context.test.d.ts +1 -0
  21. package/dist/run-with-context.test.mjs +57 -0
  22. package/dist/use.d.ts +260 -0
  23. package/dist/use.mjs +125 -0
  24. package/dist/use.test.d.ts +1 -0
  25. package/dist/use.test.mjs +381 -0
  26. package/dist/util.d.ts +20 -0
  27. package/dist/util.mjs +49 -0
  28. package/dist/util.test.d.ts +4 -0
  29. package/dist/util.test.mjs +604 -0
  30. package/dist/vue2.d.ts +15 -0
  31. package/dist/vue2.mjs +0 -0
  32. package/dist/vue3.d.ts +13 -0
  33. package/dist/vue3.mjs +0 -0
  34. package/package.json +85 -0
  35. package/src/index.test.ts +273 -0
  36. package/src/index.ts +15 -0
  37. package/src/plugin.test.ts +812 -0
  38. package/src/plugin.ts +107 -0
  39. package/src/router-link.test.ts +830 -0
  40. package/src/router-link.ts +172 -0
  41. package/src/router-view.test.ts +840 -0
  42. package/src/router-view.ts +59 -0
  43. package/src/run-with-context.test.ts +64 -0
  44. package/src/use.test.ts +484 -0
  45. package/src/use.ts +416 -0
  46. package/src/util.test.ts +760 -0
  47. package/src/util.ts +85 -0
  48. package/src/vue2.ts +18 -0
  49. package/src/vue3.ts +15 -0
@@ -0,0 +1,381 @@
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 { RouterView } from "./router-view.mjs";
11
+ import {
12
+ getRouterViewDepth,
13
+ useProvideRouter,
14
+ useRoute,
15
+ useRouter,
16
+ useRouterViewDepth
17
+ } from "./use.mjs";
18
+ describe("Router Vue Integration", () => {
19
+ let app;
20
+ let router;
21
+ let mountPoint;
22
+ beforeEach(async () => {
23
+ router = new Router({
24
+ mode: RouterMode.memory,
25
+ routes: [
26
+ { path: "/initial", component: {} },
27
+ { path: "/new-route", component: {} },
28
+ { path: "/user/:id", component: {} },
29
+ { path: "/new-path", component: {} }
30
+ ],
31
+ base: new URL("http://localhost:8000/")
32
+ });
33
+ await router.replace("/initial");
34
+ mountPoint = document.createElement("div");
35
+ mountPoint.id = "app";
36
+ document.body.appendChild(mountPoint);
37
+ });
38
+ afterEach(() => {
39
+ if (app) {
40
+ app.unmount();
41
+ }
42
+ document.body.removeChild(mountPoint);
43
+ router.destroy();
44
+ });
45
+ describe("Router and Route Access", () => {
46
+ it("should provide router and route access", async () => {
47
+ let routerResult;
48
+ let routeResult;
49
+ const TestApp = {
50
+ setup() {
51
+ useProvideRouter(router);
52
+ routerResult = useRouter();
53
+ routeResult = useRoute();
54
+ return () => h("div", "Test App");
55
+ }
56
+ };
57
+ app = createApp(TestApp);
58
+ app.mount("#app");
59
+ expect(routerResult).toEqual(router);
60
+ expect(routeResult).toBeDefined();
61
+ expect(routeResult == null ? void 0 : routeResult.path).toBe("/initial");
62
+ });
63
+ });
64
+ describe("Route Reactivity", () => {
65
+ it("should update route properties when route changes", async () => {
66
+ let routeRef;
67
+ const TestApp = {
68
+ setup() {
69
+ useProvideRouter(router);
70
+ routeRef = useRoute();
71
+ return () => h("div", routeRef == null ? void 0 : routeRef.path);
72
+ }
73
+ };
74
+ app = createApp(TestApp);
75
+ app.mount("#app");
76
+ expect(routeRef == null ? void 0 : routeRef.path).toBe("/initial");
77
+ const initialRouteRef = routeRef;
78
+ await router.replace("/new-route");
79
+ await nextTick();
80
+ expect(routeRef).toBe(initialRouteRef);
81
+ expect(routeRef == null ? void 0 : routeRef.path).toBe("/new-route");
82
+ });
83
+ it("should update route params when route changes", async () => {
84
+ var _a;
85
+ let routeRef;
86
+ const TestApp = {
87
+ setup() {
88
+ useProvideRouter(router);
89
+ routeRef = useRoute();
90
+ return () => {
91
+ var _a2;
92
+ return h("div", [
93
+ h("span", routeRef == null ? void 0 : routeRef.path),
94
+ h("span", ((_a2 = routeRef == null ? void 0 : routeRef.params) == null ? void 0 : _a2.id) || "no-id")
95
+ ]);
96
+ };
97
+ }
98
+ };
99
+ app = createApp(TestApp);
100
+ app.mount("#app");
101
+ await router.replace("/user/123");
102
+ await nextTick();
103
+ expect(routeRef == null ? void 0 : routeRef.path).toBe("/user/123");
104
+ expect((_a = routeRef == null ? void 0 : routeRef.params) == null ? void 0 : _a.id).toBe("123");
105
+ });
106
+ it("should automatically update view when route changes", async () => {
107
+ const renderCount = { value: 0 };
108
+ let routeRef;
109
+ const TestApp = {
110
+ setup() {
111
+ useProvideRouter(router);
112
+ routeRef = useRoute();
113
+ return () => {
114
+ renderCount.value++;
115
+ return h("div", routeRef == null ? void 0 : routeRef.path);
116
+ };
117
+ }
118
+ };
119
+ app = createApp(TestApp);
120
+ app.mount("#app");
121
+ const initialRenderCount = renderCount.value;
122
+ expect(routeRef == null ? void 0 : routeRef.path).toBe("/initial");
123
+ await router.replace("/new-route");
124
+ await nextTick();
125
+ expect(renderCount.value).toBeGreaterThan(initialRenderCount);
126
+ expect(routeRef == null ? void 0 : routeRef.path).toBe("/new-route");
127
+ const previousRenderCount = renderCount.value;
128
+ await router.replace("/new-path");
129
+ await nextTick();
130
+ expect(renderCount.value).toBeGreaterThan(previousRenderCount);
131
+ expect(routeRef == null ? void 0 : routeRef.path).toBe("/new-path");
132
+ });
133
+ });
134
+ describe("Nested Components", () => {
135
+ it("should provide route context to child components", async () => {
136
+ let parentRoute;
137
+ let childRoute;
138
+ const ChildComponent = {
139
+ setup() {
140
+ childRoute = useRoute();
141
+ return () => h("div", "Child: " + (childRoute == null ? void 0 : childRoute.path));
142
+ }
143
+ };
144
+ const ParentComponent = {
145
+ setup() {
146
+ parentRoute = useRoute();
147
+ return () => h("div", [
148
+ h("span", "Parent: " + (parentRoute == null ? void 0 : parentRoute.path)),
149
+ h(ChildComponent)
150
+ ]);
151
+ }
152
+ };
153
+ const TestApp = {
154
+ setup() {
155
+ useProvideRouter(router);
156
+ return () => h(ParentComponent);
157
+ }
158
+ };
159
+ app = createApp(TestApp);
160
+ app.mount("#app");
161
+ expect(parentRoute).toBeDefined();
162
+ expect(childRoute).toBeDefined();
163
+ expect(parentRoute == null ? void 0 : parentRoute.path).toBe("/initial");
164
+ expect(childRoute == null ? void 0 : childRoute.path).toBe("/initial");
165
+ await router.replace("/new-path");
166
+ await nextTick();
167
+ expect(parentRoute == null ? void 0 : parentRoute.path).toBe("/new-path");
168
+ expect(childRoute == null ? void 0 : childRoute.path).toBe("/new-path");
169
+ });
170
+ });
171
+ describe("RouterView Depth", () => {
172
+ it("should get depth in single RouterView", async () => {
173
+ let observedDepth;
174
+ const LeafProbe = defineComponent({
175
+ setup() {
176
+ const p = getCurrentInstance().proxy;
177
+ observedDepth = getRouterViewDepth(p);
178
+ return () => h("div");
179
+ }
180
+ });
181
+ const Level1 = defineComponent({
182
+ setup() {
183
+ return () => h("div", [h(LeafProbe)]);
184
+ }
185
+ });
186
+ router = new Router({
187
+ mode: RouterMode.memory,
188
+ routes: [{ path: "/level1", component: Level1 }],
189
+ base: new URL("http://localhost:8000/")
190
+ });
191
+ await router.replace("/level1");
192
+ const TestApp = defineComponent({
193
+ setup() {
194
+ useProvideRouter(router);
195
+ return () => h("div", [h(RouterView)]);
196
+ }
197
+ });
198
+ app = createApp(TestApp);
199
+ app.mount("#app");
200
+ await nextTick();
201
+ expect(observedDepth).toBe(1);
202
+ });
203
+ it("should get depth in nested RouterView", async () => {
204
+ let observedDepth;
205
+ const LeafProbe = defineComponent({
206
+ setup() {
207
+ const p = getCurrentInstance().proxy;
208
+ observedDepth = getRouterViewDepth(p);
209
+ return () => h("div");
210
+ }
211
+ });
212
+ const Level1 = defineComponent({
213
+ setup() {
214
+ return () => h("div", [h(RouterView)]);
215
+ }
216
+ });
217
+ const Leaf = defineComponent({
218
+ setup() {
219
+ return () => h("div", [h(LeafProbe)]);
220
+ }
221
+ });
222
+ router = new Router({
223
+ mode: RouterMode.memory,
224
+ routes: [
225
+ {
226
+ path: "/level1",
227
+ component: Level1,
228
+ children: [{ path: "leaf", component: Leaf }]
229
+ }
230
+ ],
231
+ base: new URL("http://localhost:8000/")
232
+ });
233
+ await router.replace("/level1/leaf");
234
+ const TestApp = defineComponent({
235
+ setup() {
236
+ useProvideRouter(router);
237
+ return () => h("div", [h(RouterView)]);
238
+ }
239
+ });
240
+ app = createApp(TestApp);
241
+ app.mount("#app");
242
+ await nextTick();
243
+ expect(observedDepth).toBe(2);
244
+ });
245
+ it("should get depth in double-nested RouterViews", async () => {
246
+ let observedDepth;
247
+ const LeafProbe = defineComponent({
248
+ setup() {
249
+ const p = getCurrentInstance().proxy;
250
+ observedDepth = getRouterViewDepth(p);
251
+ return () => h("div");
252
+ }
253
+ });
254
+ const Level1 = defineComponent({
255
+ setup() {
256
+ return () => h("div", [h(RouterView)]);
257
+ }
258
+ });
259
+ const Level2 = defineComponent({
260
+ setup() {
261
+ return () => h("div", [h(RouterView)]);
262
+ }
263
+ });
264
+ const Leaf = defineComponent({
265
+ setup() {
266
+ return () => h("div", [h(LeafProbe)]);
267
+ }
268
+ });
269
+ router = new Router({
270
+ mode: RouterMode.memory,
271
+ routes: [
272
+ {
273
+ path: "/level1",
274
+ component: Level1,
275
+ children: [
276
+ {
277
+ path: "level2",
278
+ component: Level2,
279
+ children: [{ path: "leaf", component: Leaf }]
280
+ }
281
+ ]
282
+ }
283
+ ],
284
+ base: new URL("http://localhost:8000/")
285
+ });
286
+ await router.replace("/level1/level2/leaf");
287
+ const TestApp = defineComponent({
288
+ setup() {
289
+ useProvideRouter(router);
290
+ return () => h("div", [h(RouterView)]);
291
+ }
292
+ });
293
+ app = createApp(TestApp);
294
+ app.mount("#app");
295
+ await nextTick();
296
+ expect(observedDepth).toBe(3);
297
+ });
298
+ it("should throw when no RouterView ancestor exists", async () => {
299
+ let callDepth;
300
+ const Probe = defineComponent({
301
+ setup() {
302
+ const p = getCurrentInstance().proxy;
303
+ callDepth = () => getRouterViewDepth(p);
304
+ return () => h("div");
305
+ }
306
+ });
307
+ const TestApp = defineComponent({
308
+ setup() {
309
+ useProvideRouter(router);
310
+ return () => h(Probe);
311
+ }
312
+ });
313
+ app = createApp(TestApp);
314
+ app.mount("#app");
315
+ await nextTick();
316
+ expect(() => callDepth()).toThrow(
317
+ new Error(
318
+ "[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components."
319
+ )
320
+ );
321
+ });
322
+ it("should return 0 for useRouterViewDepth without RouterView", async () => {
323
+ let observed = -1;
324
+ const Probe = defineComponent({
325
+ setup() {
326
+ observed = useRouterViewDepth();
327
+ return () => h("div");
328
+ }
329
+ });
330
+ const TestApp = defineComponent({
331
+ setup() {
332
+ useProvideRouter(router);
333
+ return () => h(Probe);
334
+ }
335
+ });
336
+ app = createApp(TestApp);
337
+ app.mount("#app");
338
+ await nextTick();
339
+ expect(observed).toBe(0);
340
+ });
341
+ it("should reflect depth via useRouterViewDepth at each level", async () => {
342
+ let level1Depth = -1;
343
+ let level2Depth = -1;
344
+ const Level2 = defineComponent({
345
+ setup() {
346
+ level2Depth = useRouterViewDepth();
347
+ return () => h("div");
348
+ }
349
+ });
350
+ const Level1 = defineComponent({
351
+ setup() {
352
+ level1Depth = useRouterViewDepth();
353
+ return () => h("div", [h(RouterView)]);
354
+ }
355
+ });
356
+ router = new Router({
357
+ mode: RouterMode.memory,
358
+ routes: [
359
+ {
360
+ path: "/level1",
361
+ component: Level1,
362
+ children: [{ path: "level2", component: Level2 }]
363
+ }
364
+ ],
365
+ base: new URL("http://localhost:8000/")
366
+ });
367
+ await router.replace("/level1/level2");
368
+ const TestApp = defineComponent({
369
+ setup() {
370
+ useProvideRouter(router);
371
+ return () => h("div", [h(RouterView)]);
372
+ }
373
+ });
374
+ app = createApp(TestApp);
375
+ app.mount("#app");
376
+ await nextTick();
377
+ expect(level1Depth).toBe(1);
378
+ expect(level2Depth).toBe(2);
379
+ });
380
+ });
381
+ });
package/dist/util.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { Route, Router } from '@esmx/router';
2
+ import type { Ref } from 'vue';
3
+ export declare const isVue2: boolean;
4
+ /**
5
+ * Define $router and $route properties on a target object.
6
+ * Used to set up global properties for Vue components.
7
+ *
8
+ * @param target - The target object to define properties on (e.g., globalProperties or prototype)
9
+ * @param routerGetter - Getter function for $router (can use `this` in Vue 2)
10
+ * @param routeGetter - Getter function for $route (can use `this` in Vue 2)
11
+ * @param configurable - Whether the properties should be configurable (default: false)
12
+ */
13
+ export declare function defineRouterProperties(target: Record<string, unknown>, routerGetter: (this: unknown) => Router, routeGetter: (this: unknown) => Route, configurable?: boolean): void;
14
+ export declare function createSymbolProperty<T>(symbol: symbol): {
15
+ readonly set: (instance: any, value: T) => void;
16
+ readonly get: (instance: any) => T | undefined;
17
+ };
18
+ export declare function createDependentProxy<T extends object>(obj: T, dep: Ref<any>): T;
19
+ export declare function isESModule(obj: unknown): obj is Record<string | symbol, any>;
20
+ export declare function resolveComponent(component: unknown): unknown;
package/dist/util.mjs ADDED
@@ -0,0 +1,49 @@
1
+ import { version } from "vue";
2
+ export const isVue2 = version.startsWith("2.");
3
+ export function defineRouterProperties(target, routerGetter, routeGetter, configurable = false) {
4
+ Object.defineProperties(target, {
5
+ $router: {
6
+ configurable,
7
+ enumerable: false,
8
+ get: routerGetter
9
+ },
10
+ $route: {
11
+ configurable,
12
+ enumerable: false,
13
+ get: routeGetter
14
+ }
15
+ });
16
+ }
17
+ export function createSymbolProperty(symbol) {
18
+ return {
19
+ set(instance, value) {
20
+ instance[symbol] = value;
21
+ },
22
+ get(instance) {
23
+ return symbol in instance ? instance[symbol] : void 0;
24
+ }
25
+ };
26
+ }
27
+ export function createDependentProxy(obj, dep) {
28
+ return new Proxy(obj, {
29
+ get(target, prop, receiver) {
30
+ dep.value;
31
+ return Reflect.get(target, prop, receiver);
32
+ }
33
+ });
34
+ }
35
+ export function isESModule(obj) {
36
+ if (!obj || typeof obj !== "object") return false;
37
+ const module = obj;
38
+ return Boolean(module.__esModule) || module[Symbol.toStringTag] === "Module";
39
+ }
40
+ export function resolveComponent(component) {
41
+ if (!component) return null;
42
+ if (isESModule(component)) {
43
+ return component.default || component;
44
+ }
45
+ if (component && typeof component === "object" && !Array.isArray(component) && "default" in component && Object.keys(component).length === 1) {
46
+ return component.default;
47
+ }
48
+ return component;
49
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @vitest-environment happy-dom
3
+ */
4
+ export {};