@route-forge/vue 2.2.1 → 3.1.0
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 +30 -8
- package/README_zh.md +30 -8
- package/dist/index.cjs +127 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -15
- package/dist/index.d.ts +59 -15
- package/dist/index.js +127 -32
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
|
-
import { InjectionKey,
|
|
2
|
+
import { InjectionKey, Plugin, Ref, ComputedRef, VNode, SlotsType } from 'vue';
|
|
3
3
|
import { RouteForge, RouteForgeOptions, BoundForge, UseForgeApiBoundReturn, UseForgeApiReturn } from '@route-forge/core';
|
|
4
4
|
export { UseForgeApiBoundCall, UseForgeApiCall } from '@route-forge/core';
|
|
5
5
|
|
|
@@ -8,28 +8,29 @@ export { UseForgeApiBoundCall, UseForgeApiCall } from '@route-forge/core';
|
|
|
8
8
|
* @see .docs/SPEC.md §4.1.7
|
|
9
9
|
*
|
|
10
10
|
* 提供:
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* - useForge() / useForge(level) / useForge(level, prefix) 返回统一方法的 forge 实例
|
|
14
|
-
* • 不传 level:forge(level, name, params?) 直接调用
|
|
15
|
-
* • 传 level:forge(name, params?) 直接调用,自动绑定层级
|
|
16
|
-
* • 传 level + prefix:forge(suffix, params?) 自动拼接 prefix
|
|
11
|
+
* - createRouteForgePlugin:装配插件,install 时经 inject symbol 注入 RouteForge 实例
|
|
12
|
+
* - 返回对象上的 interceptors(转发实例拦截器):创建后即可同步注册,无需等待 ready()
|
|
17
13
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* useForge / useForgeApi / useForgeRoute 等消费 composable 见 composables/ 下各文件。
|
|
15
|
+
*
|
|
16
|
+
* 注:v3.0.0 起不再注入 `$forge` 全局属性(残缺 facade,仅 route() 且未 ready 时抛错断渲染);
|
|
17
|
+
* 模板内生成链接用 useForgeRoute / ForgeLink / ForgeRoute,命令式场景用 useForge()。
|
|
21
18
|
*/
|
|
22
19
|
|
|
23
20
|
declare const FORGE_INJECTION_KEY: InjectionKey<RouteForge>;
|
|
24
21
|
interface RouteForgePluginOptions extends RouteForgeOptions {
|
|
25
22
|
}
|
|
26
|
-
|
|
27
|
-
type VueBoundForge = BoundForge<Ref<boolean>>;
|
|
28
|
-
declare function createRouteForgePlugin(options?: RouteForgePluginOptions): Plugin<[]> & {
|
|
23
|
+
declare function createRouteForgePlugin(forgeOrOptions?: RouteForge | RouteForgePluginOptions): Plugin<[]> & {
|
|
29
24
|
ready: RouteForge['ready'];
|
|
25
|
+
/** 转发实例上的拦截器管理器:创建后即可同步注册,无需等待 ready()(请求/响应拦截链仅影响后续 api() 调用,eager 元信息预加载走 requestRaw 不受影响) */
|
|
26
|
+
interceptors: RouteForge['interceptors'];
|
|
30
27
|
};
|
|
28
|
+
|
|
31
29
|
/**
|
|
32
|
-
* 获取 forge
|
|
30
|
+
* useForge — 获取 forge 实例(Vue 特化:levelLoaded 为 Ref<boolean>)
|
|
31
|
+
* @see .docs/SPEC.md §4.1.7
|
|
32
|
+
*
|
|
33
|
+
* 内部委托 core 的 forge.use(),仅将 levelLoaded 从 Promise<void> 替换为 Vue 响应式 Ref。
|
|
33
34
|
*
|
|
34
35
|
* @example
|
|
35
36
|
* // 不绑定层级
|
|
@@ -51,6 +52,9 @@ declare function createRouteForgePlugin(options?: RouteForgePluginOptions): Plug
|
|
|
51
52
|
* forge('show', { user: 1 }) // → forge.api('admin', 'users.show', ...)
|
|
52
53
|
* forge.route('show', { user: 1 }) // → forge.route('admin', 'users.show', ...)
|
|
53
54
|
*/
|
|
55
|
+
|
|
56
|
+
/** Vue 特化类型:levelLoaded 为 Ref<boolean> */
|
|
57
|
+
type VueBoundForge = BoundForge<Ref<boolean>>;
|
|
54
58
|
declare function useForge<L extends string>(level: L, prefix: string): VueBoundForge;
|
|
55
59
|
declare function useForge<L extends string>(level: L): VueBoundForge;
|
|
56
60
|
declare function useForge(): RouteForge;
|
|
@@ -115,6 +119,11 @@ declare const ForgeRoute: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
115
119
|
readonly required: false;
|
|
116
120
|
readonly default: undefined;
|
|
117
121
|
};
|
|
122
|
+
readonly as: {
|
|
123
|
+
readonly type: vue.PropType<object>;
|
|
124
|
+
readonly required: false;
|
|
125
|
+
readonly default: undefined;
|
|
126
|
+
};
|
|
118
127
|
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
119
128
|
[key: string]: any;
|
|
120
129
|
}>[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
@@ -131,14 +140,24 @@ declare const ForgeRoute: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
131
140
|
readonly required: false;
|
|
132
141
|
readonly default: undefined;
|
|
133
142
|
};
|
|
143
|
+
readonly as: {
|
|
144
|
+
readonly type: vue.PropType<object>;
|
|
145
|
+
readonly required: false;
|
|
146
|
+
readonly default: undefined;
|
|
147
|
+
};
|
|
134
148
|
}>> & Readonly<{}>, {
|
|
135
149
|
readonly params: Record<string, unknown> | (() => Record<string, unknown>);
|
|
150
|
+
readonly as: object;
|
|
136
151
|
}, SlotsType<{
|
|
137
152
|
default?: (props: {
|
|
138
153
|
href: string;
|
|
139
154
|
loaded: boolean;
|
|
140
155
|
}) => VNode[];
|
|
141
156
|
loading?: () => VNode[];
|
|
157
|
+
/** 解析失败(路由名不存在等):props 携带错误对象;未传回落 loading 插槽 */
|
|
158
|
+
error?: (props: {
|
|
159
|
+
error: unknown;
|
|
160
|
+
}) => VNode[];
|
|
142
161
|
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
143
162
|
type ForgeRouteProps = InstanceType<typeof ForgeRoute>['$props'];
|
|
144
163
|
|
|
@@ -156,6 +175,11 @@ declare const ForgeLink: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
156
175
|
readonly required: false;
|
|
157
176
|
readonly default: undefined;
|
|
158
177
|
};
|
|
178
|
+
readonly as: {
|
|
179
|
+
readonly type: vue.PropType<object>;
|
|
180
|
+
readonly required: false;
|
|
181
|
+
readonly default: undefined;
|
|
182
|
+
};
|
|
159
183
|
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
160
184
|
[key: string]: any;
|
|
161
185
|
}> | VNode<vue.RendererNode, vue.RendererElement, {
|
|
@@ -174,14 +198,34 @@ declare const ForgeLink: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
174
198
|
readonly required: false;
|
|
175
199
|
readonly default: undefined;
|
|
176
200
|
};
|
|
201
|
+
readonly as: {
|
|
202
|
+
readonly type: vue.PropType<object>;
|
|
203
|
+
readonly required: false;
|
|
204
|
+
readonly default: undefined;
|
|
205
|
+
};
|
|
177
206
|
}>> & Readonly<{}>, {
|
|
178
207
|
readonly params: Record<string, unknown> | (() => Record<string, unknown>);
|
|
208
|
+
readonly as: object;
|
|
179
209
|
}, SlotsType<{
|
|
180
210
|
/** 链接文本/内容 */
|
|
181
211
|
default?: () => VNode[];
|
|
182
212
|
/** 未加载占位 */
|
|
183
213
|
loading?: () => VNode[];
|
|
214
|
+
/** 解析失败(路由名不存在等):props 携带错误对象;未传回落 loading 插槽 */
|
|
215
|
+
error?: (props: {
|
|
216
|
+
error: unknown;
|
|
217
|
+
}) => VNode[];
|
|
184
218
|
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
185
219
|
type ForgeLinkProps = InstanceType<typeof ForgeLink>['$props'];
|
|
186
220
|
|
|
187
|
-
|
|
221
|
+
declare const ForgeReady: vue.DefineComponent<{}, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
222
|
+
[key: string]: any;
|
|
223
|
+
}>[] | null | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, SlotsType<{
|
|
224
|
+
/** ready 后渲染的内容 */
|
|
225
|
+
default?: () => VNode[];
|
|
226
|
+
/** 门闩期间的占位 UI(缺省不渲染任何内容) */
|
|
227
|
+
fallback?: () => VNode[];
|
|
228
|
+
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
229
|
+
type ForgeReadyProps = InstanceType<typeof ForgeReady>['$props'];
|
|
230
|
+
|
|
231
|
+
export { FORGE_INJECTION_KEY, ForgeLink, type ForgeLinkProps, ForgeReady, type ForgeReadyProps, ForgeRoute, type ForgeRouteDegradeHooks, type ForgeRouteProps, type RouteForgePluginOptions, type UseForgeApiBoundReturnVue, type UseForgeApiReturnVue, type VueBoundForge, createRouteForgePlugin, useForge, useForgeApi, useForgeRoute };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
|
-
import { InjectionKey,
|
|
2
|
+
import { InjectionKey, Plugin, Ref, ComputedRef, VNode, SlotsType } from 'vue';
|
|
3
3
|
import { RouteForge, RouteForgeOptions, BoundForge, UseForgeApiBoundReturn, UseForgeApiReturn } from '@route-forge/core';
|
|
4
4
|
export { UseForgeApiBoundCall, UseForgeApiCall } from '@route-forge/core';
|
|
5
5
|
|
|
@@ -8,28 +8,29 @@ export { UseForgeApiBoundCall, UseForgeApiCall } from '@route-forge/core';
|
|
|
8
8
|
* @see .docs/SPEC.md §4.1.7
|
|
9
9
|
*
|
|
10
10
|
* 提供:
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* - useForge() / useForge(level) / useForge(level, prefix) 返回统一方法的 forge 实例
|
|
14
|
-
* • 不传 level:forge(level, name, params?) 直接调用
|
|
15
|
-
* • 传 level:forge(name, params?) 直接调用,自动绑定层级
|
|
16
|
-
* • 传 level + prefix:forge(suffix, params?) 自动拼接 prefix
|
|
11
|
+
* - createRouteForgePlugin:装配插件,install 时经 inject symbol 注入 RouteForge 实例
|
|
12
|
+
* - 返回对象上的 interceptors(转发实例拦截器):创建后即可同步注册,无需等待 ready()
|
|
17
13
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* useForge / useForgeApi / useForgeRoute 等消费 composable 见 composables/ 下各文件。
|
|
15
|
+
*
|
|
16
|
+
* 注:v3.0.0 起不再注入 `$forge` 全局属性(残缺 facade,仅 route() 且未 ready 时抛错断渲染);
|
|
17
|
+
* 模板内生成链接用 useForgeRoute / ForgeLink / ForgeRoute,命令式场景用 useForge()。
|
|
21
18
|
*/
|
|
22
19
|
|
|
23
20
|
declare const FORGE_INJECTION_KEY: InjectionKey<RouteForge>;
|
|
24
21
|
interface RouteForgePluginOptions extends RouteForgeOptions {
|
|
25
22
|
}
|
|
26
|
-
|
|
27
|
-
type VueBoundForge = BoundForge<Ref<boolean>>;
|
|
28
|
-
declare function createRouteForgePlugin(options?: RouteForgePluginOptions): Plugin<[]> & {
|
|
23
|
+
declare function createRouteForgePlugin(forgeOrOptions?: RouteForge | RouteForgePluginOptions): Plugin<[]> & {
|
|
29
24
|
ready: RouteForge['ready'];
|
|
25
|
+
/** 转发实例上的拦截器管理器:创建后即可同步注册,无需等待 ready()(请求/响应拦截链仅影响后续 api() 调用,eager 元信息预加载走 requestRaw 不受影响) */
|
|
26
|
+
interceptors: RouteForge['interceptors'];
|
|
30
27
|
};
|
|
28
|
+
|
|
31
29
|
/**
|
|
32
|
-
* 获取 forge
|
|
30
|
+
* useForge — 获取 forge 实例(Vue 特化:levelLoaded 为 Ref<boolean>)
|
|
31
|
+
* @see .docs/SPEC.md §4.1.7
|
|
32
|
+
*
|
|
33
|
+
* 内部委托 core 的 forge.use(),仅将 levelLoaded 从 Promise<void> 替换为 Vue 响应式 Ref。
|
|
33
34
|
*
|
|
34
35
|
* @example
|
|
35
36
|
* // 不绑定层级
|
|
@@ -51,6 +52,9 @@ declare function createRouteForgePlugin(options?: RouteForgePluginOptions): Plug
|
|
|
51
52
|
* forge('show', { user: 1 }) // → forge.api('admin', 'users.show', ...)
|
|
52
53
|
* forge.route('show', { user: 1 }) // → forge.route('admin', 'users.show', ...)
|
|
53
54
|
*/
|
|
55
|
+
|
|
56
|
+
/** Vue 特化类型:levelLoaded 为 Ref<boolean> */
|
|
57
|
+
type VueBoundForge = BoundForge<Ref<boolean>>;
|
|
54
58
|
declare function useForge<L extends string>(level: L, prefix: string): VueBoundForge;
|
|
55
59
|
declare function useForge<L extends string>(level: L): VueBoundForge;
|
|
56
60
|
declare function useForge(): RouteForge;
|
|
@@ -115,6 +119,11 @@ declare const ForgeRoute: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
115
119
|
readonly required: false;
|
|
116
120
|
readonly default: undefined;
|
|
117
121
|
};
|
|
122
|
+
readonly as: {
|
|
123
|
+
readonly type: vue.PropType<object>;
|
|
124
|
+
readonly required: false;
|
|
125
|
+
readonly default: undefined;
|
|
126
|
+
};
|
|
118
127
|
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
119
128
|
[key: string]: any;
|
|
120
129
|
}>[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
@@ -131,14 +140,24 @@ declare const ForgeRoute: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
131
140
|
readonly required: false;
|
|
132
141
|
readonly default: undefined;
|
|
133
142
|
};
|
|
143
|
+
readonly as: {
|
|
144
|
+
readonly type: vue.PropType<object>;
|
|
145
|
+
readonly required: false;
|
|
146
|
+
readonly default: undefined;
|
|
147
|
+
};
|
|
134
148
|
}>> & Readonly<{}>, {
|
|
135
149
|
readonly params: Record<string, unknown> | (() => Record<string, unknown>);
|
|
150
|
+
readonly as: object;
|
|
136
151
|
}, SlotsType<{
|
|
137
152
|
default?: (props: {
|
|
138
153
|
href: string;
|
|
139
154
|
loaded: boolean;
|
|
140
155
|
}) => VNode[];
|
|
141
156
|
loading?: () => VNode[];
|
|
157
|
+
/** 解析失败(路由名不存在等):props 携带错误对象;未传回落 loading 插槽 */
|
|
158
|
+
error?: (props: {
|
|
159
|
+
error: unknown;
|
|
160
|
+
}) => VNode[];
|
|
142
161
|
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
143
162
|
type ForgeRouteProps = InstanceType<typeof ForgeRoute>['$props'];
|
|
144
163
|
|
|
@@ -156,6 +175,11 @@ declare const ForgeLink: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
156
175
|
readonly required: false;
|
|
157
176
|
readonly default: undefined;
|
|
158
177
|
};
|
|
178
|
+
readonly as: {
|
|
179
|
+
readonly type: vue.PropType<object>;
|
|
180
|
+
readonly required: false;
|
|
181
|
+
readonly default: undefined;
|
|
182
|
+
};
|
|
159
183
|
}>, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
160
184
|
[key: string]: any;
|
|
161
185
|
}> | VNode<vue.RendererNode, vue.RendererElement, {
|
|
@@ -174,14 +198,34 @@ declare const ForgeLink: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
174
198
|
readonly required: false;
|
|
175
199
|
readonly default: undefined;
|
|
176
200
|
};
|
|
201
|
+
readonly as: {
|
|
202
|
+
readonly type: vue.PropType<object>;
|
|
203
|
+
readonly required: false;
|
|
204
|
+
readonly default: undefined;
|
|
205
|
+
};
|
|
177
206
|
}>> & Readonly<{}>, {
|
|
178
207
|
readonly params: Record<string, unknown> | (() => Record<string, unknown>);
|
|
208
|
+
readonly as: object;
|
|
179
209
|
}, SlotsType<{
|
|
180
210
|
/** 链接文本/内容 */
|
|
181
211
|
default?: () => VNode[];
|
|
182
212
|
/** 未加载占位 */
|
|
183
213
|
loading?: () => VNode[];
|
|
214
|
+
/** 解析失败(路由名不存在等):props 携带错误对象;未传回落 loading 插槽 */
|
|
215
|
+
error?: (props: {
|
|
216
|
+
error: unknown;
|
|
217
|
+
}) => VNode[];
|
|
184
218
|
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
185
219
|
type ForgeLinkProps = InstanceType<typeof ForgeLink>['$props'];
|
|
186
220
|
|
|
187
|
-
|
|
221
|
+
declare const ForgeReady: vue.DefineComponent<{}, () => VNode<vue.RendererNode, vue.RendererElement, {
|
|
222
|
+
[key: string]: any;
|
|
223
|
+
}>[] | null | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, SlotsType<{
|
|
224
|
+
/** ready 后渲染的内容 */
|
|
225
|
+
default?: () => VNode[];
|
|
226
|
+
/** 门闩期间的占位 UI(缺省不渲染任何内容) */
|
|
227
|
+
fallback?: () => VNode[];
|
|
228
|
+
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
229
|
+
type ForgeReadyProps = InstanceType<typeof ForgeReady>['$props'];
|
|
230
|
+
|
|
231
|
+
export { FORGE_INJECTION_KEY, ForgeLink, type ForgeLinkProps, ForgeReady, type ForgeReadyProps, ForgeRoute, type ForgeRouteDegradeHooks, type ForgeRouteProps, type RouteForgePluginOptions, type UseForgeApiBoundReturnVue, type UseForgeApiReturnVue, type VueBoundForge, createRouteForgePlugin, useForge, useForgeApi, useForgeRoute };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { defineComponent, inject, h, ref, onMounted, computed, getCurrentInstance } from 'vue';
|
|
2
1
|
import { createRouteForge } from '@route-forge/core';
|
|
2
|
+
import { defineComponent, h, ref, inject, onMounted, getCurrentScope, onScopeDispose, computed, watch, getCurrentInstance } from 'vue';
|
|
3
3
|
|
|
4
4
|
// src/plugin.ts
|
|
5
5
|
var FORGE_INJECTION_KEY = /* @__PURE__ */ Symbol("route-forge");
|
|
6
|
-
function createRouteForgePlugin(
|
|
7
|
-
const forge = createRouteForge(
|
|
6
|
+
function createRouteForgePlugin(forgeOrOptions = {}) {
|
|
7
|
+
const forge = isRouteForge(forgeOrOptions) ? forgeOrOptions : createRouteForge(forgeOrOptions);
|
|
8
8
|
return {
|
|
9
9
|
ready: forge.ready.bind(forge),
|
|
10
|
+
interceptors: forge.interceptors,
|
|
10
11
|
install(app) {
|
|
11
12
|
app.provide(FORGE_INJECTION_KEY, forge);
|
|
12
|
-
app.config.globalProperties.$forge = {
|
|
13
|
-
route: (level, name, params) => forge.route(level, name, params)
|
|
14
|
-
};
|
|
15
13
|
}
|
|
16
14
|
};
|
|
17
15
|
}
|
|
16
|
+
function isRouteForge(x) {
|
|
17
|
+
return typeof x === "object" && x !== null && typeof x.api === "function" && typeof x.ready === "function";
|
|
18
|
+
}
|
|
18
19
|
function useForge(level, prefix) {
|
|
19
20
|
const forge = inject(FORGE_INJECTION_KEY);
|
|
20
21
|
if (!forge) {
|
|
@@ -62,7 +63,40 @@ function useForgeApi(level, prefix) {
|
|
|
62
63
|
}
|
|
63
64
|
return { pending, error, call };
|
|
64
65
|
}
|
|
65
|
-
function
|
|
66
|
+
function useInjectedForge(apiName) {
|
|
67
|
+
const forge = inject(FORGE_INJECTION_KEY);
|
|
68
|
+
if (!forge) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`[route-forge/vue] ${apiName} must be used inside an app with createRouteForgePlugin() installed`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return forge;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/degrade.ts
|
|
77
|
+
var reportedKeys = /* @__PURE__ */ new Set();
|
|
78
|
+
var renderWarnKeys = /* @__PURE__ */ new Set();
|
|
79
|
+
function dedupeKey(component, error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
return `${component}:${message}`;
|
|
82
|
+
}
|
|
83
|
+
function reportDegrade(component, error, enabled = true) {
|
|
84
|
+
if (!enabled) return;
|
|
85
|
+
const key = dedupeKey(component, error);
|
|
86
|
+
if (reportedKeys.has(key)) return;
|
|
87
|
+
reportedKeys.add(key);
|
|
88
|
+
console.error(
|
|
89
|
+
`%c[route-forge]%c ${component} \u8DEF\u7531\u89E3\u6790\u5931\u8D25\uFF08\u5DF2\u964D\u7EA7\u4E3A\u7A7A\u5B57\u7B26\u4E32\uFF0C\u6E32\u67D3\u672A\u4E2D\u65AD\uFF09`,
|
|
90
|
+
"color:#c0392b;font-weight:bold",
|
|
91
|
+
"color:inherit",
|
|
92
|
+
error
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
function reportRenderWarn(error, enabled) {
|
|
96
|
+
if (!enabled) return;
|
|
97
|
+
const key = `useForgeRoute:${error instanceof Error ? error.message : String(error)}`;
|
|
98
|
+
if (renderWarnKeys.has(key)) return;
|
|
99
|
+
renderWarnKeys.add(key);
|
|
66
100
|
console.warn(
|
|
67
101
|
"%c[route-forge]%c useForgeRoute \u6E32\u67D3\u671F\u9519\u8BEF\uFF08\u5DF2\u964D\u7EA7\u4E3A\u7A7A\u5B57\u7B26\u4E32\uFF0C\u6E32\u67D3\u672A\u4E2D\u65AD\uFF09",
|
|
68
102
|
"color:#e67e22;font-weight:bold",
|
|
@@ -70,13 +104,18 @@ function warnRenderError(error) {
|
|
|
70
104
|
error
|
|
71
105
|
);
|
|
72
106
|
}
|
|
107
|
+
|
|
108
|
+
// src/composables/useForgeRoute.ts
|
|
73
109
|
function useForgeRoute(level, name, params, hooks) {
|
|
110
|
+
return useForgeRouteState(level, name, params, hooks).href;
|
|
111
|
+
}
|
|
112
|
+
function useForgeRouteState(level, name, params, hooks) {
|
|
74
113
|
if (typeof level !== "string") {
|
|
75
114
|
throw new TypeError(
|
|
76
115
|
"[route-forge/vue] useForgeRoute(): level must be a static string \u2014 getter form is not supported (levels are deterministic declarations; create another useForgeRoute call for another level)"
|
|
77
116
|
);
|
|
78
117
|
}
|
|
79
|
-
const forge =
|
|
118
|
+
const forge = useInjectedForge("useForgeRoute()");
|
|
80
119
|
const lvl = level;
|
|
81
120
|
const levelLoaded = ref(forge.isLoaded(lvl));
|
|
82
121
|
if (!levelLoaded.value) {
|
|
@@ -87,17 +126,36 @@ function useForgeRoute(level, name, params, hooks) {
|
|
|
87
126
|
});
|
|
88
127
|
});
|
|
89
128
|
}
|
|
90
|
-
|
|
91
|
-
|
|
129
|
+
const dataVersion = ref(0);
|
|
130
|
+
const unsubscribe = forge.onRoutesChange((changed) => {
|
|
131
|
+
if (changed === lvl) dataVersion.value++;
|
|
132
|
+
});
|
|
133
|
+
if (getCurrentScope()) onScopeDispose(unsubscribe);
|
|
134
|
+
const state = computed(() => {
|
|
135
|
+
if (!levelLoaded.value) return { href: "", error: null };
|
|
136
|
+
void dataVersion.value;
|
|
92
137
|
const n = typeof name === "function" ? name() : name;
|
|
93
138
|
const p = params ? params() : void 0;
|
|
94
139
|
try {
|
|
95
|
-
return forge.route(lvl, n, p);
|
|
140
|
+
return { href: forge.route(lvl, n, p), error: null };
|
|
96
141
|
} catch (e) {
|
|
97
|
-
|
|
98
|
-
return "";
|
|
142
|
+
return { href: "", error: e };
|
|
99
143
|
}
|
|
100
144
|
});
|
|
145
|
+
watch(
|
|
146
|
+
() => state.value.error,
|
|
147
|
+
(err) => {
|
|
148
|
+
if (err == null) return;
|
|
149
|
+
if (hooks?.onDegrade) hooks.onDegrade(err);
|
|
150
|
+
else reportRenderWarn(err, forge.warnings);
|
|
151
|
+
},
|
|
152
|
+
{ immediate: true }
|
|
153
|
+
);
|
|
154
|
+
return {
|
|
155
|
+
href: computed(() => state.value.href),
|
|
156
|
+
error: computed(() => state.value.error),
|
|
157
|
+
isLevelLoaded: computed(() => levelLoaded.value)
|
|
158
|
+
};
|
|
101
159
|
}
|
|
102
160
|
var forgeLinkProps = {
|
|
103
161
|
/** 层级名:静态快照,setup 求值一次后固定,不支持中途切换 */
|
|
@@ -112,16 +170,18 @@ var forgeLinkProps = {
|
|
|
112
170
|
type: [Object, Function],
|
|
113
171
|
required: false,
|
|
114
172
|
default: void 0
|
|
173
|
+
},
|
|
174
|
+
/**
|
|
175
|
+
* 手动注入链接组件(组件对象或标签名字符串),注入后同时收到 `href` 与 `to` 两个 prop
|
|
176
|
+
* (对齐 @route-forge/react 的 ForgeLink `as` 契约)。不传时按默认链路:
|
|
177
|
+
* 探测全局 RouterLink → 原生 `<a>`。用于 RouterLink 局部注册 / 非 vue-router 链接组件场景。
|
|
178
|
+
*/
|
|
179
|
+
as: {
|
|
180
|
+
type: [Object, String, Function],
|
|
181
|
+
required: false,
|
|
182
|
+
default: void 0
|
|
115
183
|
}
|
|
116
184
|
};
|
|
117
|
-
function reportDegrade(component, error) {
|
|
118
|
-
console.error(
|
|
119
|
-
`%c[route-forge]%c ${component} \u8DEF\u7531\u89E3\u6790\u5931\u8D25\uFF08\u5DF2\u964D\u7EA7\u4E3A\u7A7A\u5B57\u7B26\u4E32\uFF0C\u6E32\u67D3\u672A\u4E2D\u65AD\uFF09`,
|
|
120
|
-
"color:#c0392b;font-weight:bold",
|
|
121
|
-
"color:inherit",
|
|
122
|
-
error
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
185
|
function warnUnloadedOnce(component, level, warned) {
|
|
126
186
|
if (warned.value) return;
|
|
127
187
|
warned.value = true;
|
|
@@ -143,20 +203,23 @@ var ForgeRoute = defineComponent({
|
|
|
143
203
|
props: forgeLinkProps,
|
|
144
204
|
slots: Object,
|
|
145
205
|
setup(props, { slots }) {
|
|
146
|
-
const forge =
|
|
206
|
+
const forge = useInjectedForge("ForgeRoute");
|
|
147
207
|
const nameGetter = () => typeof props.name === "function" ? props.name() : props.name;
|
|
148
208
|
const paramsGetter = () => typeof props.params === "function" ? props.params() : props.params;
|
|
149
|
-
const
|
|
150
|
-
onDegrade: (e) => reportDegrade("ForgeRoute", e)
|
|
209
|
+
const state = useForgeRouteState(props.level, nameGetter, paramsGetter, {
|
|
210
|
+
onDegrade: (e) => reportDegrade("ForgeRoute", e, forge.warnings)
|
|
151
211
|
});
|
|
152
212
|
const unloadWarned = { value: false };
|
|
153
213
|
return () => {
|
|
154
|
-
const url = href.value;
|
|
214
|
+
const url = state.href.value;
|
|
155
215
|
const loaded = url !== "";
|
|
156
216
|
if (!loaded && !forge.isLoaded(props.level)) {
|
|
157
217
|
warnUnloadedOnce("ForgeRoute", props.level, unloadWarned);
|
|
158
218
|
}
|
|
159
|
-
if (!
|
|
219
|
+
if (!state.isLevelLoaded.value) return slots.loading ? slots.loading() : null;
|
|
220
|
+
if (state.error.value != null) {
|
|
221
|
+
return slots.error ? slots.error({ error: state.error.value }) : slots.loading ? slots.loading() : null;
|
|
222
|
+
}
|
|
160
223
|
return slots.default ? slots.default({ href: url, loaded }) : null;
|
|
161
224
|
};
|
|
162
225
|
}
|
|
@@ -168,22 +231,32 @@ var ForgeLink = defineComponent({
|
|
|
168
231
|
props: forgeLinkProps,
|
|
169
232
|
slots: Object,
|
|
170
233
|
setup(props, { slots, attrs }) {
|
|
171
|
-
const forge =
|
|
234
|
+
const forge = useInjectedForge("ForgeLink");
|
|
172
235
|
const nameGetter = () => typeof props.name === "function" ? props.name() : props.name;
|
|
173
236
|
const paramsGetter = () => typeof props.params === "function" ? props.params() : props.params;
|
|
174
|
-
const
|
|
175
|
-
onDegrade: (e) => reportDegrade("ForgeLink", e)
|
|
237
|
+
const state = useForgeRouteState(props.level, nameGetter, paramsGetter, {
|
|
238
|
+
onDegrade: (e) => reportDegrade("ForgeLink", e, forge.warnings)
|
|
176
239
|
});
|
|
177
240
|
const unloadWarned = { value: false };
|
|
178
241
|
const routerLink = resolveRouterLink();
|
|
179
242
|
return () => {
|
|
180
|
-
const url = href.value;
|
|
243
|
+
const url = state.href.value;
|
|
181
244
|
const loaded = url !== "";
|
|
182
245
|
if (!loaded && !forge.isLoaded(props.level)) {
|
|
183
246
|
warnUnloadedOnce("ForgeLink", props.level, unloadWarned);
|
|
184
247
|
}
|
|
185
|
-
if (!
|
|
248
|
+
if (!state.isLevelLoaded.value) return slots.loading ? slots.loading() : null;
|
|
249
|
+
if (state.error.value != null) {
|
|
250
|
+
return slots.error ? slots.error({ error: state.error.value }) : slots.loading ? slots.loading() : null;
|
|
251
|
+
}
|
|
186
252
|
const children = slots.default ? slots.default() : void 0;
|
|
253
|
+
if (props.as) {
|
|
254
|
+
return h(
|
|
255
|
+
props.as,
|
|
256
|
+
{ ...attrs, to: url, href: url },
|
|
257
|
+
{ default: () => children }
|
|
258
|
+
);
|
|
259
|
+
}
|
|
187
260
|
if (routerLink) {
|
|
188
261
|
return h(
|
|
189
262
|
routerLink,
|
|
@@ -195,7 +268,29 @@ var ForgeLink = defineComponent({
|
|
|
195
268
|
};
|
|
196
269
|
}
|
|
197
270
|
});
|
|
271
|
+
var ForgeReady = defineComponent({
|
|
272
|
+
name: "ForgeReady",
|
|
273
|
+
slots: Object,
|
|
274
|
+
setup(_, { slots }) {
|
|
275
|
+
const forge = useInjectedForge("ForgeReady");
|
|
276
|
+
const open = ref(forge.isReady());
|
|
277
|
+
if (!open.value) {
|
|
278
|
+
forge.ready().then(
|
|
279
|
+
() => {
|
|
280
|
+
open.value = true;
|
|
281
|
+
},
|
|
282
|
+
(err) => {
|
|
283
|
+
console.error(
|
|
284
|
+
"[route-forge] ForgeReady: ready() rejected \u2014 content stays gated (same as an unhandled ready().catch)",
|
|
285
|
+
err
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return () => open.value ? slots.default?.() : slots.fallback ? slots.fallback() : null;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
198
293
|
|
|
199
|
-
export { FORGE_INJECTION_KEY, ForgeLink, ForgeRoute, createRouteForgePlugin, useForge, useForgeApi, useForgeRoute };
|
|
294
|
+
export { FORGE_INJECTION_KEY, ForgeLink, ForgeReady, ForgeRoute, createRouteForgePlugin, useForge, useForgeApi, useForgeRoute };
|
|
200
295
|
//# sourceMappingURL=index.js.map
|
|
201
296
|
//# sourceMappingURL=index.js.map
|