@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,57 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ createApp,
4
+ getCurrentInstance,
5
+ h,
6
+ inject,
7
+ nextTick,
8
+ provide
9
+ } from "vue";
10
+ describe("app.runWithContext()", () => {
11
+ it("should exist on Vue app", () => {
12
+ const app = createApp({ render: () => h("div") });
13
+ expect(typeof app.runWithContext).toBe("function");
14
+ });
15
+ it("should enable inject to read provided value within context", () => {
16
+ const KEY = Symbol("ctx-key");
17
+ const app = createApp({ render: () => h("div") });
18
+ app.provide(KEY, 42);
19
+ const value = app.runWithContext(() => inject(KEY));
20
+ expect(value).toBe(42);
21
+ });
22
+ it("should not read value provided in root setup via runWithContext", async () => {
23
+ const KEY = Symbol("ctx-key-setup");
24
+ const app = createApp({
25
+ setup() {
26
+ provide(KEY, 7);
27
+ return () => h("div");
28
+ }
29
+ });
30
+ const container = document.createElement("div");
31
+ document.body.appendChild(container);
32
+ app.mount(container);
33
+ await nextTick();
34
+ const value = app.runWithContext(() => inject(KEY));
35
+ expect(value).toBeUndefined();
36
+ app.unmount();
37
+ container.remove();
38
+ });
39
+ it("should read app-level provide set inside setup via appContext", async () => {
40
+ const KEY = Symbol("ctx-key-setup-app");
41
+ const app = createApp({
42
+ setup() {
43
+ const appInst = getCurrentInstance().appContext.app;
44
+ appInst.provide(KEY, 9);
45
+ return () => h("div");
46
+ }
47
+ });
48
+ const container = document.createElement("div");
49
+ document.body.appendChild(container);
50
+ app.mount(container);
51
+ await nextTick();
52
+ const value = app.runWithContext(() => inject(KEY));
53
+ expect(value).toBe(9);
54
+ app.unmount();
55
+ container.remove();
56
+ });
57
+ });
package/dist/use.d.ts ADDED
@@ -0,0 +1,260 @@
1
+ import type { Route, Router, RouterLinkProps } from '@esmx/router';
2
+ export interface VueInstance {
3
+ $parent?: VueInstance | null;
4
+ $root?: VueInstance | null;
5
+ $children?: VueInstance[] | null;
6
+ }
7
+ /**
8
+ * Get router instance from a Vue component instance.
9
+ * This is a lower-level function used internally by useRouter().
10
+ * Use this in Options API, use useRouter() in Composition API.
11
+ *
12
+ * @param instance - Vue component instance
13
+ * @returns Router instance
14
+ * @throws {Error} If router context is not found
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Options API usage
19
+ * import { defineComponent } from 'vue';
20
+ * import { getRouter } from '@esmx/router-vue';
21
+ *
22
+ * export default defineComponent({
23
+ * mounted() {
24
+ * const router = getRouter(this);
25
+ * router.push('/dashboard');
26
+ * },
27
+ * methods: {
28
+ * handleNavigation() {
29
+ * const router = getRouter(this);
30
+ * router.replace('/profile');
31
+ * }
32
+ * }
33
+ * });
34
+ * ```
35
+ */
36
+ export declare function getRouter(instance: VueInstance): Router;
37
+ /**
38
+ * Get current route from a Vue component instance.
39
+ * This is a lower-level function used internally by useRoute().
40
+ * Use this in Options API, use useRoute() in Composition API.
41
+ *
42
+ * @param instance - Vue component instance
43
+ * @returns Current route object
44
+ * @throws {Error} If router context is not found
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Options API usage
49
+ * import { defineComponent } from 'vue';
50
+ * import { getRoute } from '@esmx/router-vue';
51
+ *
52
+ * export default defineComponent({
53
+ * computed: {
54
+ * routeInfo() {
55
+ * const route = getRoute(this);
56
+ * return {
57
+ * path: route.path,
58
+ * params: route.params,
59
+ * query: route.query
60
+ * };
61
+ * }
62
+ * }
63
+ * });
64
+ * ```
65
+ */
66
+ export declare function getRoute(instance: VueInstance): Route;
67
+ /**
68
+ * Get the router instance in a Vue component.
69
+ * Must be called within setup() or other composition functions.
70
+ * Use this in Composition API, use getRouter() in Options API.
71
+ *
72
+ * @returns Router instance for navigation and route management
73
+ * @throws {Error} If called outside setup() or router context not found
74
+ *
75
+ * @example
76
+ * ```vue
77
+ * <script setup lang="ts">
78
+ * import { useRouter } from '@esmx/router-vue';
79
+ *
80
+ * const router = useRouter();
81
+ *
82
+ * const navigateToHome = () => {
83
+ * router.push('/home');
84
+ * };
85
+ *
86
+ * const goBack = () => {
87
+ * router.back();
88
+ * };
89
+ *
90
+ * const navigateWithQuery = () => {
91
+ * router.push({
92
+ * path: '/search',
93
+ * query: { q: 'vue router', page: '1' }
94
+ * });
95
+ * };
96
+ * </script>
97
+ * ```
98
+ */
99
+ export declare function useRouter(): Router;
100
+ /**
101
+ * Get the current route information in a Vue component.
102
+ * Returns a reactive reference that automatically updates when the route changes.
103
+ * Must be called within setup() or other composition functions.
104
+ * Use this in Composition API, use getRoute() in Options API.
105
+ *
106
+ * @returns Current route object with path, params, query, etc.
107
+ * @throws {Error} If called outside setup() or router context not found
108
+ *
109
+ * @example
110
+ * ```vue
111
+ * <template>
112
+ * <div>
113
+ * <h1>{{ route.meta?.title || 'Page' }}</h1>
114
+ * <p>Path: {{ route.path }}</p>
115
+ * <p>Params: {{ JSON.stringify(route.params) }}</p>
116
+ * <p>Query: {{ JSON.stringify(route.query) }}</p>
117
+ * </div>
118
+ * </template>
119
+ *
120
+ * <script setup lang="ts">
121
+ * import { useRoute } from '@esmx/router-vue';
122
+ * import { watch } from 'vue';
123
+ *
124
+ * const route = useRoute();
125
+ *
126
+ * watch(() => route.path, (newPath) => {
127
+ * console.log('Route changed to:', newPath);
128
+ * });
129
+ * </script>
130
+ * ```
131
+ */
132
+ export declare function useRoute(): Route;
133
+ /**
134
+ * Provide router context to child components.
135
+ * This must be called in a parent component to make the router available
136
+ * to child components via useRouter() and useRoute().
137
+ *
138
+ * @param router - Router instance to provide to child components
139
+ * @throws {Error} If called outside setup()
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * // Vue 3 usage
144
+ * import { createApp } from 'vue';
145
+ * import { Router } from '@esmx/router';
146
+ * import { useProvideRouter } from '@esmx/router-vue';
147
+ *
148
+ * const routes = [
149
+ * { path: '/', component: () => import('./Home.vue') },
150
+ * { path: '/about', component: () => import('./About.vue') }
151
+ * ];
152
+ *
153
+ * const router = new Router({ routes });
154
+ * const app = createApp({
155
+ * setup() {
156
+ * useProvideRouter(router);
157
+ * }
158
+ * });
159
+ * app.mount('#app');
160
+ * ```
161
+ */
162
+ export declare function useProvideRouter(router: Router): void;
163
+ /**
164
+ * Get the current RouterView depth in nested routing scenarios.
165
+ * Returns the depth of the current RouterView component in the component tree.
166
+ * Useful for advanced routing scenarios where you need to know the nesting level.
167
+ *
168
+ * @param isRender - Whether this is used in a RouterView component that needs to provide depth for children (default: false)
169
+ * @returns Current RouterView depth (0 for root level, 1 for first nested level, etc.)
170
+ * @throws {Error} If called outside setup()
171
+ *
172
+ * @example
173
+ * ```vue
174
+ * <template>
175
+ * <div>
176
+ * <p>Current RouterView depth: {{ depth }}</p>
177
+ * <RouterView />
178
+ * </div>
179
+ * </template>
180
+ *
181
+ * <script setup lang="ts">
182
+ * import { useRouterViewDepth } from '@esmx/router-vue';
183
+ *
184
+ * // Get current depth without providing for children
185
+ * const depth = useRouterViewDepth();
186
+ * console.log('Current RouterView depth:', depth); // 0, 1, 2, etc.
187
+ *
188
+ * // Get current depth and provide depth + 1 for children (used in RouterView component)
189
+ * const depth = useRouterViewDepth(true);
190
+ * </script>
191
+ * ```
192
+ */
193
+ export declare function _useRouterViewDepth(isRender?: boolean): number;
194
+ /**
195
+ * Get the current RouterView depth in nested routing scenarios.
196
+ * Returns the depth of the current RouterView component in the component tree.
197
+ * Useful for advanced routing scenarios where you need to know the nesting level.
198
+ *
199
+ * @returns Current RouterView depth (0 for root level, 1 for first nested level, etc.)
200
+ * @throws {Error} If called outside setup()
201
+ *
202
+ * @example
203
+ * ```vue
204
+ * <template>
205
+ * <div>
206
+ * <p>Current RouterView depth: {{ depth }}</p>
207
+ * <RouterView />
208
+ * </div>
209
+ * </template>
210
+ *
211
+ * <script setup lang="ts">
212
+ * import { useRouterViewDepth } from '@esmx/router-vue';
213
+ *
214
+ * // Get current depth without providing for children
215
+ * const depth = useRouterViewDepth();
216
+ * console.log('Current RouterView depth:', depth); // 0, 1, 2, etc.
217
+ * </script>
218
+ * ```
219
+ */
220
+ export declare function useRouterViewDepth(): number;
221
+ /**
222
+ * Get injected RouterView depth from a Vue instance's ancestors.
223
+ * Traverses parent chain to find the value provided under ROUTER_VIEW_DEPTH_KEY.
224
+ *
225
+ * @param instance - Vue component instance to start from
226
+ * @returns Injected RouterView depth value from nearest ancestor
227
+ * @throws {Error} If no ancestor provided ROUTER_VIEW_DEPTH_KEY
228
+ */
229
+ export declare function getRouterViewDepth(instance: VueInstance): number;
230
+ /**
231
+ * Create reactive link helpers for navigation elements.
232
+ * Returns computed properties for link attributes, classes, and event handlers.
233
+ *
234
+ * @param props - RouterLink properties configuration
235
+ * @returns Computed link resolver with attributes and event handlers
236
+ *
237
+ * @example
238
+ * ```vue
239
+ * <template>
240
+ * <a
241
+ * v-bind="link.attributes"
242
+ * v-on="link.createEventHandlers()"
243
+ * :class="{ active: link.isActive }"
244
+ * >
245
+ * Home
246
+ * </a>
247
+ * </template>
248
+ *
249
+ * <script setup lang="ts">
250
+ * import { useLink } from '@esmx/router-vue';
251
+ *
252
+ * const link = useLink({
253
+ * to: '/home',
254
+ * type: 'push',
255
+ * exact: 'include'
256
+ * }).value;
257
+ * </script>
258
+ * ```
259
+ */
260
+ export declare function useLink(props: RouterLinkProps): import("vue").ComputedRef<import("@esmx/router").RouterLinkResolved>;
package/dist/use.mjs ADDED
@@ -0,0 +1,125 @@
1
+ import {
2
+ computed,
3
+ getCurrentInstance,
4
+ inject,
5
+ onBeforeUnmount,
6
+ provide,
7
+ ref
8
+ } from "vue";
9
+ import {
10
+ createDependentProxy,
11
+ createSymbolProperty,
12
+ defineRouterProperties,
13
+ isVue2
14
+ } from "./util.mjs";
15
+ const ROUTER_CONTEXT_KEY = Symbol("router-context");
16
+ const ROUTER_INJECT_KEY = Symbol("router-inject");
17
+ const ROUTER_VIEW_DEPTH_KEY = Symbol("router-view-depth");
18
+ const routerContextProperty = createSymbolProperty(ROUTER_CONTEXT_KEY);
19
+ const routerViewDepthProperty = createSymbolProperty(
20
+ ROUTER_VIEW_DEPTH_KEY
21
+ );
22
+ function getCurrentProxy() {
23
+ const instance = getCurrentInstance();
24
+ if (!instance || !instance.proxy) {
25
+ throw new Error(
26
+ "[@esmx/router-vue] Must be used within setup() or other composition functions"
27
+ );
28
+ }
29
+ return instance.proxy;
30
+ }
31
+ function findRouterContext(vm) {
32
+ let context = routerContextProperty.get(vm);
33
+ if (context) {
34
+ return context;
35
+ }
36
+ let current = vm.$parent;
37
+ while (current) {
38
+ context = routerContextProperty.get(current);
39
+ if (context) {
40
+ routerContextProperty.set(vm, context);
41
+ return context;
42
+ }
43
+ current = current.$parent;
44
+ }
45
+ throw new Error(
46
+ "[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component."
47
+ );
48
+ }
49
+ export function getRouter(instance) {
50
+ return findRouterContext(instance).router;
51
+ }
52
+ export function getRoute(instance) {
53
+ return findRouterContext(instance).route;
54
+ }
55
+ function useRouterContext() {
56
+ const injectedContext = inject(ROUTER_INJECT_KEY);
57
+ if (injectedContext) {
58
+ return injectedContext;
59
+ }
60
+ const proxy = getCurrentProxy();
61
+ return findRouterContext(proxy);
62
+ }
63
+ export function useRouter() {
64
+ return useRouterContext().router;
65
+ }
66
+ export function useRoute() {
67
+ return useRouterContext().route;
68
+ }
69
+ export function useProvideRouter(router) {
70
+ const proxy = getCurrentProxy();
71
+ const dep = ref(0);
72
+ const proxiedRouter = createDependentProxy(router, dep);
73
+ const proxiedRoute = createDependentProxy(router.route, dep);
74
+ const context = {
75
+ router: proxiedRouter,
76
+ route: proxiedRoute
77
+ };
78
+ provide(ROUTER_INJECT_KEY, context);
79
+ routerContextProperty.set(proxy, context);
80
+ if (!isVue2) {
81
+ const app = getCurrentInstance().appContext.app;
82
+ defineRouterProperties(
83
+ app.config.globalProperties,
84
+ () => proxiedRouter,
85
+ () => proxiedRoute,
86
+ true
87
+ );
88
+ }
89
+ const unwatch = router.afterEach((to) => {
90
+ if (router.route === to) {
91
+ to.syncTo(proxiedRoute);
92
+ dep.value++;
93
+ }
94
+ });
95
+ onBeforeUnmount(unwatch);
96
+ }
97
+ export function _useRouterViewDepth(isRender) {
98
+ const depth = inject(ROUTER_VIEW_DEPTH_KEY, 0);
99
+ if (isRender) {
100
+ provide(ROUTER_VIEW_DEPTH_KEY, depth + 1);
101
+ const proxy = getCurrentProxy();
102
+ routerViewDepthProperty.set(proxy, depth + 1);
103
+ }
104
+ return depth;
105
+ }
106
+ export function useRouterViewDepth() {
107
+ return _useRouterViewDepth();
108
+ }
109
+ export function getRouterViewDepth(instance) {
110
+ let current = instance.$parent;
111
+ while (current) {
112
+ const value = routerViewDepthProperty.get(current);
113
+ if (typeof value === "number") return value;
114
+ current = current.$parent;
115
+ }
116
+ throw new Error(
117
+ "[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components."
118
+ );
119
+ }
120
+ export function useLink(props) {
121
+ const router = useRouter();
122
+ return computed(() => {
123
+ return router.resolveLink(props);
124
+ });
125
+ }
@@ -0,0 +1 @@
1
+ export {};