web-extend-plugin-vue2 0.3.0 → 0.3.2
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 +73 -200
- package/dist/index.cjs +540 -456
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +539 -449
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +18 -180
- package/package.json +3 -6
package/dist/index.cjs
CHANGED
|
@@ -7,23 +7,15 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
7
7
|
var Vue__default = /*#__PURE__*/_interopDefault(Vue);
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Public constants and runtime defaults consumed by resolveRuntimeOptions and
|
|
11
|
+
* other runtime modules.
|
|
12
12
|
*/
|
|
13
|
-
/** 与 `package.json` 的 peer 下限一致;`npm run test:peer-min` / CI matrix 须与此保持同步 */
|
|
14
13
|
const peerMinimumVersions = {
|
|
15
|
-
vue: '2.6.
|
|
16
|
-
vueRouter: '3.5.
|
|
14
|
+
vue: '2.6.0',
|
|
15
|
+
vueRouter: '3.5.0'
|
|
17
16
|
};
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// 协议与品牌(发布契约;与清单 `hostPluginApiVersion` 对齐 semver 主版本,一般不随宿主覆盖)
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
17
|
const HOST_PLUGIN_API_VERSION = '1.0.0';
|
|
22
|
-
/** 控制台日志与首次引导横幅使用的短名称 */
|
|
23
18
|
const RUNTIME_CONSOLE_LABEL = 'web-extend-plugin-vue2';
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// 与 `build-env` / `resolveBundledEnv` 配套的键名(单一事实来源,便于检索与文档)
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
19
|
const webExtendPluginEnvKeys = {
|
|
28
20
|
manifestBase: 'VITE_FRONTEND_PLUGIN_BASE',
|
|
29
21
|
manifestListPath: 'VITE_WEB_PLUGIN_MANIFEST_PATH',
|
|
@@ -44,27 +36,14 @@ const webExtendPluginEnvKeys = {
|
|
|
44
36
|
webPluginDevIds: 'VITE_WEB_PLUGIN_DEV_IDS',
|
|
45
37
|
webPluginDevMap: 'VITE_WEB_PLUGIN_DEV_MAP',
|
|
46
38
|
pluginsBootstrapSummary: 'VITE_PLUGINS_BOOTSTRAP_SUMMARY',
|
|
47
|
-
/** 清单路径备选:部分宿主单独使用 */
|
|
48
39
|
manifestPathAlt: 'VUE_APP_WEB_PLUGIN_MANIFEST_PATH'
|
|
49
40
|
};
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
|
-
// `manifestMode` 未配置且环境未指定时的默认值
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
41
|
const defaultManifestMode = 'api';
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// `manifest-fetch-composer` 中间件默认(中间件 options 可逐项覆盖)
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
42
|
const defaultManifestFetchCache = {
|
|
58
43
|
storageKeyPrefix: 'wep.manifestFetch.v1',
|
|
59
44
|
maxEntries: 50
|
|
60
45
|
};
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// 路由合成名前缀(`createHostApi` 为无 name 的路由生成 `__wep_${pluginId}_${seq}`)
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
46
|
const routeSynthNamePrefix = '__wep_';
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// 宿主可通过 `resolveRuntimeOptions` 覆盖的运行时默认值(与 README / index.d.ts 描述一致)
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
47
|
const defaultWebExtendPluginRuntime = {
|
|
69
48
|
manifestBase: '/fp-api',
|
|
70
49
|
manifestListPath: '/api/frontend-plugins',
|
|
@@ -76,11 +55,7 @@ const defaultWebExtendPluginRuntime = {
|
|
|
76
55
|
defaultImplicitDevPluginIds: [],
|
|
77
56
|
allowedScriptHosts: ['localhost', '127.0.0.1', '::1'],
|
|
78
57
|
bridgeAllowedPathPrefixes: ['/api/'],
|
|
79
|
-
/** 与 `hostLayoutComponent` 同时使用时默认父路由 name */
|
|
80
|
-
pluginRoutesParentName: '__wepPluginHost',
|
|
81
|
-
/** 插件壳路径(与菜单、ensurePluginHostRoute 一致) */
|
|
82
58
|
pluginMountPath: '/plugin',
|
|
83
|
-
/** `manifestMode=api` 且开发环境下,API 失败或 plugins 为空时尝试的静态 JSON(可放于 `public/web-plugins/`) */
|
|
84
59
|
devFallbackStaticManifestUrl: '/web-plugins/plugins.manifest.json'
|
|
85
60
|
};
|
|
86
61
|
|
|
@@ -417,6 +392,9 @@ function resolveRuntimeOptions$1(user = {}) {
|
|
|
417
392
|
...(typeof user.adaptRouteDeclarations === 'function'
|
|
418
393
|
? { adaptRouteDeclarations: user.adaptRouteDeclarations }
|
|
419
394
|
: {}),
|
|
395
|
+
...(typeof user.onPluginRoutesContributed === 'function'
|
|
396
|
+
? { onPluginRoutesContributed: user.onPluginRoutesContributed }
|
|
397
|
+
: {}),
|
|
420
398
|
...(user.hostContext !== undefined &&
|
|
421
399
|
user.hostContext !== null &&
|
|
422
400
|
typeof user.hostContext === 'object' &&
|
|
@@ -3208,7 +3186,7 @@ var semverExports = requireSemver();
|
|
|
3208
3186
|
var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
|
|
3209
3187
|
|
|
3210
3188
|
/**
|
|
3211
|
-
* 引导时注入的 router 引用,供 disposeWebPlugin
|
|
3189
|
+
* 引导时注入的 router 引用,供 disposeWebPlugin 卸载插件动态路由。
|
|
3212
3190
|
*/
|
|
3213
3191
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3214
3192
|
let bootstrapRouter;
|
|
@@ -3222,6 +3200,223 @@ function getPluginBootstrapRouter() {
|
|
|
3222
3200
|
return bootstrapRouter;
|
|
3223
3201
|
}
|
|
3224
3202
|
|
|
3203
|
+
/**
|
|
3204
|
+
* 记录各插件通过 registerRoutes 挂到 router 上的顶层路由,
|
|
3205
|
+
* 优先使用官方 `addRoute()` 返回的 disposer 做卸载;仅在 disposer 不可用时回退到 name。
|
|
3206
|
+
*/
|
|
3207
|
+
const pluginIdToTopRoutes = new Map();
|
|
3208
|
+
function recordPluginTopRoutes(pluginId, routes) {
|
|
3209
|
+
if (!pluginId || routes.length === 0) {
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
const normalized = routes.filter((route) => route && route.name);
|
|
3213
|
+
if (normalized.length === 0) {
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
const cur = pluginIdToTopRoutes.get(pluginId) || [];
|
|
3217
|
+
pluginIdToTopRoutes.set(pluginId, cur.concat(normalized));
|
|
3218
|
+
}
|
|
3219
|
+
function tryRemoveRouteByName(
|
|
3220
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3221
|
+
router, pluginId, name) {
|
|
3222
|
+
if (!router || typeof router.removeRoute !== 'function') {
|
|
3223
|
+
console.warn('[wep] route disposer unavailable,且 router.removeRoute 不可用,动态路由可能残留', pluginId, name);
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
try {
|
|
3227
|
+
router.removeRoute(name);
|
|
3228
|
+
}
|
|
3229
|
+
catch (e) {
|
|
3230
|
+
console.warn('[wep] removeRoute fallback failed', name, e);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* 从 matcher 移除该插件登记过的顶层路由(含其子树)。
|
|
3235
|
+
* 优先走 `router.addRoute()` 返回的 disposer;仅在 disposer 不可用时回退 `router.removeRoute(name)`。
|
|
3236
|
+
*/
|
|
3237
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3238
|
+
function removeRegisteredRoutesForPlugin(router, pluginId) {
|
|
3239
|
+
const routes = pluginIdToTopRoutes.get(pluginId);
|
|
3240
|
+
if (!routes || routes.length === 0) {
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
for (let i = routes.length - 1; i >= 0; i--) {
|
|
3244
|
+
const route = routes[i];
|
|
3245
|
+
if (typeof route.dispose === 'function') {
|
|
3246
|
+
try {
|
|
3247
|
+
route.dispose();
|
|
3248
|
+
continue;
|
|
3249
|
+
}
|
|
3250
|
+
catch (e) {
|
|
3251
|
+
console.warn('[wep] route disposer failed', route.name, e);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
tryRemoveRouteByName(router, pluginId, route.name);
|
|
3255
|
+
}
|
|
3256
|
+
pluginIdToTopRoutes.delete(pluginId);
|
|
3257
|
+
}
|
|
3258
|
+
/** 调试或宿主高级场景:查询已登记 name(勿依赖顺序语义) */
|
|
3259
|
+
function getRegisteredTopRouteNamesForPlugin(pluginId) {
|
|
3260
|
+
return (pluginIdToTopRoutes.get(pluginId) || []).map((route) => route.name);
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
/**
|
|
3264
|
+
* Host-side reactive registries for extension-point slot components.
|
|
3265
|
+
* Menus/sidebar mapping stays on the host and is not duplicated here.
|
|
3266
|
+
*/
|
|
3267
|
+
const registries = {
|
|
3268
|
+
slots: {},
|
|
3269
|
+
slotRevision: 0
|
|
3270
|
+
};
|
|
3271
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3272
|
+
function ensureRegistriesReactive(VueLike) {
|
|
3273
|
+
if (!VueLike || typeof VueLike.observable !== 'function') {
|
|
3274
|
+
return registries;
|
|
3275
|
+
}
|
|
3276
|
+
if (registries.__wepObservedBy__ === VueLike) {
|
|
3277
|
+
return registries;
|
|
3278
|
+
}
|
|
3279
|
+
VueLike.observable(registries);
|
|
3280
|
+
Object.defineProperty(registries, '__wepObservedBy__', {
|
|
3281
|
+
value: VueLike,
|
|
3282
|
+
configurable: true,
|
|
3283
|
+
enumerable: false,
|
|
3284
|
+
writable: true
|
|
3285
|
+
});
|
|
3286
|
+
return registries;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
/**
|
|
3290
|
+
* 按插件 id 收集 `onTeardown` 回调,供 `disposeWebPlugin` 统一执行。
|
|
3291
|
+
*/
|
|
3292
|
+
const _byPlugin = new Map();
|
|
3293
|
+
function registerPluginTeardown(pluginId, fn) {
|
|
3294
|
+
if (typeof fn !== 'function') {
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
let list = _byPlugin.get(pluginId);
|
|
3298
|
+
if (!list) {
|
|
3299
|
+
list = [];
|
|
3300
|
+
_byPlugin.set(pluginId, list);
|
|
3301
|
+
}
|
|
3302
|
+
list.push(fn);
|
|
3303
|
+
}
|
|
3304
|
+
function runPluginTeardowns(pluginId) {
|
|
3305
|
+
const list = _byPlugin.get(pluginId);
|
|
3306
|
+
if (!list) {
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
_byPlugin.delete(pluginId);
|
|
3310
|
+
for (const fn of list) {
|
|
3311
|
+
try {
|
|
3312
|
+
fn();
|
|
3313
|
+
}
|
|
3314
|
+
catch (e) {
|
|
3315
|
+
console.warn('[wep] teardown failed', pluginId, e);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
/**
|
|
3321
|
+
* 动态加载脚本(去重与并发合并)。
|
|
3322
|
+
*/
|
|
3323
|
+
const loadScriptMemo = new Map();
|
|
3324
|
+
function markScriptOwnership(script, pluginId) {
|
|
3325
|
+
if (pluginId) {
|
|
3326
|
+
script.setAttribute('data-plugin-asset', pluginId);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
function clearLoadedScriptMemo(src) {
|
|
3330
|
+
if (typeof src === 'string' && src) {
|
|
3331
|
+
loadScriptMemo.delete(src);
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
loadScriptMemo.clear();
|
|
3335
|
+
}
|
|
3336
|
+
function loadScript(src, pluginId) {
|
|
3337
|
+
if (typeof document === 'undefined') {
|
|
3338
|
+
return Promise.reject(new Error('loadScript: no document'));
|
|
3339
|
+
}
|
|
3340
|
+
if (loadScriptMemo.has(src)) {
|
|
3341
|
+
return loadScriptMemo.get(src);
|
|
3342
|
+
}
|
|
3343
|
+
const p = new Promise((resolve, reject) => {
|
|
3344
|
+
const scripts = document.getElementsByTagName('script');
|
|
3345
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
3346
|
+
const el = scripts[i];
|
|
3347
|
+
if (el.src === src) {
|
|
3348
|
+
markScriptOwnership(el, pluginId);
|
|
3349
|
+
if (el.getAttribute('data-wep-loaded') === 'true') {
|
|
3350
|
+
resolve();
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3353
|
+
el.addEventListener('load', () => {
|
|
3354
|
+
el.setAttribute('data-wep-loaded', 'true');
|
|
3355
|
+
resolve();
|
|
3356
|
+
}, { once: true });
|
|
3357
|
+
el.addEventListener('error', () => reject(new Error('loadScript failed: ' + src)), { once: true });
|
|
3358
|
+
return;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const s = document.createElement('script');
|
|
3362
|
+
s.async = true;
|
|
3363
|
+
s.src = src;
|
|
3364
|
+
markScriptOwnership(s, pluginId);
|
|
3365
|
+
s.onload = () => {
|
|
3366
|
+
s.setAttribute('data-wep-loaded', 'true');
|
|
3367
|
+
resolve();
|
|
3368
|
+
};
|
|
3369
|
+
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3370
|
+
document.head.appendChild(s);
|
|
3371
|
+
});
|
|
3372
|
+
loadScriptMemo.set(src, p);
|
|
3373
|
+
p.catch(() => loadScriptMemo.delete(src));
|
|
3374
|
+
return p;
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
/**
|
|
3378
|
+
* 卸载单个插件:执行 teardown、移除该插件登记的动态路由、清理注册表与 activator、移除带 `data-plugin-asset` 的 DOM。
|
|
3379
|
+
* 优先使用 `addRoute()` 返回的 disposer;必要时回退 `router.removeRoute(name)`。
|
|
3380
|
+
*/
|
|
3381
|
+
function disposeWebPlugin(pluginId) {
|
|
3382
|
+
if (!pluginId || typeof pluginId !== 'string') {
|
|
3383
|
+
return;
|
|
3384
|
+
}
|
|
3385
|
+
runPluginTeardowns(pluginId);
|
|
3386
|
+
removeRegisteredRoutesForPlugin(getPluginBootstrapRouter(), pluginId);
|
|
3387
|
+
const slots = registries.slots;
|
|
3388
|
+
for (const pointId of Object.keys(slots)) {
|
|
3389
|
+
const list = slots[pointId];
|
|
3390
|
+
if (!Array.isArray(list)) {
|
|
3391
|
+
continue;
|
|
3392
|
+
}
|
|
3393
|
+
const next = list.filter((x) => x.pluginId !== pluginId);
|
|
3394
|
+
if (next.length === 0) {
|
|
3395
|
+
Vue__default.default.delete(slots, pointId);
|
|
3396
|
+
}
|
|
3397
|
+
else if (next.length !== list.length) {
|
|
3398
|
+
Vue__default.default.set(slots, pointId, next);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
registries.slotRevision++;
|
|
3402
|
+
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
3403
|
+
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
3404
|
+
}
|
|
3405
|
+
if (typeof document !== 'undefined') {
|
|
3406
|
+
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
3407
|
+
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
3408
|
+
if (el.tagName === 'SCRIPT') {
|
|
3409
|
+
const src = el.src;
|
|
3410
|
+
if (src) {
|
|
3411
|
+
clearLoadedScriptMemo(src);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
el.remove();
|
|
3415
|
+
}
|
|
3416
|
+
});
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3225
3420
|
/**
|
|
3226
3421
|
* 开发模式插件 URL 映射(显式 JSON + 隐式 dev 探测)。
|
|
3227
3422
|
*/
|
|
@@ -3384,49 +3579,6 @@ function startPluginDevSseForMap(devMap, isDev, hostSet, ssePath) {
|
|
|
3384
3579
|
}
|
|
3385
3580
|
}
|
|
3386
3581
|
|
|
3387
|
-
/**
|
|
3388
|
-
* 动态加载脚本(去重与并发合并)。
|
|
3389
|
-
*/
|
|
3390
|
-
const loadScriptMemo = new Map();
|
|
3391
|
-
function loadScript(src) {
|
|
3392
|
-
if (typeof document === 'undefined') {
|
|
3393
|
-
return Promise.reject(new Error('loadScript: no document'));
|
|
3394
|
-
}
|
|
3395
|
-
if (loadScriptMemo.has(src)) {
|
|
3396
|
-
return loadScriptMemo.get(src);
|
|
3397
|
-
}
|
|
3398
|
-
const p = new Promise((resolve, reject) => {
|
|
3399
|
-
const scripts = document.getElementsByTagName('script');
|
|
3400
|
-
for (let i = 0; i < scripts.length; i++) {
|
|
3401
|
-
const el = scripts[i];
|
|
3402
|
-
if (el.src === src) {
|
|
3403
|
-
if (el.getAttribute('data-wep-loaded') === 'true') {
|
|
3404
|
-
resolve();
|
|
3405
|
-
return;
|
|
3406
|
-
}
|
|
3407
|
-
el.addEventListener('load', () => {
|
|
3408
|
-
el.setAttribute('data-wep-loaded', 'true');
|
|
3409
|
-
resolve();
|
|
3410
|
-
}, { once: true });
|
|
3411
|
-
el.addEventListener('error', () => reject(new Error('loadScript failed: ' + src)), { once: true });
|
|
3412
|
-
return;
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
const s = document.createElement('script');
|
|
3416
|
-
s.async = true;
|
|
3417
|
-
s.src = src;
|
|
3418
|
-
s.onload = () => {
|
|
3419
|
-
s.setAttribute('data-wep-loaded', 'true');
|
|
3420
|
-
resolve();
|
|
3421
|
-
};
|
|
3422
|
-
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3423
|
-
document.head.appendChild(s);
|
|
3424
|
-
});
|
|
3425
|
-
loadScriptMemo.set(src, p);
|
|
3426
|
-
p.catch(() => loadScriptMemo.delete(src));
|
|
3427
|
-
return p;
|
|
3428
|
-
}
|
|
3429
|
-
|
|
3430
3582
|
let _printed = false;
|
|
3431
3583
|
/** 在首次引导插件时打印一行运行时标识(非大块 ASCII art)。 */
|
|
3432
3584
|
function printRuntimeBannerOnce() {
|
|
@@ -3575,6 +3727,29 @@ function resolveStaticManifestUrlForFetch(url, origin) {
|
|
|
3575
3727
|
}
|
|
3576
3728
|
}
|
|
3577
3729
|
|
|
3730
|
+
/**
|
|
3731
|
+
* 记录当前已成功激活的插件 id,供宿主做只读观测。
|
|
3732
|
+
*/
|
|
3733
|
+
const activatedPluginIds = new Set();
|
|
3734
|
+
function clearActivatedPluginIds() {
|
|
3735
|
+
activatedPluginIds.clear();
|
|
3736
|
+
}
|
|
3737
|
+
function markPluginActivated(pluginId) {
|
|
3738
|
+
if (!pluginId) {
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
activatedPluginIds.add(pluginId);
|
|
3742
|
+
}
|
|
3743
|
+
function markPluginDeactivated(pluginId) {
|
|
3744
|
+
if (!pluginId) {
|
|
3745
|
+
return;
|
|
3746
|
+
}
|
|
3747
|
+
activatedPluginIds.delete(pluginId);
|
|
3748
|
+
}
|
|
3749
|
+
function getActivatedPluginIds$1() {
|
|
3750
|
+
return [...activatedPluginIds];
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3578
3753
|
/**
|
|
3579
3754
|
* 拉取插件清单、加载入口脚本并调用各插件 `activator`。
|
|
3580
3755
|
*/
|
|
@@ -3610,7 +3785,7 @@ async function loadPluginEntry(p, entryUrl, devMap, hostSet) {
|
|
|
3610
3785
|
catch (e) {
|
|
3611
3786
|
console.warn('[wep] dev import failed, trying manifest entryUrl', p.id, e);
|
|
3612
3787
|
if (entryUrl && isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3613
|
-
await loadScript(entryUrl);
|
|
3788
|
+
await loadScript(entryUrl, p.id);
|
|
3614
3789
|
}
|
|
3615
3790
|
return;
|
|
3616
3791
|
}
|
|
@@ -3620,7 +3795,40 @@ async function loadPluginEntry(p, entryUrl, devMap, hostSet) {
|
|
|
3620
3795
|
console.warn('[wep] skip (entryUrl not allowed)', p.id, entryUrl);
|
|
3621
3796
|
return;
|
|
3622
3797
|
}
|
|
3623
|
-
await loadScript(entryUrl);
|
|
3798
|
+
await loadScript(entryUrl, p.id);
|
|
3799
|
+
}
|
|
3800
|
+
function coercePriority(value) {
|
|
3801
|
+
if (value === null || value === undefined) {
|
|
3802
|
+
return null;
|
|
3803
|
+
}
|
|
3804
|
+
const n = Number(value);
|
|
3805
|
+
return Number.isFinite(n) ? n : null;
|
|
3806
|
+
}
|
|
3807
|
+
function sortByPriority(plugins) {
|
|
3808
|
+
return plugins
|
|
3809
|
+
.map((entry, index) => ({
|
|
3810
|
+
entry,
|
|
3811
|
+
priority: coercePriority(entry.priority),
|
|
3812
|
+
index
|
|
3813
|
+
}))
|
|
3814
|
+
.sort((a, b) => {
|
|
3815
|
+
const aHas = a.priority !== null;
|
|
3816
|
+
const bHas = b.priority !== null;
|
|
3817
|
+
if (!aHas && !bHas) {
|
|
3818
|
+
return a.index - b.index;
|
|
3819
|
+
}
|
|
3820
|
+
if (!aHas) {
|
|
3821
|
+
return 1;
|
|
3822
|
+
}
|
|
3823
|
+
if (!bHas) {
|
|
3824
|
+
return -1;
|
|
3825
|
+
}
|
|
3826
|
+
if (a.priority !== b.priority) {
|
|
3827
|
+
return a.priority - b.priority;
|
|
3828
|
+
}
|
|
3829
|
+
return a.index - b.index;
|
|
3830
|
+
})
|
|
3831
|
+
.map((decorated) => decorated.entry);
|
|
3624
3832
|
}
|
|
3625
3833
|
async function bootstrapPlugins$1(
|
|
3626
3834
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -3632,6 +3840,7 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3632
3840
|
return;
|
|
3633
3841
|
}
|
|
3634
3842
|
printRuntimeBannerOnce();
|
|
3843
|
+
clearActivatedPluginIds();
|
|
3635
3844
|
const opts = resolveRuntimeOptions$1(runtimeOptions || {});
|
|
3636
3845
|
setPluginBootstrapRouter(router);
|
|
3637
3846
|
ensurePluginHostRoute$1(router, opts);
|
|
@@ -3710,6 +3919,9 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3710
3919
|
: {}),
|
|
3711
3920
|
...(typeof opts.adaptRouteDeclarations === 'function'
|
|
3712
3921
|
? { adaptRouteDeclarations: opts.adaptRouteDeclarations }
|
|
3922
|
+
: {}),
|
|
3923
|
+
...(typeof opts.onPluginRoutesContributed === 'function'
|
|
3924
|
+
? { onPluginRoutesContributed: opts.onPluginRoutesContributed }
|
|
3713
3925
|
: {})
|
|
3714
3926
|
};
|
|
3715
3927
|
if (!manifestResult.ok) {
|
|
@@ -3738,22 +3950,30 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3738
3950
|
const maj = coerced ? coerced.major : 0;
|
|
3739
3951
|
const range = `^${maj}.0.0`;
|
|
3740
3952
|
if (!semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3741
|
-
console.warn('[wep] host API version mismatch', {
|
|
3953
|
+
console.warn('[wep] manifest host API version mismatch; skip bootstrap', {
|
|
3742
3954
|
host: HOST_PLUGIN_API_VERSION,
|
|
3743
3955
|
manifest: apiVer
|
|
3744
3956
|
});
|
|
3957
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3958
|
+
console.info('[wep] bootstrap_summary', {
|
|
3959
|
+
ok: false,
|
|
3960
|
+
reason: 'manifest_host_api_version_mismatch'
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
return;
|
|
3745
3964
|
}
|
|
3746
3965
|
}
|
|
3747
3966
|
window.__PLUGIN_ACTIVATORS__ = window.__PLUGIN_ACTIVATORS__ || {};
|
|
3748
|
-
const
|
|
3967
|
+
const originalPlugins = data.plugins || [];
|
|
3968
|
+
const plugins = sortByPriority(originalPlugins);
|
|
3749
3969
|
if (plugins.length === 0) {
|
|
3750
3970
|
const hint = isStatic ? 'check static JSON file and plugins[]' : 'check backend and URL';
|
|
3751
3971
|
console.info('[wep] empty plugin manifest — ' + hint, manifestUrlUsed);
|
|
3752
3972
|
}
|
|
3753
3973
|
const summary = {
|
|
3754
|
-
manifestCount:
|
|
3974
|
+
manifestCount: originalPlugins.length,
|
|
3755
3975
|
activated: 0,
|
|
3756
|
-
|
|
3976
|
+
skipApiVersion: 0,
|
|
3757
3977
|
skipLoad: 0,
|
|
3758
3978
|
skipNoActivator: 0,
|
|
3759
3979
|
activateFail: 0
|
|
@@ -3761,8 +3981,10 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3761
3981
|
for (const p of plugins) {
|
|
3762
3982
|
const range = p.engines && p.engines.host;
|
|
3763
3983
|
if (range && !semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3764
|
-
console.warn('[wep] skip plugin (engines.host)', p.id, range
|
|
3765
|
-
|
|
3984
|
+
console.warn('[wep] skip plugin (engines.host)', p.id, range, {
|
|
3985
|
+
host: HOST_PLUGIN_API_VERSION
|
|
3986
|
+
});
|
|
3987
|
+
summary.skipApiVersion++;
|
|
3766
3988
|
continue;
|
|
3767
3989
|
}
|
|
3768
3990
|
const entryUrl = p.entryUrl;
|
|
@@ -3798,6 +4020,11 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3798
4020
|
const hostApi = createHostApiFactory(p.id, router, hostKit);
|
|
3799
4021
|
try {
|
|
3800
4022
|
await Promise.resolve(activator(hostApi, { pluginRecord }));
|
|
4023
|
+
markPluginActivated(p.id);
|
|
4024
|
+
const teardownCapableHostApi = hostApi;
|
|
4025
|
+
if (typeof teardownCapableHostApi.onTeardown === 'function') {
|
|
4026
|
+
teardownCapableHostApi.onTeardown(p.id, () => markPluginDeactivated(p.id));
|
|
4027
|
+
}
|
|
3801
4028
|
summary.activated++;
|
|
3802
4029
|
if (typeof opts.onAfterPluginActivate === 'function') {
|
|
3803
4030
|
await Promise.resolve(opts.onAfterPluginActivate({
|
|
@@ -3810,6 +4037,12 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3810
4037
|
}
|
|
3811
4038
|
catch (e) {
|
|
3812
4039
|
console.error('[wep] activate failed', p.id, e);
|
|
4040
|
+
try {
|
|
4041
|
+
disposeWebPlugin(p.id);
|
|
4042
|
+
}
|
|
4043
|
+
catch (disposeErr) {
|
|
4044
|
+
console.warn('[wep] rollback failed after activation error', p.id, disposeErr);
|
|
4045
|
+
}
|
|
3813
4046
|
summary.activateFail++;
|
|
3814
4047
|
if (typeof opts.onPluginActivateError === 'function') {
|
|
3815
4048
|
try {
|
|
@@ -3840,6 +4073,7 @@ var pluginRuntime = /*#__PURE__*/Object.freeze({
|
|
|
3840
4073
|
bootstrapPlugins: bootstrapPlugins$1,
|
|
3841
4074
|
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
3842
4075
|
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
4076
|
+
getActivatedPluginIds: getActivatedPluginIds$1,
|
|
3843
4077
|
resolveRuntimeOptions: resolveRuntimeOptions$1
|
|
3844
4078
|
});
|
|
3845
4079
|
|
|
@@ -3998,48 +4232,40 @@ var manifestComposer = /*#__PURE__*/Object.freeze({
|
|
|
3998
4232
|
});
|
|
3999
4233
|
|
|
4000
4234
|
/**
|
|
4001
|
-
*
|
|
4002
|
-
* 菜单由宿主从路由 `meta` 推导(见 `buildMenuDescriptorsFromRoutes`),框架不维护平行菜单列表。
|
|
4003
|
-
*/
|
|
4004
|
-
const registries = Vue__default.default.observable({
|
|
4005
|
-
slots: {},
|
|
4006
|
-
slotRevision: 0
|
|
4007
|
-
});
|
|
4008
|
-
|
|
4009
|
-
/**
|
|
4010
|
-
* 按插件 id 收集 `onTeardown` 回调,供 `disposeWebPlugin` 统一执行。
|
|
4235
|
+
* 插件访问后端的受控通道:`fetch` 仅允许落在配置的路径前缀下(默认 `/api/`),默认 `credentials: 'same-origin'`。
|
|
4011
4236
|
*/
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
return;
|
|
4237
|
+
function normalizeBridgePath(input) {
|
|
4238
|
+
if (typeof window === 'undefined') {
|
|
4239
|
+
throw new Error('[wep:bridge] window is unavailable');
|
|
4016
4240
|
}
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
list = [];
|
|
4020
|
-
_byPlugin.set(pluginId, list);
|
|
4241
|
+
if (typeof input !== 'string' || !input.startsWith('/')) {
|
|
4242
|
+
throw new Error('[wep:bridge] path must start with /');
|
|
4021
4243
|
}
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
function runPluginTeardowns(pluginId) {
|
|
4025
|
-
const list = _byPlugin.get(pluginId);
|
|
4026
|
-
if (!list) {
|
|
4027
|
-
return;
|
|
4244
|
+
if (input.includes('\\')) {
|
|
4245
|
+
throw new Error('[wep:bridge] path must not contain backslashes');
|
|
4028
4246
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
4247
|
+
const url = new URL(input, window.location.origin);
|
|
4248
|
+
if (url.origin !== window.location.origin) {
|
|
4249
|
+
throw new Error('[wep:bridge] cross-origin path is not allowed');
|
|
4250
|
+
}
|
|
4251
|
+
const normalized = url.pathname;
|
|
4252
|
+
const decodedPath = (() => {
|
|
4031
4253
|
try {
|
|
4032
|
-
|
|
4254
|
+
return decodeURIComponent(normalized);
|
|
4033
4255
|
}
|
|
4034
|
-
catch
|
|
4035
|
-
|
|
4256
|
+
catch {
|
|
4257
|
+
return normalized;
|
|
4036
4258
|
}
|
|
4037
|
-
}
|
|
4259
|
+
})();
|
|
4260
|
+
const hasTraversalSegment = decodedPath
|
|
4261
|
+
.split('/')
|
|
4262
|
+
.filter(Boolean)
|
|
4263
|
+
.some((segment) => segment === '.' || segment === '..');
|
|
4264
|
+
if (hasTraversalSegment) {
|
|
4265
|
+
throw new Error('[wep:bridge] path traversal is not allowed');
|
|
4266
|
+
}
|
|
4267
|
+
return normalized + url.search;
|
|
4038
4268
|
}
|
|
4039
|
-
|
|
4040
|
-
/**
|
|
4041
|
-
* 插件访问后端的受控通道:`fetch` 仅允许落在配置的路径前缀下(默认 `/api/`),默认 `credentials: 'same-origin'`。
|
|
4042
|
-
*/
|
|
4043
4269
|
function createRequestBridge(config = {}) {
|
|
4044
4270
|
const raw = Array.isArray(config.allowedPathPrefixes) && config.allowedPathPrefixes.length > 0
|
|
4045
4271
|
? config.allowedPathPrefixes
|
|
@@ -4047,14 +4273,13 @@ function createRequestBridge(config = {}) {
|
|
|
4047
4273
|
const allowedPathPrefixes = raw.map((p) => ensureLeadingPath(p));
|
|
4048
4274
|
return {
|
|
4049
4275
|
async request(path, init = {}) {
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
}
|
|
4053
|
-
const allowed = allowedPathPrefixes.some((p) => path.startsWith(p));
|
|
4276
|
+
const normalizedPath = normalizeBridgePath(path);
|
|
4277
|
+
const pathnameOnly = normalizedPath.split('?')[0];
|
|
4278
|
+
const allowed = allowedPathPrefixes.some((p) => pathnameOnly === p || pathnameOnly.startsWith(`${p.replace(/\/$/, '')}/`));
|
|
4054
4279
|
if (!allowed) {
|
|
4055
|
-
throw new Error('[wep:bridge] path not allowed: ' +
|
|
4280
|
+
throw new Error('[wep:bridge] path not allowed: ' + normalizedPath);
|
|
4056
4281
|
}
|
|
4057
|
-
return fetch(
|
|
4282
|
+
return fetch(normalizedPath, {
|
|
4058
4283
|
credentials: 'same-origin',
|
|
4059
4284
|
...init
|
|
4060
4285
|
});
|
|
@@ -4063,51 +4288,83 @@ function createRequestBridge(config = {}) {
|
|
|
4063
4288
|
}
|
|
4064
4289
|
|
|
4065
4290
|
/**
|
|
4066
|
-
*
|
|
4291
|
+
* 按插件记录已贡献路由的可序列化快照,避免宿主依赖 router 内部 matcher 形态。
|
|
4067
4292
|
*/
|
|
4068
|
-
const
|
|
4069
|
-
function
|
|
4070
|
-
if (
|
|
4071
|
-
return;
|
|
4293
|
+
const contributedRoutesByPlugin = new Map();
|
|
4294
|
+
function sanitizeValue(value) {
|
|
4295
|
+
if (value == null) {
|
|
4296
|
+
return value;
|
|
4072
4297
|
}
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
}
|
|
4076
|
-
/**
|
|
4077
|
-
* 从 matcher 移除该插件登记过的顶层路由(含其子树)。
|
|
4078
|
-
* 需 vue-router ≥3.5 的 removeRoute;否则静默跳过。
|
|
4079
|
-
*/
|
|
4080
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4081
|
-
function removeRegisteredRoutesForPlugin(router, pluginId) {
|
|
4082
|
-
const names = pluginIdToRouteNames.get(pluginId);
|
|
4083
|
-
if (!names || names.length === 0) {
|
|
4084
|
-
return;
|
|
4298
|
+
if (typeof value === 'function') {
|
|
4299
|
+
return undefined;
|
|
4085
4300
|
}
|
|
4086
|
-
if (
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4301
|
+
if (Array.isArray(value)) {
|
|
4302
|
+
return value
|
|
4303
|
+
.map((item) => sanitizeValue(item))
|
|
4304
|
+
.filter((item) => item !== undefined);
|
|
4090
4305
|
}
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4306
|
+
if (typeof value !== 'object') {
|
|
4307
|
+
return value;
|
|
4308
|
+
}
|
|
4309
|
+
const out = {};
|
|
4310
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
4311
|
+
if (key === 'component' || key === 'components') {
|
|
4312
|
+
continue;
|
|
4094
4313
|
}
|
|
4095
|
-
|
|
4096
|
-
|
|
4314
|
+
const sanitized = sanitizeValue(raw);
|
|
4315
|
+
if (sanitized !== undefined) {
|
|
4316
|
+
out[key] = sanitized;
|
|
4097
4317
|
}
|
|
4098
4318
|
}
|
|
4099
|
-
|
|
4319
|
+
return out;
|
|
4100
4320
|
}
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4321
|
+
function sanitizeContributedRoutes(routes) {
|
|
4322
|
+
return routes
|
|
4323
|
+
.map((route) => sanitizeValue(route))
|
|
4324
|
+
.filter((route) => !!route && typeof route === 'object');
|
|
4325
|
+
}
|
|
4326
|
+
function recordContributedRoutesForPlugin(pluginId, routes) {
|
|
4327
|
+
const sanitized = sanitizeContributedRoutes(routes);
|
|
4328
|
+
const current = contributedRoutesByPlugin.get(pluginId) || [];
|
|
4329
|
+
const next = current.concat(sanitized);
|
|
4330
|
+
contributedRoutesByPlugin.set(pluginId, next);
|
|
4331
|
+
return next.map((route) => sanitizeValue(route));
|
|
4332
|
+
}
|
|
4333
|
+
function getContributedRoutesForPlugin(pluginId) {
|
|
4334
|
+
const routes = contributedRoutesByPlugin.get(pluginId) || [];
|
|
4335
|
+
return routes.map((route) => sanitizeValue(route));
|
|
4336
|
+
}
|
|
4337
|
+
function clearContributedRoutesForPlugin(pluginId) {
|
|
4338
|
+
contributedRoutesByPlugin.delete(pluginId);
|
|
4104
4339
|
}
|
|
4105
4340
|
|
|
4106
4341
|
/**
|
|
4107
|
-
* 构造插件 `activator(hostApi)` 使用的宿主 API
|
|
4342
|
+
* 构造插件 `activator(hostApi)` 使用的宿主 API:路由、扩展点、资源与受控请求桥。
|
|
4108
4343
|
*/
|
|
4109
4344
|
let slotItemKeySeq = 0;
|
|
4110
4345
|
let routeSynthSeq = 0;
|
|
4346
|
+
function decorateRouteTreeWithPluginMeta(pluginId, route) {
|
|
4347
|
+
const meta = route.meta && typeof route.meta === 'object' && !Array.isArray(route.meta)
|
|
4348
|
+
? route.meta
|
|
4349
|
+
: {};
|
|
4350
|
+
const wepMeta = meta.__wep && typeof meta.__wep === 'object' && !Array.isArray(meta.__wep)
|
|
4351
|
+
? meta.__wep
|
|
4352
|
+
: {};
|
|
4353
|
+
const nextChildren = Array.isArray(route.children)
|
|
4354
|
+
? route.children.map((child) => decorateRouteTreeWithPluginMeta(pluginId, child))
|
|
4355
|
+
: route.children;
|
|
4356
|
+
return {
|
|
4357
|
+
...route,
|
|
4358
|
+
meta: {
|
|
4359
|
+
...meta,
|
|
4360
|
+
__wep: {
|
|
4361
|
+
...wepMeta,
|
|
4362
|
+
pluginId
|
|
4363
|
+
}
|
|
4364
|
+
},
|
|
4365
|
+
...(nextChildren ? { children: nextChildren } : {})
|
|
4366
|
+
};
|
|
4367
|
+
}
|
|
4111
4368
|
function analyzeRouteInputTree(nodes) {
|
|
4112
4369
|
let hasDecl = false;
|
|
4113
4370
|
let hasCfg = false;
|
|
@@ -4149,26 +4406,71 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4149
4406
|
const parentName = typeof hostKitOptions.pluginRoutesParentName === 'string'
|
|
4150
4407
|
? hostKitOptions.pluginRoutesParentName.trim()
|
|
4151
4408
|
: '';
|
|
4409
|
+
function rollbackRegisteredTopRoutes(routes) {
|
|
4410
|
+
for (let i = routes.length - 1; i >= 0; i--) {
|
|
4411
|
+
const route = routes[i];
|
|
4412
|
+
if (typeof route.dispose === 'function') {
|
|
4413
|
+
try {
|
|
4414
|
+
route.dispose();
|
|
4415
|
+
continue;
|
|
4416
|
+
}
|
|
4417
|
+
catch (e) {
|
|
4418
|
+
console.warn('[wep] rollback route disposer failed', route.name, e);
|
|
4419
|
+
}
|
|
4420
|
+
}
|
|
4421
|
+
if (typeof router.removeRoute === 'function') {
|
|
4422
|
+
try {
|
|
4423
|
+
router.removeRoute(route.name);
|
|
4424
|
+
}
|
|
4425
|
+
catch (e) {
|
|
4426
|
+
console.warn('[wep] rollback removeRoute failed', route.name, e);
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4152
4431
|
function applyInternalRegister(rawRouteConfigs) {
|
|
4153
4432
|
const wrapped = rawRouteConfigs.map((r) => ({
|
|
4154
|
-
...r,
|
|
4155
|
-
name: r.name || `${routeSynthNamePrefix}${pluginId}_${routeSynthSeq++}
|
|
4156
|
-
meta: { ...(r.meta || {}), pluginId }
|
|
4433
|
+
...decorateRouteTreeWithPluginMeta(pluginId, r),
|
|
4434
|
+
name: r.name || `${routeSynthNamePrefix}${pluginId}_${routeSynthSeq++}`
|
|
4157
4435
|
}));
|
|
4158
4436
|
if (typeof router.addRoute !== 'function') {
|
|
4159
4437
|
throw new Error('[wep] vue-router 3.5+ 必需:请使用 router.addRoute(不再支持 addRoutes)');
|
|
4160
4438
|
}
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4439
|
+
const registeredTopRoutes = [];
|
|
4440
|
+
try {
|
|
4441
|
+
if (parentName) {
|
|
4442
|
+
for (const r of wrapped) {
|
|
4443
|
+
const dispose = router.addRoute(parentName, r);
|
|
4444
|
+
registeredTopRoutes.push({
|
|
4445
|
+
name: String(r.name),
|
|
4446
|
+
dispose: typeof dispose === 'function' ? dispose : undefined
|
|
4447
|
+
});
|
|
4448
|
+
}
|
|
4165
4449
|
}
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4450
|
+
else {
|
|
4451
|
+
for (const r of wrapped) {
|
|
4452
|
+
const dispose = router.addRoute(r);
|
|
4453
|
+
registeredTopRoutes.push({
|
|
4454
|
+
name: String(r.name),
|
|
4455
|
+
dispose: typeof dispose === 'function' ? dispose : undefined
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4170
4458
|
}
|
|
4171
4459
|
}
|
|
4460
|
+
catch (e) {
|
|
4461
|
+
rollbackRegisteredTopRoutes(registeredTopRoutes);
|
|
4462
|
+
throw e;
|
|
4463
|
+
}
|
|
4464
|
+
recordPluginTopRoutes(pluginId, registeredTopRoutes);
|
|
4465
|
+
const contributedRoutes = recordContributedRoutesForPlugin(pluginId, wrapped);
|
|
4466
|
+
if (contributedRoutes.length > 0 && typeof hostKitOptions.onPluginRoutesContributed === 'function') {
|
|
4467
|
+
hostKitOptions.onPluginRoutesContributed({
|
|
4468
|
+
pluginId,
|
|
4469
|
+
router,
|
|
4470
|
+
routes: wrapped,
|
|
4471
|
+
contributedRoutes
|
|
4472
|
+
});
|
|
4473
|
+
}
|
|
4172
4474
|
}
|
|
4173
4475
|
function injectStylesheet(href) {
|
|
4174
4476
|
const link = document.createElement('link');
|
|
@@ -4191,6 +4493,12 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4191
4493
|
const hostContext = hostKitOptions.hostContext != null && typeof hostKitOptions.hostContext === 'object'
|
|
4192
4494
|
? hostKitOptions.hostContext
|
|
4193
4495
|
: Object.freeze({});
|
|
4496
|
+
const hostVue = hostContext && hostContext.Vue;
|
|
4497
|
+
const VueRuntime = hostVue || Vue__default.default;
|
|
4498
|
+
ensureRegistriesReactive(VueRuntime);
|
|
4499
|
+
registerPluginTeardown(pluginId, () => {
|
|
4500
|
+
clearContributedRoutesForPlugin(pluginId);
|
|
4501
|
+
});
|
|
4194
4502
|
return {
|
|
4195
4503
|
hostPluginApiVersion: HOST_PLUGIN_API_VERSION,
|
|
4196
4504
|
/** 宿主注入的只读依赖(store、router、开放能力等),见 `resolveRuntimeOptions.hostContext` */
|
|
@@ -4243,7 +4551,7 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4243
4551
|
return;
|
|
4244
4552
|
}
|
|
4245
4553
|
if (!registries.slots[pointId]) {
|
|
4246
|
-
|
|
4554
|
+
VueRuntime.set(registries.slots, pointId, []);
|
|
4247
4555
|
}
|
|
4248
4556
|
const list = registries.slots[pointId];
|
|
4249
4557
|
for (const c of components) {
|
|
@@ -4271,6 +4579,7 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4271
4579
|
registerSanitizedHtmlSnippet() {
|
|
4272
4580
|
throw new Error('registerSanitizedHtmlSnippet is not enabled');
|
|
4273
4581
|
},
|
|
4582
|
+
getContributedRoutes: () => getContributedRoutesForPlugin(pluginId),
|
|
4274
4583
|
getBridge: () => bridge,
|
|
4275
4584
|
onTeardown(_pluginId, fn) {
|
|
4276
4585
|
if (typeof fn === 'function') {
|
|
@@ -4280,189 +4589,69 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4280
4589
|
};
|
|
4281
4590
|
}
|
|
4282
4591
|
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
Vue__default.default.delete(slots, pointId);
|
|
4302
|
-
}
|
|
4303
|
-
else if (next.length !== list.length) {
|
|
4304
|
-
Vue__default.default.set(slots, pointId, next);
|
|
4305
|
-
}
|
|
4306
|
-
}
|
|
4307
|
-
registries.slotRevision++;
|
|
4308
|
-
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
4309
|
-
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
4310
|
-
}
|
|
4311
|
-
if (typeof document !== 'undefined') {
|
|
4312
|
-
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
4313
|
-
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
4314
|
-
el.remove();
|
|
4592
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4593
|
+
function createExtensionPointComponent(VueLike = Vue__default.default) {
|
|
4594
|
+
ensureRegistriesReactive(VueLike);
|
|
4595
|
+
const SlotErrorBoundary = VueLike.extend({
|
|
4596
|
+
name: 'SlotErrorBoundary',
|
|
4597
|
+
props: { label: String },
|
|
4598
|
+
data() {
|
|
4599
|
+
return { error: null };
|
|
4600
|
+
},
|
|
4601
|
+
errorCaptured(err) {
|
|
4602
|
+
this.error = err instanceof Error && err.message ? err.message : String(err);
|
|
4603
|
+
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4604
|
+
return false;
|
|
4605
|
+
},
|
|
4606
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4607
|
+
render(h) {
|
|
4608
|
+
if (this.error) {
|
|
4609
|
+
return h('div', { class: 'plugin-point-error' }, `[插件 ${this.label}] 渲染失败`);
|
|
4315
4610
|
}
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
}
|
|
4319
|
-
|
|
4320
|
-
/**
|
|
4321
|
-
* 从路由配置(含 meta)推导侧栏/目录用菜单描述,与 registerRoutes 入参同源。
|
|
4322
|
-
*
|
|
4323
|
-
* 约定:
|
|
4324
|
-
* - `meta.menu === false`:该节点及其子树不参与菜单
|
|
4325
|
-
* - 节点出现条件:`meta.title` 或 `meta.menuTitle` 非空字符串,或存在非空的子菜单列表
|
|
4326
|
-
* - `meta.order` 数字越小越靠前;缺省为 0
|
|
4327
|
-
* - 可选:`meta.icon`、`meta.permission`、`meta.hidden`、`meta.external`
|
|
4328
|
-
*/
|
|
4329
|
-
function pickTitle(meta, route) {
|
|
4330
|
-
if (typeof meta.menuTitle === 'string' && meta.menuTitle) {
|
|
4331
|
-
return meta.menuTitle;
|
|
4332
|
-
}
|
|
4333
|
-
if (typeof meta.title === 'string' && meta.title) {
|
|
4334
|
-
return meta.title;
|
|
4335
|
-
}
|
|
4336
|
-
if (route.name != null && String(route.name)) {
|
|
4337
|
-
return String(route.name);
|
|
4338
|
-
}
|
|
4339
|
-
return String(route.path || '');
|
|
4340
|
-
}
|
|
4341
|
-
function sortDescriptors(items) {
|
|
4342
|
-
items.sort((a, b) => a.order - b.order || a.path.localeCompare(b.path));
|
|
4343
|
-
for (const x of items) {
|
|
4344
|
-
if (x.children && x.children.length) {
|
|
4345
|
-
sortDescriptors(x.children);
|
|
4346
|
-
}
|
|
4347
|
-
}
|
|
4348
|
-
}
|
|
4349
|
-
/**
|
|
4350
|
-
* 从插件侧 RouteConfig 树构建菜单描述(不含 component,便于并入宿主菜单 state)。
|
|
4351
|
-
*/
|
|
4352
|
-
function buildMenuDescriptorsFromRoutes(routes, pluginId) {
|
|
4353
|
-
const out = [];
|
|
4354
|
-
function walk(route) {
|
|
4355
|
-
const meta = (route.meta || {});
|
|
4356
|
-
if (meta.menu === false) {
|
|
4357
|
-
return null;
|
|
4358
|
-
}
|
|
4359
|
-
const chIn = Array.isArray(route.children) ? route.children : [];
|
|
4360
|
-
const childMenus = chIn.map(walk).filter(Boolean);
|
|
4361
|
-
const hasTitle = typeof meta.title === 'string' || typeof meta.menuTitle === 'string';
|
|
4362
|
-
if (!hasTitle && childMenus.length === 0) {
|
|
4363
|
-
return null;
|
|
4364
|
-
}
|
|
4365
|
-
const item = {
|
|
4366
|
-
path: String(route.path || ''),
|
|
4367
|
-
name: route.name,
|
|
4368
|
-
title: pickTitle(meta, route),
|
|
4369
|
-
order: meta.order != null ? Number(meta.order) : 0,
|
|
4370
|
-
...(pluginId ? { pluginId } : {}),
|
|
4371
|
-
...(meta.icon !== undefined ? { icon: meta.icon } : {}),
|
|
4372
|
-
...(meta.permission !== undefined ? { permission: meta.permission } : {}),
|
|
4373
|
-
...(meta.hidden === true ? { hidden: true } : {}),
|
|
4374
|
-
...(meta.external === true ? { external: true } : {}),
|
|
4375
|
-
...(childMenus.length ? { children: childMenus } : {})
|
|
4376
|
-
};
|
|
4377
|
-
return item;
|
|
4378
|
-
}
|
|
4379
|
-
for (const r of routes) {
|
|
4380
|
-
const m = walk(r);
|
|
4381
|
-
if (m) {
|
|
4382
|
-
out.push(m);
|
|
4611
|
+
const d = this.$slots.default;
|
|
4612
|
+
return d && d[0] ? d[0] : h('span');
|
|
4383
4613
|
}
|
|
4384
|
-
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
* 样式由宿主全局 CSS 覆盖 `.extension-point` / `.plugin-point-error`。
|
|
4392
|
-
*/
|
|
4393
|
-
const SlotErrorBoundary = Vue__default.default.extend({
|
|
4394
|
-
name: 'SlotErrorBoundary',
|
|
4395
|
-
props: { label: String },
|
|
4396
|
-
data() {
|
|
4397
|
-
return { error: null };
|
|
4398
|
-
},
|
|
4399
|
-
errorCaptured(err) {
|
|
4400
|
-
this.error = err instanceof Error && err.message ? err.message : String(err);
|
|
4401
|
-
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4402
|
-
return false;
|
|
4403
|
-
},
|
|
4404
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4405
|
-
render(h) {
|
|
4406
|
-
if (this.error) {
|
|
4407
|
-
return h('div', { class: 'plugin-point-error' }, `[插件 ${this.label}] 渲染失败`);
|
|
4408
|
-
}
|
|
4409
|
-
const d = this.$slots.default;
|
|
4410
|
-
return d && d[0] ? d[0] : h('span');
|
|
4411
|
-
}
|
|
4412
|
-
});
|
|
4413
|
-
var ExtensionPoint = Vue__default.default.extend({
|
|
4414
|
-
name: 'ExtensionPoint',
|
|
4415
|
-
components: { SlotErrorBoundary },
|
|
4416
|
-
props: {
|
|
4417
|
-
pointId: { type: String, required: true },
|
|
4418
|
-
slotProps: { type: Object, default: () => ({}) }
|
|
4419
|
-
},
|
|
4420
|
-
computed: {
|
|
4421
|
-
items() {
|
|
4422
|
-
void registries.slotRevision;
|
|
4423
|
-
return registries.slots[this.pointId] || [];
|
|
4614
|
+
});
|
|
4615
|
+
return VueLike.extend({
|
|
4616
|
+
name: 'ExtensionPoint',
|
|
4617
|
+
components: { SlotErrorBoundary },
|
|
4618
|
+
props: {
|
|
4619
|
+
pointId: { type: String, required: true },
|
|
4620
|
+
slotProps: { type: Object, default: () => ({}) }
|
|
4424
4621
|
},
|
|
4425
|
-
|
|
4426
|
-
|
|
4622
|
+
computed: {
|
|
4623
|
+
items() {
|
|
4624
|
+
void registries.slotRevision;
|
|
4625
|
+
return registries.slots[this.pointId] || [];
|
|
4626
|
+
},
|
|
4627
|
+
forwardProps() {
|
|
4628
|
+
return this.slotProps || {};
|
|
4629
|
+
}
|
|
4630
|
+
},
|
|
4631
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4632
|
+
render(h) {
|
|
4633
|
+
return h('div', {
|
|
4634
|
+
class: 'extension-point',
|
|
4635
|
+
attrs: { 'data-point-id': this.pointId }
|
|
4636
|
+
}, this.items.map((item) => h(SlotErrorBoundary, {
|
|
4637
|
+
key: item.key,
|
|
4638
|
+
props: { label: item.pluginId }
|
|
4639
|
+
}, [h(item.component, { props: this.forwardProps })])));
|
|
4427
4640
|
}
|
|
4428
|
-
}
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
return h('div', {
|
|
4432
|
-
class: 'extension-point',
|
|
4433
|
-
attrs: { 'data-point-id': this.pointId }
|
|
4434
|
-
}, this.items.map((item) => h(SlotErrorBoundary, {
|
|
4435
|
-
key: item.key,
|
|
4436
|
-
props: { label: item.pluginId }
|
|
4437
|
-
}, [h(item.component, { props: this.forwardProps })])));
|
|
4438
|
-
}
|
|
4439
|
-
});
|
|
4641
|
+
});
|
|
4642
|
+
}
|
|
4643
|
+
var ExtensionPoint = createExtensionPointComponent();
|
|
4440
4644
|
|
|
4441
|
-
/**
|
|
4442
|
-
* 注册全局 `ExtensionPoint` 并异步拉取清单、激活插件。
|
|
4443
|
-
*/
|
|
4444
|
-
/**
|
|
4445
|
-
* @param Vue Vue 构造函数
|
|
4446
|
-
* @param router vue-router 实例
|
|
4447
|
-
* @param options 传给 `resolveRuntimeOptions`;可含 `env` 以读取 `VITE_*`
|
|
4448
|
-
*/
|
|
4449
4645
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4450
4646
|
function installWebExtendPluginVue2(Vue, router, options) {
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
setWebExtendPluginEnv(injectedEnv);
|
|
4647
|
+
if (Vue) {
|
|
4648
|
+
ensureRegistriesReactive(Vue);
|
|
4649
|
+
Vue.component('ExtensionPoint', createExtensionPointComponent(Vue));
|
|
4455
4650
|
}
|
|
4456
|
-
|
|
4457
|
-
Vue.component('ExtensionPoint', ExtensionPoint);
|
|
4458
|
-
}
|
|
4459
|
-
const runtime = resolveRuntimeOptions$1(runtimeUser);
|
|
4651
|
+
const runtime = resolveRuntimeOptions$1(options || {});
|
|
4460
4652
|
return bootstrapPlugins$1(router, (id, r, kit) => createHostApi(id, r, kit || {}), runtime);
|
|
4461
4653
|
}
|
|
4462
4654
|
|
|
4463
|
-
/**
|
|
4464
|
-
* Vue CLI + 统一 axios(如 RuoYi `utils/request`)场景的 `install` 预设。
|
|
4465
|
-
*/
|
|
4466
4655
|
function resolveManifestPathUnderApiBase(manifestUrl, apiBase) {
|
|
4467
4656
|
const base = String(apiBase !== undefined
|
|
4468
4657
|
? apiBase
|
|
@@ -4472,18 +4661,13 @@ function resolveManifestPathUnderApiBase(manifestUrl, apiBase) {
|
|
|
4472
4661
|
if (typeof window === 'undefined') {
|
|
4473
4662
|
return '/api/frontend-plugins';
|
|
4474
4663
|
}
|
|
4475
|
-
const
|
|
4476
|
-
let path =
|
|
4664
|
+
const url = new URL(manifestUrl, window.location.origin);
|
|
4665
|
+
let path = url.pathname + url.search;
|
|
4477
4666
|
if (base && path.startsWith(base)) {
|
|
4478
4667
|
path = path.slice(base.length) || '/';
|
|
4479
4668
|
}
|
|
4480
4669
|
return path;
|
|
4481
4670
|
}
|
|
4482
|
-
/**
|
|
4483
|
-
* 常见 Java 清单路径:与 `VUE_APP_BASE_API` 拼接为 `${base}/frontend-plugins`。
|
|
4484
|
-
* 若后端使用 `/api/frontend-plugins` 段,请在 extra 中显式传入 `manifestListPath`。
|
|
4485
|
-
*/
|
|
4486
|
-
const defaultVueCliJavaManifestListPath = '/frontend-plugins';
|
|
4487
4671
|
function bridgePrefixesFromVueCliEnv() {
|
|
4488
4672
|
const base = (typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4489
4673
|
? String(process.env.VUE_APP_BASE_API)
|
|
@@ -4504,13 +4688,10 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
|
4504
4688
|
? String(extra.manifestBase).replace(/\/$/, '')
|
|
4505
4689
|
: '';
|
|
4506
4690
|
const stripBase = userBase || envBase;
|
|
4507
|
-
const manifestMode = resolveManifestModeFromInputs(extraManifestMode);
|
|
4508
|
-
const staticManifestUrl = resolveStaticManifestUrlFromInputs(extraStaticManifestUrl);
|
|
4509
4691
|
const fetchManifestApi = async (ctx) => {
|
|
4510
4692
|
try {
|
|
4511
|
-
const url = resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase);
|
|
4512
4693
|
const body = await request({
|
|
4513
|
-
url,
|
|
4694
|
+
url: resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase),
|
|
4514
4695
|
method: 'get'
|
|
4515
4696
|
});
|
|
4516
4697
|
const data = unwrapNestedManifestBody(body);
|
|
@@ -4523,17 +4704,18 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
|
4523
4704
|
}
|
|
4524
4705
|
return { ok: true, data };
|
|
4525
4706
|
}
|
|
4526
|
-
catch (
|
|
4527
|
-
return { ok: false, error
|
|
4707
|
+
catch (error) {
|
|
4708
|
+
return { ok: false, error, data: null };
|
|
4528
4709
|
}
|
|
4529
4710
|
};
|
|
4530
|
-
const
|
|
4711
|
+
const manifestMode = resolveManifestModeFromInputs(extraManifestMode);
|
|
4712
|
+
const staticManifestUrl = resolveStaticManifestUrlFromInputs(extraStaticManifestUrl);
|
|
4531
4713
|
const fetchManifest = typeof userFetchManifest === 'function'
|
|
4532
4714
|
? userFetchManifest
|
|
4533
4715
|
: manifestMode === 'static'
|
|
4534
|
-
?
|
|
4716
|
+
? fetchStaticManifestViaHttp
|
|
4535
4717
|
: fetchManifestApi;
|
|
4536
|
-
const
|
|
4718
|
+
const options = {
|
|
4537
4719
|
manifestBase: stripBase || undefined,
|
|
4538
4720
|
bridgeAllowedPathPrefixes: bridgePrefixesFromVueCliEnv(),
|
|
4539
4721
|
manifestMode,
|
|
@@ -4544,137 +4726,39 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
|
4544
4726
|
const listPath = typeof process !== 'undefined' &&
|
|
4545
4727
|
process.env &&
|
|
4546
4728
|
process.env[webExtendPluginEnvKeys.manifestPathAlt];
|
|
4547
|
-
if (listPath &&
|
|
4548
|
-
|
|
4729
|
+
if (listPath && options.manifestListPath === undefined && extra.manifestListPath === undefined) {
|
|
4730
|
+
options.manifestListPath = String(process.env[webExtendPluginEnvKeys.manifestPathAlt]);
|
|
4549
4731
|
}
|
|
4550
|
-
return
|
|
4551
|
-
}
|
|
4552
|
-
/**
|
|
4553
|
-
* 少样板接入:`hostContext` 自动含 `router`(及可选 `store`)、`isDev` 与常见 `manifestListPath` 默认值。
|
|
4554
|
-
* 更多运行时字段仍可通过 `extra` 覆盖。
|
|
4555
|
-
*/
|
|
4556
|
-
function createVueCliAxiosQuickInstallOptions(
|
|
4557
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4558
|
-
router, deps, extra = {}) {
|
|
4559
|
-
const { request, hostLayoutComponent, store, hostContext: depHostContext } = deps;
|
|
4560
|
-
const { hostContext: extraHostContext, isDev: extraIsDev, manifestListPath: extraListPath, ...restExtra } = extra;
|
|
4561
|
-
const hostContext = {
|
|
4562
|
-
router,
|
|
4563
|
-
...(store !== undefined ? { store } : {}),
|
|
4564
|
-
...(depHostContext && typeof depHostContext === 'object' ? depHostContext : {}),
|
|
4565
|
-
...(extraHostContext && typeof extraHostContext === 'object' ? extraHostContext : {})
|
|
4566
|
-
};
|
|
4567
|
-
const manifestListPath = extraListPath !== undefined && String(extraListPath).trim() !== ''
|
|
4568
|
-
? String(extraListPath)
|
|
4569
|
-
: defaultVueCliJavaManifestListPath;
|
|
4570
|
-
return createVueCliAxiosInstallOptions({ request }, {
|
|
4571
|
-
hostLayoutComponent,
|
|
4572
|
-
hostContext,
|
|
4573
|
-
isDev: extraIsDev !== undefined ? Boolean(extraIsDev) : resolveBundledIsDev(),
|
|
4574
|
-
manifestListPath,
|
|
4575
|
-
...restExtra
|
|
4576
|
-
});
|
|
4732
|
+
return options;
|
|
4577
4733
|
}
|
|
4578
|
-
const presetVueCliAxios = Object.freeze({
|
|
4579
|
-
id: 'vue-cli-axios',
|
|
4580
|
-
description: 'Vue CLI + axios request for API manifest; optional manifestMode=static uses fetch',
|
|
4581
|
-
createInstallOptions: createVueCliAxiosInstallOptions,
|
|
4582
|
-
createQuickInstallOptions: createVueCliAxiosQuickInstallOptions,
|
|
4583
|
-
defaultJavaManifestListPath: defaultVueCliJavaManifestListPath,
|
|
4584
|
-
manifestPathForApiBase: resolveManifestPathUnderApiBase,
|
|
4585
|
-
unwrapManifestBody: unwrapNestedManifestBody
|
|
4586
|
-
});
|
|
4587
4734
|
|
|
4588
|
-
|
|
4589
|
-
* Vue CLI + axios 场景的一键安装:合并 hostContext、默认清单路径与 IIFE 全局 Vue。
|
|
4590
|
-
*/
|
|
4591
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4592
|
-
function installVueCliAxiosWebPlugins(Vue, router, deps, extra) {
|
|
4593
|
-
/** 避免输出 `?.`:Vue CLI 4 / Webpack 4 默认不转译 node_modules */
|
|
4594
|
-
const exposeGlobalVue = extra == null || extra.exposeGlobalVue !== false;
|
|
4595
|
-
if (exposeGlobalVue && typeof window !== 'undefined') {
|
|
4596
|
-
window.Vue = Vue;
|
|
4597
|
-
}
|
|
4598
|
-
const { exposeGlobalVue: _skip, ...restExtra } = extra || {};
|
|
4599
|
-
const opts = createVueCliAxiosQuickInstallOptions(router, deps, restExtra);
|
|
4600
|
-
return installWebExtendPluginVue2(Vue, router, opts);
|
|
4601
|
-
}
|
|
4602
|
-
|
|
4603
|
-
/**
|
|
4604
|
-
* 包对外稳定导出与 `WebExtendPluginVue2` 命名空间。
|
|
4605
|
-
*/
|
|
4606
|
-
const { bootstrapPlugins, defaultFetchWebPluginManifest, resolveRuntimeOptions, ensurePluginHostRoute } = pluginRuntime;
|
|
4735
|
+
const { bootstrapPlugins, defaultFetchWebPluginManifest, resolveRuntimeOptions, ensurePluginHostRoute, getActivatedPluginIds } = pluginRuntime;
|
|
4607
4736
|
const { composeManifestFetch, manifestFetchCacheMiddleware, wrapManifestFetchWithCache } = manifestComposer;
|
|
4608
|
-
const WebExtendPluginVue2 = Object.freeze({
|
|
4609
|
-
install: installWebExtendPluginVue2,
|
|
4610
|
-
runtime: Object.freeze({
|
|
4611
|
-
bootstrapPlugins: bootstrapPlugins$1,
|
|
4612
|
-
resolveRuntimeOptions: resolveRuntimeOptions$1,
|
|
4613
|
-
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
4614
|
-
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
4615
|
-
composeManifestFetch: composeManifestFetch$1,
|
|
4616
|
-
manifestFetchCacheMiddleware: manifestFetchCacheMiddleware$1,
|
|
4617
|
-
wrapManifestFetchWithCache: wrapManifestFetchWithCache$1
|
|
4618
|
-
}),
|
|
4619
|
-
host: Object.freeze({
|
|
4620
|
-
createHostApi,
|
|
4621
|
-
disposeWebPlugin,
|
|
4622
|
-
createRequestBridge,
|
|
4623
|
-
registries,
|
|
4624
|
-
buildMenuDescriptorsFromRoutes,
|
|
4625
|
-
getRegisteredTopRouteNamesForPlugin
|
|
4626
|
-
}),
|
|
4627
|
-
config: Object.freeze({
|
|
4628
|
-
defaultWebExtendPluginRuntime,
|
|
4629
|
-
setWebExtendPluginEnv,
|
|
4630
|
-
webExtendPluginEnvKeys,
|
|
4631
|
-
defaultManifestFetchCache,
|
|
4632
|
-
defaultManifestMode,
|
|
4633
|
-
routeSynthNamePrefix,
|
|
4634
|
-
peerMinimumVersions
|
|
4635
|
-
}),
|
|
4636
|
-
constants: Object.freeze({
|
|
4637
|
-
HOST_PLUGIN_API_VERSION,
|
|
4638
|
-
RUNTIME_CONSOLE_LABEL
|
|
4639
|
-
}),
|
|
4640
|
-
components: Object.freeze({
|
|
4641
|
-
ExtensionPoint
|
|
4642
|
-
}),
|
|
4643
|
-
presets: Object.freeze({
|
|
4644
|
-
vueCliAxios: presetVueCliAxios
|
|
4645
|
-
})
|
|
4646
|
-
});
|
|
4647
4737
|
|
|
4648
4738
|
exports.ExtensionPoint = ExtensionPoint;
|
|
4649
4739
|
exports.HOST_PLUGIN_API_VERSION = HOST_PLUGIN_API_VERSION;
|
|
4650
4740
|
exports.RUNTIME_CONSOLE_LABEL = RUNTIME_CONSOLE_LABEL;
|
|
4651
|
-
exports.WebExtendPluginVue2 = WebExtendPluginVue2;
|
|
4652
4741
|
exports.bootstrapPlugins = bootstrapPlugins;
|
|
4653
|
-
exports.buildMenuDescriptorsFromRoutes = buildMenuDescriptorsFromRoutes;
|
|
4654
4742
|
exports.composeManifestFetch = composeManifestFetch;
|
|
4655
4743
|
exports.createHostApi = createHostApi;
|
|
4656
4744
|
exports.createRequestBridge = createRequestBridge;
|
|
4657
4745
|
exports.createVueCliAxiosInstallOptions = createVueCliAxiosInstallOptions;
|
|
4658
|
-
exports.createVueCliAxiosQuickInstallOptions = createVueCliAxiosQuickInstallOptions;
|
|
4659
4746
|
exports.defaultFetchWebPluginManifest = defaultFetchWebPluginManifest;
|
|
4660
4747
|
exports.defaultManifestFetchCache = defaultManifestFetchCache;
|
|
4661
4748
|
exports.defaultManifestMode = defaultManifestMode;
|
|
4662
|
-
exports.defaultVueCliJavaManifestListPath = defaultVueCliJavaManifestListPath;
|
|
4663
4749
|
exports.defaultWebExtendPluginRuntime = defaultWebExtendPluginRuntime;
|
|
4664
4750
|
exports.disposeWebPlugin = disposeWebPlugin;
|
|
4665
4751
|
exports.ensurePluginHostRoute = ensurePluginHostRoute;
|
|
4752
|
+
exports.getActivatedPluginIds = getActivatedPluginIds;
|
|
4753
|
+
exports.getContributedRoutesForPlugin = getContributedRoutesForPlugin;
|
|
4666
4754
|
exports.getRegisteredTopRouteNamesForPlugin = getRegisteredTopRouteNamesForPlugin;
|
|
4667
|
-
exports.installVueCliAxiosWebPlugins = installVueCliAxiosWebPlugins;
|
|
4668
4755
|
exports.installWebExtendPluginVue2 = installWebExtendPluginVue2;
|
|
4669
4756
|
exports.manifestFetchCacheMiddleware = manifestFetchCacheMiddleware;
|
|
4670
4757
|
exports.peerMinimumVersions = peerMinimumVersions;
|
|
4671
|
-
exports.presetVueCliAxios = presetVueCliAxios;
|
|
4672
4758
|
exports.registries = registries;
|
|
4673
|
-
exports.resolveManifestPathUnderApiBase = resolveManifestPathUnderApiBase;
|
|
4674
4759
|
exports.resolveRuntimeOptions = resolveRuntimeOptions;
|
|
4675
4760
|
exports.routeSynthNamePrefix = routeSynthNamePrefix;
|
|
4676
4761
|
exports.setWebExtendPluginEnv = setWebExtendPluginEnv;
|
|
4677
|
-
exports.unwrapNestedManifestBody = unwrapNestedManifestBody;
|
|
4678
4762
|
exports.webExtendPluginEnvKeys = webExtendPluginEnvKeys;
|
|
4679
4763
|
exports.wrapManifestFetchWithCache = wrapManifestFetchWithCache;
|
|
4680
4764
|
//# sourceMappingURL=index.cjs.map
|