web-extend-plugin-vue2 0.2.5 → 0.3.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/dist/index.mjs CHANGED
@@ -338,15 +338,9 @@ function resolveRuntimeOptions$1(user = {}) {
338
338
  return String(DEF.devFallbackStaticManifestUrl).trim();
339
339
  })();
340
340
  const hostLayoutComponent = user.hostLayoutComponent;
341
- const pluginRoutesParentName = (() => {
342
- if (user.pluginRoutesParentName !== undefined) {
343
- return String(user.pluginRoutesParentName).trim();
344
- }
345
- if (hostLayoutComponent != null) {
346
- return String(DEF.pluginRoutesParentName).trim();
347
- }
348
- return '';
349
- })();
341
+ const pluginRoutesParentName = user.pluginRoutesParentName !== undefined && String(user.pluginRoutesParentName).trim() !== ''
342
+ ? String(user.pluginRoutesParentName).trim()
343
+ : '';
350
344
  const pluginMountRaw = user.pluginMountPath !== undefined && String(user.pluginMountPath).trim() !== ''
351
345
  ? String(user.pluginMountPath).trim()
352
346
  : String(resolveBundledEnv(EK.mountPath, '') || DEF.pluginMountPath).trim();
@@ -354,7 +348,7 @@ function resolveRuntimeOptions$1(user = {}) {
354
348
  const pluginHostRouteMeta = user.pluginHostRouteMeta !== undefined && user.pluginHostRouteMeta !== null
355
349
  ? user.pluginHostRouteMeta
356
350
  : undefined;
357
- const ensurePluginHostRoute = user.ensurePluginHostRoute !== false;
351
+ const ensurePluginHostRoute = user.ensurePluginHostRoute === true;
358
352
  const devManifestFallback = (() => {
359
353
  if (manifestMode === 'static') {
360
354
  return false;
@@ -417,12 +411,6 @@ function resolveRuntimeOptions$1(user = {}) {
417
411
  ...(typeof user.adaptRouteDeclarations === 'function'
418
412
  ? { adaptRouteDeclarations: user.adaptRouteDeclarations }
419
413
  : {}),
420
- ...(typeof user.applyPluginMenuItems === 'function'
421
- ? { applyPluginMenuItems: user.applyPluginMenuItems }
422
- : {}),
423
- ...(typeof user.revokePluginMenuItems === 'function'
424
- ? { revokePluginMenuItems: user.revokePluginMenuItems }
425
- : {}),
426
414
  ...(user.hostContext !== undefined &&
427
415
  user.hostContext !== null &&
428
416
  typeof user.hostContext === 'object' &&
@@ -3214,21 +3202,18 @@ var semverExports = requireSemver();
3214
3202
  var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
3215
3203
 
3216
3204
  /**
3217
- * `disposeWebPlugin` 使用的菜单撤销钩子:在 `bootstrapPlugins` 中用 `resolveRuntimeOptions` 结果注册。
3205
+ * 引导时注入的 router 引用,供 disposeWebPlugin 按插件批量 removeRoute。
3218
3206
  */
3219
- let revokePluginMenuItems;
3220
- function setRevokePluginMenuItems(fn) {
3221
- revokePluginMenuItems = fn;
3207
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3208
+ let bootstrapRouter;
3209
+ /** bootstrapPlugins 在浏览器环境调用 */
3210
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3211
+ function setPluginBootstrapRouter(router) {
3212
+ bootstrapRouter = router;
3222
3213
  }
3223
- function revokePluginMenusIfConfigured(pluginId) {
3224
- if (typeof revokePluginMenuItems === 'function') {
3225
- try {
3226
- revokePluginMenuItems(pluginId);
3227
- }
3228
- catch (e) {
3229
- console.warn('[wep] revokePluginMenuItems failed', pluginId, e);
3230
- }
3231
- }
3214
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3215
+ function getPluginBootstrapRouter() {
3216
+ return bootstrapRouter;
3232
3217
  }
3233
3218
 
3234
3219
  /**
@@ -3485,7 +3470,8 @@ async function fetchStaticManifestViaHttp(ctx) {
3485
3470
  }
3486
3471
 
3487
3472
  /**
3488
- * 开箱:在未手工配置时,注册 `/plugin` + 宿主 Layout 的命名父路由,供 `router.addRoute(parentName, child)` 挂载插件页。
3473
+ * `ensurePluginHostRoute === true` 且提供 `pluginRoutesParentName` + `hostLayoutComponent` 时,
3474
+ * 注册 `pluginMountPath` + Layout 的命名父路由,供 `router.addRoute(parentName, child)` 挂载插件页。
3489
3475
  */
3490
3476
  function routeNameExists(router, name) {
3491
3477
  if (!name) {
@@ -3512,7 +3498,7 @@ function walkRouteNames(routes, name) {
3512
3498
  return false;
3513
3499
  }
3514
3500
  function ensurePluginHostRoute$1(router, opts) {
3515
- if (opts.ensurePluginHostRoute === false) {
3501
+ if (opts.ensurePluginHostRoute !== true) {
3516
3502
  return;
3517
3503
  }
3518
3504
  if (!router || typeof router.addRoute !== 'function') {
@@ -3641,7 +3627,7 @@ createHostApiFactory, runtimeOptions) {
3641
3627
  }
3642
3628
  printRuntimeBannerOnce();
3643
3629
  const opts = resolveRuntimeOptions$1(runtimeOptions || {});
3644
- setRevokePluginMenuItems(typeof opts.revokePluginMenuItems === 'function' ? opts.revokePluginMenuItems : undefined);
3630
+ setPluginBootstrapRouter(router);
3645
3631
  ensurePluginHostRoute$1(router, opts);
3646
3632
  const base = String(opts.manifestBase).replace(/\/$/, '');
3647
3633
  const isStatic = opts.manifestMode === 'static';
@@ -3718,12 +3704,6 @@ createHostApiFactory, runtimeOptions) {
3718
3704
  : {}),
3719
3705
  ...(typeof opts.adaptRouteDeclarations === 'function'
3720
3706
  ? { adaptRouteDeclarations: opts.adaptRouteDeclarations }
3721
- : {}),
3722
- ...(typeof opts.applyPluginMenuItems === 'function'
3723
- ? { applyPluginMenuItems: opts.applyPluginMenuItems }
3724
- : {}),
3725
- ...(typeof opts.revokePluginMenuItems === 'function'
3726
- ? { revokePluginMenuItems: opts.revokePluginMenuItems }
3727
3707
  : {})
3728
3708
  };
3729
3709
  if (!manifestResult.ok) {
@@ -4013,7 +3993,7 @@ var manifestComposer = /*#__PURE__*/Object.freeze({
4013
3993
 
4014
3994
  /**
4015
3995
  * 宿主侧响应式注册表:扩展点槽位(供布局与 `ExtensionPoint` 消费)。
4016
- * 菜单数据由宿主在 `applyPluginMenuItems` 中自行并入其路由/菜单 state,框架不维护平行菜单列表。
3996
+ * 菜单由宿主从路由 `meta` 推导(见 `buildMenuDescriptorsFromRoutes`),框架不维护平行菜单列表。
4017
3997
  */
4018
3998
  const registries = Vue.observable({
4019
3999
  slots: {},
@@ -4076,6 +4056,47 @@ function createRequestBridge(config = {}) {
4076
4056
  };
4077
4057
  }
4078
4058
 
4059
+ /**
4060
+ * 记录各插件通过 registerRoutes 挂到 router 上的顶层 route name,便于 dispose 时 removeRoute。
4061
+ */
4062
+ const pluginIdToRouteNames = new Map();
4063
+ function recordPluginTopRouteNames(pluginId, names) {
4064
+ if (!pluginId || names.length === 0) {
4065
+ return;
4066
+ }
4067
+ const cur = pluginIdToRouteNames.get(pluginId) || [];
4068
+ pluginIdToRouteNames.set(pluginId, cur.concat(names));
4069
+ }
4070
+ /**
4071
+ * 从 matcher 移除该插件登记过的顶层路由(含其子树)。
4072
+ * 需 vue-router ≥3.5 的 removeRoute;否则静默跳过。
4073
+ */
4074
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4075
+ function removeRegisteredRoutesForPlugin(router, pluginId) {
4076
+ const names = pluginIdToRouteNames.get(pluginId);
4077
+ if (!names || names.length === 0) {
4078
+ return;
4079
+ }
4080
+ if (!router || typeof router.removeRoute !== 'function') {
4081
+ console.warn('[wep] router.removeRoute 不可用,跳过动态路由卸载', pluginId);
4082
+ pluginIdToRouteNames.delete(pluginId);
4083
+ return;
4084
+ }
4085
+ for (let i = names.length - 1; i >= 0; i--) {
4086
+ try {
4087
+ router.removeRoute(names[i]);
4088
+ }
4089
+ catch (e) {
4090
+ console.warn('[wep] removeRoute failed', names[i], e);
4091
+ }
4092
+ }
4093
+ pluginIdToRouteNames.delete(pluginId);
4094
+ }
4095
+ /** 测试或宿主高级场景:查询已登记 name(勿依赖顺序语义) */
4096
+ function getRegisteredTopRouteNamesForPlugin(pluginId) {
4097
+ return [...(pluginIdToRouteNames.get(pluginId) || [])];
4098
+ }
4099
+
4079
4100
  /**
4080
4101
  * 构造插件 `activator(hostApi)` 使用的宿主 API:路由、菜单、扩展点、资源与受控请求桥。
4081
4102
  */
@@ -4131,6 +4152,7 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
4131
4152
  if (typeof router.addRoute !== 'function') {
4132
4153
  throw new Error('[wep] vue-router 3.5+ 必需:请使用 router.addRoute(不再支持 addRoutes)');
4133
4154
  }
4155
+ recordPluginTopRouteNames(pluginId, wrapped.map((r) => String(r.name)));
4134
4156
  if (parentName) {
4135
4157
  for (const r of wrapped) {
4136
4158
  router.addRoute(parentName, r);
@@ -4210,16 +4232,6 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
4210
4232
  applyInternalRegister(configs);
4211
4233
  }
4212
4234
  },
4213
- registerMenuItems(items) {
4214
- const apply = hostKitOptions.applyPluginMenuItems;
4215
- if (typeof apply !== 'function') {
4216
- throw new Error('[wep] registerMenuItems 需要宿主在 resolveRuntimeOptions 中提供 applyPluginMenuItems,将菜单数据并入宿主侧栏/目录 state(框架不再维护 registries.menus)');
4217
- }
4218
- const list = Array.isArray(items) ? items : [];
4219
- const enriched = list.map((item) => ({ ...item, pluginId }));
4220
- enriched.sort((a, b) => (a.order != null ? Number(a.order) : 0) - (b.order != null ? Number(b.order) : 0));
4221
- apply({ pluginId, items: enriched });
4222
- },
4223
4235
  registerSlotComponents(pointId, components) {
4224
4236
  if (!pointId) {
4225
4237
  return;
@@ -4263,15 +4275,15 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
4263
4275
  }
4264
4276
 
4265
4277
  /**
4266
- * 卸载单个插件:执行 teardown、清理注册表与 activator、移除带 `data-plugin-asset` 的 DOM。
4267
- * 注意:Vue Router 3 无公开 `removeRoute`,动态路由通常需整页刷新或宿主自行维护。
4278
+ * 卸载单个插件:执行 teardown、按登记 name 批量 removeRoute、清理注册表与 activator、移除带 `data-plugin-asset` 的 DOM。
4279
+ * 依赖 vue-router 3.5 removeRoute;引导时由 bootstrapPlugins 注入 router。
4268
4280
  */
4269
4281
  function disposeWebPlugin(pluginId) {
4270
4282
  if (!pluginId || typeof pluginId !== 'string') {
4271
4283
  return;
4272
4284
  }
4273
4285
  runPluginTeardowns(pluginId);
4274
- revokePluginMenusIfConfigured(pluginId);
4286
+ removeRegisteredRoutesForPlugin(getPluginBootstrapRouter(), pluginId);
4275
4287
  const slots = registries.slots;
4276
4288
  for (const pointId of Object.keys(slots)) {
4277
4289
  const list = slots[pointId];
@@ -4299,6 +4311,75 @@ function disposeWebPlugin(pluginId) {
4299
4311
  }
4300
4312
  }
4301
4313
 
4314
+ /**
4315
+ * 从路由配置(含 meta)推导侧栏/目录用菜单描述,与 registerRoutes 入参同源。
4316
+ *
4317
+ * 约定:
4318
+ * - `meta.menu === false`:该节点及其子树不参与菜单
4319
+ * - 节点出现条件:`meta.title` 或 `meta.menuTitle` 非空字符串,或存在非空的子菜单列表
4320
+ * - `meta.order` 数字越小越靠前;缺省为 0
4321
+ * - 可选:`meta.icon`、`meta.permission`、`meta.hidden`、`meta.external`
4322
+ */
4323
+ function pickTitle(meta, route) {
4324
+ if (typeof meta.menuTitle === 'string' && meta.menuTitle) {
4325
+ return meta.menuTitle;
4326
+ }
4327
+ if (typeof meta.title === 'string' && meta.title) {
4328
+ return meta.title;
4329
+ }
4330
+ if (route.name != null && String(route.name)) {
4331
+ return String(route.name);
4332
+ }
4333
+ return String(route.path || '');
4334
+ }
4335
+ function sortDescriptors(items) {
4336
+ items.sort((a, b) => a.order - b.order || a.path.localeCompare(b.path));
4337
+ for (const x of items) {
4338
+ if (x.children && x.children.length) {
4339
+ sortDescriptors(x.children);
4340
+ }
4341
+ }
4342
+ }
4343
+ /**
4344
+ * 从插件侧 RouteConfig 树构建菜单描述(不含 component,便于并入宿主菜单 state)。
4345
+ */
4346
+ function buildMenuDescriptorsFromRoutes(routes, pluginId) {
4347
+ const out = [];
4348
+ function walk(route) {
4349
+ const meta = (route.meta || {});
4350
+ if (meta.menu === false) {
4351
+ return null;
4352
+ }
4353
+ const chIn = Array.isArray(route.children) ? route.children : [];
4354
+ const childMenus = chIn.map(walk).filter(Boolean);
4355
+ const hasTitle = typeof meta.title === 'string' || typeof meta.menuTitle === 'string';
4356
+ if (!hasTitle && childMenus.length === 0) {
4357
+ return null;
4358
+ }
4359
+ const item = {
4360
+ path: String(route.path || ''),
4361
+ name: route.name,
4362
+ title: pickTitle(meta, route),
4363
+ order: meta.order != null ? Number(meta.order) : 0,
4364
+ ...(pluginId ? { pluginId } : {}),
4365
+ ...(meta.icon !== undefined ? { icon: meta.icon } : {}),
4366
+ ...(meta.permission !== undefined ? { permission: meta.permission } : {}),
4367
+ ...(meta.hidden === true ? { hidden: true } : {}),
4368
+ ...(meta.external === true ? { external: true } : {}),
4369
+ ...(childMenus.length ? { children: childMenus } : {})
4370
+ };
4371
+ return item;
4372
+ }
4373
+ for (const r of routes) {
4374
+ const m = walk(r);
4375
+ if (m) {
4376
+ out.push(m);
4377
+ }
4378
+ }
4379
+ sortDescriptors(out);
4380
+ return out;
4381
+ }
4382
+
4302
4383
  /**
4303
4384
  * 布局中的扩展点占位;插件通过 `hostApi.registerSlotComponents(pointId, ...)` 注入组件。
4304
4385
  * 样式由宿主全局 CSS 覆盖 `.extension-point` / `.plugin-point-error`。
@@ -4469,7 +4550,7 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
4469
4550
  function createVueCliAxiosQuickInstallOptions(
4470
4551
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4471
4552
  router, deps, extra = {}) {
4472
- const { request, hostLayoutComponent, store, hostContext: depHostContext, applyPluginMenuItems, revokePluginMenuItems } = deps;
4553
+ const { request, hostLayoutComponent, store, hostContext: depHostContext } = deps;
4473
4554
  const { hostContext: extraHostContext, isDev: extraIsDev, manifestListPath: extraListPath, ...restExtra } = extra;
4474
4555
  const hostContext = {
4475
4556
  router,
@@ -4483,8 +4564,6 @@ router, deps, extra = {}) {
4483
4564
  return createVueCliAxiosInstallOptions({ request }, {
4484
4565
  hostLayoutComponent,
4485
4566
  hostContext,
4486
- applyPluginMenuItems,
4487
- revokePluginMenuItems,
4488
4567
  isDev: extraIsDev !== undefined ? Boolean(extraIsDev) : resolveBundledIsDev(),
4489
4568
  manifestListPath,
4490
4569
  ...restExtra
@@ -4505,7 +4584,8 @@ const presetVueCliAxios = Object.freeze({
4505
4584
  */
4506
4585
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4507
4586
  function installVueCliAxiosWebPlugins(Vue, router, deps, extra) {
4508
- const exposeGlobalVue = extra?.exposeGlobalVue !== false;
4587
+ /** 避免输出 `?.`:Vue CLI 4 / Webpack 4 默认不转译 node_modules */
4588
+ const exposeGlobalVue = extra == null || extra.exposeGlobalVue !== false;
4509
4589
  if (exposeGlobalVue && typeof window !== 'undefined') {
4510
4590
  window.Vue = Vue;
4511
4591
  }
@@ -4534,7 +4614,9 @@ const WebExtendPluginVue2 = Object.freeze({
4534
4614
  createHostApi,
4535
4615
  disposeWebPlugin,
4536
4616
  createRequestBridge,
4537
- registries
4617
+ registries,
4618
+ buildMenuDescriptorsFromRoutes,
4619
+ getRegisteredTopRouteNamesForPlugin
4538
4620
  }),
4539
4621
  config: Object.freeze({
4540
4622
  defaultWebExtendPluginRuntime,
@@ -4557,5 +4639,5 @@ const WebExtendPluginVue2 = Object.freeze({
4557
4639
  })
4558
4640
  });
4559
4641
 
4560
- export { ExtensionPoint, HOST_PLUGIN_API_VERSION, RUNTIME_CONSOLE_LABEL, WebExtendPluginVue2, bootstrapPlugins, composeManifestFetch, createHostApi, createRequestBridge, createVueCliAxiosInstallOptions, createVueCliAxiosQuickInstallOptions, defaultFetchWebPluginManifest, defaultManifestFetchCache, defaultManifestMode, defaultVueCliJavaManifestListPath, defaultWebExtendPluginRuntime, disposeWebPlugin, ensurePluginHostRoute, installVueCliAxiosWebPlugins, installWebExtendPluginVue2, manifestFetchCacheMiddleware, peerMinimumVersions, presetVueCliAxios, registries, resolveManifestPathUnderApiBase, resolveRuntimeOptions, routeSynthNamePrefix, setWebExtendPluginEnv, unwrapNestedManifestBody, webExtendPluginEnvKeys, wrapManifestFetchWithCache };
4642
+ export { ExtensionPoint, HOST_PLUGIN_API_VERSION, RUNTIME_CONSOLE_LABEL, WebExtendPluginVue2, bootstrapPlugins, buildMenuDescriptorsFromRoutes, composeManifestFetch, createHostApi, createRequestBridge, createVueCliAxiosInstallOptions, createVueCliAxiosQuickInstallOptions, defaultFetchWebPluginManifest, defaultManifestFetchCache, defaultManifestMode, defaultVueCliJavaManifestListPath, defaultWebExtendPluginRuntime, disposeWebPlugin, ensurePluginHostRoute, getRegisteredTopRouteNamesForPlugin, installVueCliAxiosWebPlugins, installWebExtendPluginVue2, manifestFetchCacheMiddleware, peerMinimumVersions, presetVueCliAxios, registries, resolveManifestPathUnderApiBase, resolveRuntimeOptions, routeSynthNamePrefix, setWebExtendPluginEnv, unwrapNestedManifestBody, webExtendPluginEnvKeys, wrapManifestFetchWithCache };
4561
4643
  //# sourceMappingURL=index.mjs.map