web-extend-plugin-vue2 0.2.2 → 0.2.5
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 +223 -252
- package/dist/index.cjs +1627 -1563
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1616 -1562
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +260 -16
- package/package.json +10 -3
package/dist/index.cjs
CHANGED
|
@@ -6,438 +6,492 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
6
6
|
|
|
7
7
|
var Vue__default = /*#__PURE__*/_interopDefault(Vue);
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
9
|
+
/**
|
|
10
|
+
* 对外配置单一入口:`resolveRuntimeOptions` / 文档 / 其它模块的默认值与环境键名均由此引用。
|
|
11
|
+
* 宿主通过 `resolveRuntimeOptions(partial)` 覆盖的对象形状见 `defaultWebExtendPluginRuntime`。
|
|
12
|
+
*/
|
|
13
|
+
/** 与 `package.json` 的 peer 下限一致;`npm run test:peer-min` / CI matrix 须与此保持同步 */
|
|
14
|
+
const peerMinimumVersions = {
|
|
15
|
+
vue: '2.6.14',
|
|
16
|
+
vueRouter: '3.5.4'
|
|
17
|
+
};
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// 协议与品牌(发布契约;与清单 `hostPluginApiVersion` 对齐 semver 主版本,一般不随宿主覆盖)
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const HOST_PLUGIN_API_VERSION = '1.0.0';
|
|
22
|
+
/** 控制台日志与首次引导横幅使用的短名称 */
|
|
23
|
+
const RUNTIME_CONSOLE_LABEL = 'web-extend-plugin-vue2';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// 与 `build-env` / `resolveBundledEnv` 配套的键名(单一事实来源,便于检索与文档)
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
const webExtendPluginEnvKeys = {
|
|
28
|
+
manifestBase: 'VITE_FRONTEND_PLUGIN_BASE',
|
|
29
|
+
manifestListPath: 'VITE_WEB_PLUGIN_MANIFEST_PATH',
|
|
30
|
+
implicitDevIds: 'VITE_WEB_PLUGIN_IMPLICIT_DEV_IDS',
|
|
31
|
+
allowedScriptHosts: 'VITE_WEB_PLUGIN_ALLOWED_SCRIPT_HOSTS',
|
|
32
|
+
bridgePrefixes: 'VITE_WEB_PLUGIN_BRIDGE_PREFIXES',
|
|
33
|
+
devFallbackManifestUrl: 'VITE_WEB_PLUGIN_DEV_FALLBACK_MANIFEST_URL',
|
|
34
|
+
manifestMode: 'VITE_WEB_PLUGIN_MANIFEST_MODE',
|
|
35
|
+
staticManifestUrl: 'VITE_WEB_PLUGIN_STATIC_MANIFEST_URL',
|
|
36
|
+
mountPath: 'VITE_WEB_PLUGIN_MOUNT_PATH',
|
|
37
|
+
devEntry: 'VITE_WEB_PLUGIN_DEV_ENTRY',
|
|
38
|
+
devPing: 'VITE_WEB_PLUGIN_DEV_PING_PATH',
|
|
39
|
+
devSse: 'VITE_WEB_PLUGIN_DEV_SSE_PATH',
|
|
40
|
+
devPingTimeout: 'VITE_WEB_PLUGIN_DEV_PING_TIMEOUT_MS',
|
|
41
|
+
manifestCredentials: 'VITE_WEB_PLUGIN_MANIFEST_CREDENTIALS',
|
|
42
|
+
devManifestFallback: 'VITE_WEB_PLUGIN_DEV_MANIFEST_FALLBACK',
|
|
43
|
+
webPluginDevOrigin: 'VITE_WEB_PLUGIN_DEV_ORIGIN',
|
|
44
|
+
webPluginDevIds: 'VITE_WEB_PLUGIN_DEV_IDS',
|
|
45
|
+
webPluginDevMap: 'VITE_WEB_PLUGIN_DEV_MAP',
|
|
46
|
+
pluginsBootstrapSummary: 'VITE_PLUGINS_BOOTSTRAP_SUMMARY',
|
|
47
|
+
/** 清单路径备选:部分宿主单独使用 */
|
|
48
|
+
manifestPathAlt: 'VUE_APP_WEB_PLUGIN_MANIFEST_PATH'
|
|
49
|
+
};
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// `manifestMode` 未配置且环境未指定时的默认值
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
const defaultManifestMode = 'api';
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// `manifest-fetch-composer` 中间件默认(中间件 options 可逐项覆盖)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
const defaultManifestFetchCache = {
|
|
58
|
+
storageKeyPrefix: 'wep.manifestFetch.v1',
|
|
59
|
+
maxEntries: 50
|
|
60
|
+
};
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// 路由合成名前缀(`createHostApi` 为无 name 的路由生成 `__wep_${pluginId}_${seq}`)
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
const routeSynthNamePrefix = '__wep_';
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// 宿主可通过 `resolveRuntimeOptions` 覆盖的运行时默认值(与 README / index.d.ts 描述一致)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
const defaultWebExtendPluginRuntime = {
|
|
69
|
+
manifestBase: '/fp-api',
|
|
70
|
+
manifestListPath: '/api/frontend-plugins',
|
|
71
|
+
manifestFetchCredentials: 'include',
|
|
72
|
+
devPingPath: '/__web_plugin_dev_ping',
|
|
73
|
+
devReloadSsePath: '/__web_plugin_reload_stream',
|
|
74
|
+
webPluginDevEntryPath: '/src/plugin-entry.js',
|
|
75
|
+
devPingTimeoutMs: 500,
|
|
76
|
+
defaultImplicitDevPluginIds: [],
|
|
77
|
+
allowedScriptHosts: ['localhost', '127.0.0.1', '::1'],
|
|
78
|
+
bridgeAllowedPathPrefixes: ['/api/'],
|
|
79
|
+
/** 与 `hostLayoutComponent` 同时使用时默认父路由 name */
|
|
80
|
+
pluginRoutesParentName: '__wepPluginHost',
|
|
81
|
+
/** 插件壳路径(与菜单、ensurePluginHostRoute 一致) */
|
|
82
|
+
pluginMountPath: '/plugin',
|
|
83
|
+
/** `manifestMode=api` 且开发环境下,API 失败或 plugins 为空时尝试的静态 JSON(可放于 `public/web-plugins/`) */
|
|
84
|
+
devFallbackStaticManifestUrl: '/web-plugins/plugins.manifest.json'
|
|
66
85
|
};
|
|
67
86
|
|
|
68
87
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* - Vite:在入口调用 `setWebExtendPluginEnv(import.meta.env)`,或 `installWebExtendPluginVue2(..., { env: import.meta.env })`。
|
|
72
|
-
* - 也可在入口前设置 `globalThis.__WEP_ENV__ = import.meta.env`(与 setWebExtendPluginEnv 二选一即可)。
|
|
73
|
-
*
|
|
74
|
-
* @module bundled-env
|
|
88
|
+
* 与具体打包器解耦的运行时环境读取:优先显式注入,其次 `globalThis.__WEP_ENV__`,再读 `process.env`。
|
|
89
|
+
* Vite 宿主建议在入口调用 `setWebExtendPluginEnv(import.meta.env)`。
|
|
75
90
|
*/
|
|
76
|
-
|
|
77
|
-
/** @type {Record<string, unknown> | null} */
|
|
78
|
-
let _explicitEnv = null;
|
|
79
|
-
|
|
91
|
+
let _injected = null;
|
|
80
92
|
/**
|
|
81
|
-
*
|
|
82
|
-
* @param {Record<string, unknown> | null | undefined} env
|
|
93
|
+
* 注入与 `import.meta.env` 同形态的对象(键如 `VITE_*`、`DEV`)。
|
|
83
94
|
*/
|
|
84
95
|
function setWebExtendPluginEnv(env) {
|
|
85
|
-
|
|
96
|
+
_injected = env && typeof env === 'object' ? env : null;
|
|
86
97
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (g && g.__WEP_ENV__ && typeof g.__WEP_ENV__ === 'object') {
|
|
98
|
-
return g.__WEP_ENV__
|
|
98
|
+
function getEnvObject() {
|
|
99
|
+
if (_injected) {
|
|
100
|
+
return _injected;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const g = typeof globalThis !== 'undefined' ? globalThis : undefined;
|
|
104
|
+
const raw = g;
|
|
105
|
+
if (raw && raw.__WEP_ENV__ && typeof raw.__WEP_ENV__ === 'object') {
|
|
106
|
+
return raw.__WEP_ENV__;
|
|
107
|
+
}
|
|
99
108
|
}
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
catch {
|
|
110
|
+
/* ignore */
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
102
113
|
}
|
|
103
|
-
|
|
104
114
|
/**
|
|
105
|
-
*
|
|
106
|
-
* @param {string} key
|
|
107
|
-
* @returns {string|undefined}
|
|
115
|
+
* 读取注入环境中的字符串配置(`VITE_*` / `PLUGIN_*` 等价键由调用方传入)。
|
|
108
116
|
*/
|
|
109
117
|
function readInjectedEnvKey(key) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
const o = getEnvObject();
|
|
119
|
+
if (!o || !(key in o)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const v = o[key];
|
|
123
|
+
if (v === undefined || v === '') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return String(v);
|
|
119
127
|
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* @returns {boolean}
|
|
123
|
-
*/
|
|
124
128
|
function readInjectedEnvDev() {
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
const o = getEnvObject();
|
|
130
|
+
return !!(o && o.DEV === true);
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
/**
|
|
130
|
-
* 从注入环境与 `process.env` 解析 VITE_/PLUGIN_ 键。
|
|
131
|
-
* @module runtime/env-resolve
|
|
132
|
-
*/
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* @param {string} key
|
|
136
|
-
* @returns {string|undefined}
|
|
134
|
+
* 从注入环境与 `process.env` 解析 `VITE_*` / `PLUGIN_*` 键。
|
|
137
135
|
*/
|
|
138
136
|
function readProcessEnv(key) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
137
|
+
try {
|
|
138
|
+
if (typeof process !== 'undefined' && process.env && key in process.env) {
|
|
139
|
+
const v = process.env[key];
|
|
140
|
+
if (v !== undefined && v !== '') {
|
|
141
|
+
return String(v);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
/* ignore */
|
|
145
147
|
}
|
|
146
|
-
|
|
147
|
-
return undefined
|
|
148
|
+
return undefined;
|
|
148
149
|
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* @param {string} viteStyleKey
|
|
152
|
-
* @returns {string|null}
|
|
153
|
-
*/
|
|
154
150
|
function viteKeyToPluginAlternate(viteStyleKey) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
151
|
+
if (typeof viteStyleKey !== 'string' || !viteStyleKey.startsWith('VITE_')) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return `PLUGIN_${viteStyleKey.slice(5)}`;
|
|
159
155
|
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* @param {string} key
|
|
163
|
-
* @param {string} [fallback='']
|
|
164
|
-
*/
|
|
165
156
|
function resolveBundledEnv(key, fallback = '') {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const first = fromInjected === undefined || fromInjected === null ? fromProcess : fromInjected;
|
|
174
|
-
return first === undefined || first === null ? fallback : first
|
|
157
|
+
const alt = viteKeyToPluginAlternate(key);
|
|
158
|
+
const inj = readInjectedEnvKey(key);
|
|
159
|
+
const fromInjected = inj === undefined || inj === null ? (alt ? readInjectedEnvKey(alt) : undefined) : inj;
|
|
160
|
+
const proc = readProcessEnv(key);
|
|
161
|
+
const fromProcess = proc === undefined || proc === null ? (alt ? readProcessEnv(alt) : undefined) : proc;
|
|
162
|
+
const first = fromInjected === undefined || fromInjected === null ? fromProcess : fromInjected;
|
|
163
|
+
return first === undefined || first === null ? fallback : first;
|
|
175
164
|
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* @returns {boolean}
|
|
179
|
-
*/
|
|
180
165
|
function resolveBundledIsDev() {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
166
|
+
try {
|
|
167
|
+
if (readInjectedEnvDev()) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
/* ignore */
|
|
184
173
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
174
|
+
try {
|
|
175
|
+
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'development') {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
189
178
|
}
|
|
190
|
-
|
|
191
|
-
|
|
179
|
+
catch {
|
|
180
|
+
/* ignore */
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
192
183
|
}
|
|
193
184
|
|
|
194
185
|
/**
|
|
195
|
-
*
|
|
196
|
-
* @module runtime/path-host-utils
|
|
186
|
+
* 清单模式与静态清单 URL 配置解析(显式 options + VITE_* / PLUGIN_* 环境)。
|
|
197
187
|
*/
|
|
188
|
+
const EK$1 = webExtendPluginEnvKeys;
|
|
189
|
+
function resolveManifestModeFromInputs(userMode) {
|
|
190
|
+
if (userMode !== undefined && userMode !== null && String(userMode).trim() !== '') {
|
|
191
|
+
const s = String(userMode).trim().toLowerCase();
|
|
192
|
+
if (s === 'static' || s === 'api') {
|
|
193
|
+
return s;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const e = resolveBundledEnv(EK$1.manifestMode, '');
|
|
197
|
+
if (e) {
|
|
198
|
+
const s = String(e).trim().toLowerCase();
|
|
199
|
+
if (s === 'static' || s === 'api') {
|
|
200
|
+
return s;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return defaultManifestMode;
|
|
204
|
+
}
|
|
205
|
+
function resolveStaticManifestUrlFromInputs(userUrl) {
|
|
206
|
+
if (userUrl !== undefined && userUrl !== null) {
|
|
207
|
+
const s = String(userUrl).trim();
|
|
208
|
+
if (s !== '') {
|
|
209
|
+
return s;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return String(resolveBundledEnv(EK$1.staticManifestUrl, '') || '').trim();
|
|
213
|
+
}
|
|
198
214
|
|
|
199
215
|
/**
|
|
200
|
-
*
|
|
216
|
+
* 路径与脚本主机名校验工具。
|
|
201
217
|
*/
|
|
202
218
|
function ensureLeadingPath(p) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
219
|
+
const t = String(p || '').trim();
|
|
220
|
+
if (!t) {
|
|
221
|
+
return '/';
|
|
222
|
+
}
|
|
223
|
+
return t.startsWith('/') ? t : `/${t}`;
|
|
208
224
|
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @param {string} hostname
|
|
212
|
-
*/
|
|
213
225
|
function normalizeHost(hostname) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
if (!hostname) {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
const h = hostname.toLowerCase();
|
|
230
|
+
if (h.startsWith('[') && h.endsWith(']')) {
|
|
231
|
+
return h.slice(1, -1);
|
|
232
|
+
}
|
|
233
|
+
return h;
|
|
222
234
|
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @param {string[]} hostnames
|
|
226
|
-
* @returns {Set<string>}
|
|
227
|
-
*/
|
|
228
235
|
function buildAllowedScriptHostsSet(hostnames) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
236
|
+
const s = new Set();
|
|
237
|
+
for (const h of hostnames) {
|
|
238
|
+
const n = normalizeHost(h);
|
|
239
|
+
if (n) {
|
|
240
|
+
s.add(n);
|
|
241
|
+
}
|
|
234
242
|
}
|
|
235
|
-
|
|
236
|
-
return s
|
|
243
|
+
return s;
|
|
237
244
|
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* @param {string} url
|
|
241
|
-
* @param {Set<string>} hostSet
|
|
242
|
-
*/
|
|
243
245
|
function isScriptHostAllowed(url, hostSet) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
if (typeof window === 'undefined') {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const u = new URL(url, window.location.origin);
|
|
251
|
+
const h = normalizeHost(u.hostname);
|
|
252
|
+
return hostSet.has(h);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
/**
|
|
257
260
|
* 合并用户、环境与默认配置得到运行时选项。
|
|
258
|
-
* @module runtime/resolve-runtime-options
|
|
259
261
|
*/
|
|
260
|
-
|
|
261
|
-
|
|
262
262
|
const DEF = defaultWebExtendPluginRuntime;
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* @param {string|undefined} userVal
|
|
266
|
-
* @param {string} envKey
|
|
267
|
-
* @param {RequestCredentials} fallback
|
|
268
|
-
*/
|
|
263
|
+
const EK = webExtendPluginEnvKeys;
|
|
269
264
|
function resolveManifestCredentials(userVal, envKey, fallback) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
265
|
+
if (userVal !== undefined) {
|
|
266
|
+
const s = String(userVal);
|
|
267
|
+
if (s === 'include' || s === 'omit' || s === 'same-origin') {
|
|
268
|
+
return s;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const e = resolveBundledEnv(envKey, '');
|
|
272
|
+
if (e === 'include' || e === 'omit' || e === 'same-origin') {
|
|
273
|
+
return e;
|
|
274
274
|
}
|
|
275
|
-
|
|
276
|
-
const e = resolveBundledEnv(envKey, '');
|
|
277
|
-
if (e === 'include' || e === 'omit' || e === 'same-origin') {
|
|
278
|
-
return e
|
|
279
|
-
}
|
|
280
|
-
return fallback
|
|
275
|
+
return fallback;
|
|
281
276
|
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* @param {number|undefined} userVal
|
|
285
|
-
* @param {string} envKey
|
|
286
|
-
* @param {number} fallback
|
|
287
|
-
*/
|
|
288
277
|
function resolvePositiveInt(userVal, envKey, fallback) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
278
|
+
if (typeof userVal === 'number' && Number.isFinite(userVal) && userVal > 0) {
|
|
279
|
+
return Math.floor(userVal);
|
|
280
|
+
}
|
|
281
|
+
const raw = resolveBundledEnv(envKey, '');
|
|
282
|
+
const n = raw ? parseInt(raw, 10) : NaN;
|
|
283
|
+
if (Number.isFinite(n) && n > 0) {
|
|
284
|
+
return n;
|
|
285
|
+
}
|
|
286
|
+
return fallback;
|
|
298
287
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
* 合并用户、环境变量与 `defaultWebExtendPluginRuntime`,得到完整运行时选项(宿主可只传需要覆盖的字段)。
|
|
302
|
-
* @param {WebExtendPluginRuntimeOptions} [user]
|
|
303
|
-
* @returns {object}
|
|
304
|
-
*/
|
|
288
|
+
/** 合并用户、环境变量与 `defaultWebExtendPluginRuntime`,得到完整运行时选项(宿主可只传需要覆盖的字段)。 */
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
305
290
|
function resolveRuntimeOptions$1(user = {}) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
.split(',')
|
|
324
|
-
.map((s) => s.trim())
|
|
325
|
-
.filter(Boolean)
|
|
326
|
-
}
|
|
327
|
-
return [...DEF.defaultImplicitDevPluginIds]
|
|
328
|
-
})();
|
|
329
|
-
|
|
330
|
-
const allowedScriptHosts =
|
|
331
|
-
Array.isArray(user.allowedScriptHosts) && user.allowedScriptHosts.length > 0
|
|
332
|
-
? user.allowedScriptHosts.map((h) => normalizeHost(String(h))).filter(Boolean)
|
|
333
|
-
: (() => {
|
|
334
|
-
const e = resolveBundledEnv('VITE_WEB_PLUGIN_ALLOWED_SCRIPT_HOSTS', '');
|
|
335
|
-
if (e) {
|
|
336
|
-
return e
|
|
337
|
-
.split(',')
|
|
338
|
-
.map((s) => normalizeHost(s.trim()))
|
|
339
|
-
.filter(Boolean)
|
|
340
|
-
}
|
|
341
|
-
return [...DEF.allowedScriptHosts]
|
|
291
|
+
const manifestBaseRaw = user.manifestBase !== undefined && user.manifestBase !== ''
|
|
292
|
+
? String(user.manifestBase)
|
|
293
|
+
: resolveBundledEnv(EK.manifestBase, DEF.manifestBase) || DEF.manifestBase;
|
|
294
|
+
const manifestListPath = ensureLeadingPath(user.manifestListPath !== undefined && user.manifestListPath !== ''
|
|
295
|
+
? user.manifestListPath
|
|
296
|
+
: resolveBundledEnv(EK.manifestListPath, DEF.manifestListPath));
|
|
297
|
+
const defaultImplicitDevPluginIds = Array.isArray(user.defaultImplicitDevPluginIds)
|
|
298
|
+
? user.defaultImplicitDevPluginIds.map(String).filter(Boolean)
|
|
299
|
+
: (() => {
|
|
300
|
+
const e = resolveBundledEnv(EK.implicitDevIds, '');
|
|
301
|
+
if (e) {
|
|
302
|
+
return e
|
|
303
|
+
.split(',')
|
|
304
|
+
.map((s) => s.trim())
|
|
305
|
+
.filter(Boolean);
|
|
306
|
+
}
|
|
307
|
+
return [...DEF.defaultImplicitDevPluginIds];
|
|
342
308
|
})();
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
return [...DEF.bridgeAllowedPathPrefixes]
|
|
309
|
+
const allowedScriptHosts = Array.isArray(user.allowedScriptHosts) && user.allowedScriptHosts.length > 0
|
|
310
|
+
? user.allowedScriptHosts.map((h) => normalizeHost(String(h))).filter(Boolean)
|
|
311
|
+
: (() => {
|
|
312
|
+
const e = resolveBundledEnv(EK.allowedScriptHosts, '');
|
|
313
|
+
if (e) {
|
|
314
|
+
return e
|
|
315
|
+
.split(',')
|
|
316
|
+
.map((s) => normalizeHost(s.trim()))
|
|
317
|
+
.filter(Boolean);
|
|
318
|
+
}
|
|
319
|
+
return [...DEF.allowedScriptHosts];
|
|
356
320
|
})();
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
321
|
+
const bridgeAllowedPathPrefixes = Array.isArray(user.bridgeAllowedPathPrefixes) && user.bridgeAllowedPathPrefixes.length > 0
|
|
322
|
+
? user.bridgeAllowedPathPrefixes.map((p) => ensureLeadingPath(p)).filter(Boolean)
|
|
323
|
+
: (() => {
|
|
324
|
+
const e = resolveBundledEnv(EK.bridgePrefixes, '');
|
|
325
|
+
if (e) {
|
|
326
|
+
return e
|
|
327
|
+
.split(',')
|
|
328
|
+
.map((s) => ensureLeadingPath(s.trim()))
|
|
329
|
+
.filter(Boolean);
|
|
330
|
+
}
|
|
331
|
+
return [...DEF.bridgeAllowedPathPrefixes];
|
|
332
|
+
})();
|
|
333
|
+
const manifestMode = resolveManifestModeFromInputs(user.manifestMode);
|
|
334
|
+
const staticManifestUrl = resolveStaticManifestUrlFromInputs(user.staticManifestUrl);
|
|
335
|
+
const isDevResolved = user.isDev !== undefined ? user.isDev : resolveBundledIsDev();
|
|
336
|
+
const devFallbackStaticManifestUrl = (() => {
|
|
337
|
+
if (user.devFallbackStaticManifestUrl !== undefined && String(user.devFallbackStaticManifestUrl).trim() !== '') {
|
|
338
|
+
return String(user.devFallbackStaticManifestUrl).trim();
|
|
339
|
+
}
|
|
340
|
+
const e = resolveBundledEnv(EK.devFallbackManifestUrl, '');
|
|
341
|
+
if (e) {
|
|
342
|
+
return e.trim();
|
|
343
|
+
}
|
|
344
|
+
return String(DEF.devFallbackStaticManifestUrl).trim();
|
|
345
|
+
})();
|
|
346
|
+
const hostLayoutComponent = user.hostLayoutComponent;
|
|
347
|
+
const pluginRoutesParentName = (() => {
|
|
348
|
+
if (user.pluginRoutesParentName !== undefined) {
|
|
349
|
+
return String(user.pluginRoutesParentName).trim();
|
|
350
|
+
}
|
|
351
|
+
if (hostLayoutComponent != null) {
|
|
352
|
+
return String(DEF.pluginRoutesParentName).trim();
|
|
353
|
+
}
|
|
354
|
+
return '';
|
|
355
|
+
})();
|
|
356
|
+
const pluginMountRaw = user.pluginMountPath !== undefined && String(user.pluginMountPath).trim() !== ''
|
|
357
|
+
? String(user.pluginMountPath).trim()
|
|
358
|
+
: String(resolveBundledEnv(EK.mountPath, '') || DEF.pluginMountPath).trim();
|
|
359
|
+
const pluginMountPath = ensureLeadingPath(pluginMountRaw || DEF.pluginMountPath);
|
|
360
|
+
const pluginHostRouteMeta = user.pluginHostRouteMeta !== undefined && user.pluginHostRouteMeta !== null
|
|
361
|
+
? user.pluginHostRouteMeta
|
|
362
|
+
: undefined;
|
|
363
|
+
const ensurePluginHostRoute = user.ensurePluginHostRoute !== false;
|
|
364
|
+
const devManifestFallback = (() => {
|
|
365
|
+
if (manifestMode === 'static') {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (user.devManifestFallback === false) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
if (user.devManifestFallback === true) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
const envFlag = resolveBundledEnv(EK.devManifestFallback, '');
|
|
375
|
+
if (envFlag === '0' || envFlag === 'false') {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
if (envFlag === '1' || envFlag === 'true') {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
return !!isDevResolved;
|
|
382
|
+
})();
|
|
383
|
+
return {
|
|
384
|
+
manifestBase: manifestBaseRaw.replace(/\/$/, '') || DEF.manifestBase.replace(/\/$/, ''),
|
|
385
|
+
manifestListPath,
|
|
386
|
+
manifestMode,
|
|
387
|
+
staticManifestUrl,
|
|
388
|
+
devManifestFallback,
|
|
389
|
+
devFallbackStaticManifestUrl,
|
|
390
|
+
manifestFetchCredentials: resolveManifestCredentials(user.manifestFetchCredentials, EK.manifestCredentials, DEF.manifestFetchCredentials),
|
|
391
|
+
isDev: isDevResolved,
|
|
392
|
+
webPluginDevOrigin: user.webPluginDevOrigin !== undefined
|
|
393
|
+
? user.webPluginDevOrigin
|
|
394
|
+
: resolveBundledEnv(EK.webPluginDevOrigin, ''),
|
|
395
|
+
webPluginDevIds: user.webPluginDevIds !== undefined ? user.webPluginDevIds : resolveBundledEnv(EK.webPluginDevIds, ''),
|
|
396
|
+
webPluginDevMapJson: user.webPluginDevMapJson !== undefined
|
|
397
|
+
? user.webPluginDevMapJson
|
|
398
|
+
: resolveBundledEnv(EK.webPluginDevMap, ''),
|
|
399
|
+
webPluginDevEntryPath: ensureLeadingPath(user.webPluginDevEntryPath !== undefined && user.webPluginDevEntryPath !== ''
|
|
400
|
+
? user.webPluginDevEntryPath
|
|
401
|
+
: resolveBundledEnv(EK.devEntry, DEF.webPluginDevEntryPath)),
|
|
402
|
+
devPingPath: ensureLeadingPath(user.devPingPath !== undefined && user.devPingPath !== ''
|
|
403
|
+
? user.devPingPath
|
|
404
|
+
: resolveBundledEnv(EK.devPing, DEF.devPingPath)),
|
|
405
|
+
devReloadSsePath: ensureLeadingPath(user.devReloadSsePath !== undefined && user.devReloadSsePath !== ''
|
|
406
|
+
? user.devReloadSsePath
|
|
407
|
+
: resolveBundledEnv(EK.devSse, DEF.devReloadSsePath)),
|
|
408
|
+
devPingTimeoutMs: resolvePositiveInt(user.devPingTimeoutMs, EK.devPingTimeout, DEF.devPingTimeoutMs),
|
|
409
|
+
defaultImplicitDevPluginIds,
|
|
410
|
+
allowedScriptHosts,
|
|
411
|
+
bridgeAllowedPathPrefixes,
|
|
412
|
+
bootstrapSummary: user.bootstrapSummary,
|
|
413
|
+
hostLayoutComponent,
|
|
414
|
+
pluginMountPath,
|
|
415
|
+
pluginHostRouteMeta,
|
|
416
|
+
ensurePluginHostRoute,
|
|
417
|
+
pluginRoutesParentName,
|
|
418
|
+
...(typeof user.fetchManifest === 'function' ? { fetchManifest: user.fetchManifest } : {}),
|
|
419
|
+
...(typeof user.transformRoutes === 'function' ? { transformRoutes: user.transformRoutes } : {}),
|
|
420
|
+
...(typeof user.interceptRegisterRoutes === 'function'
|
|
421
|
+
? { interceptRegisterRoutes: user.interceptRegisterRoutes }
|
|
422
|
+
: {}),
|
|
423
|
+
...(typeof user.adaptRouteDeclarations === 'function'
|
|
424
|
+
? { adaptRouteDeclarations: user.adaptRouteDeclarations }
|
|
425
|
+
: {}),
|
|
426
|
+
...(typeof user.applyPluginMenuItems === 'function'
|
|
427
|
+
? { applyPluginMenuItems: user.applyPluginMenuItems }
|
|
428
|
+
: {}),
|
|
429
|
+
...(typeof user.revokePluginMenuItems === 'function'
|
|
430
|
+
? { revokePluginMenuItems: user.revokePluginMenuItems }
|
|
431
|
+
: {}),
|
|
432
|
+
...(user.hostContext !== undefined &&
|
|
433
|
+
user.hostContext !== null &&
|
|
434
|
+
typeof user.hostContext === 'object' &&
|
|
435
|
+
!Array.isArray(user.hostContext)
|
|
436
|
+
? { hostContext: user.hostContext }
|
|
437
|
+
: {}),
|
|
438
|
+
...(typeof user.onBeforePluginActivate === 'function'
|
|
439
|
+
? { onBeforePluginActivate: user.onBeforePluginActivate }
|
|
440
|
+
: {}),
|
|
441
|
+
...(typeof user.onAfterPluginActivate === 'function'
|
|
442
|
+
? { onAfterPluginActivate: user.onAfterPluginActivate }
|
|
443
|
+
: {}),
|
|
444
|
+
...(typeof user.onPluginActivateError === 'function'
|
|
445
|
+
? { onPluginActivateError: user.onPluginActivateError }
|
|
446
|
+
: {})
|
|
447
|
+
};
|
|
397
448
|
}
|
|
398
449
|
|
|
399
450
|
/**
|
|
400
|
-
*
|
|
401
|
-
* @module runtime/default-fetch-manifest
|
|
402
|
-
*/
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* @typedef {object} FetchWebPluginManifestContext
|
|
406
|
-
* @property {string} manifestUrl
|
|
407
|
-
* @property {RequestCredentials} credentials
|
|
408
|
-
*/
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* @typedef {object} FetchWebPluginManifestResult
|
|
412
|
-
* @property {boolean} ok
|
|
413
|
-
* @property {number} [status]
|
|
414
|
-
* @property {{ hostPluginApiVersion?: string, plugins?: object[] }|null} [data]
|
|
415
|
-
* @property {unknown} [error]
|
|
416
|
-
*/
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* @callback FetchWebPluginManifestFn
|
|
420
|
-
* @param {FetchWebPluginManifestContext} ctx
|
|
421
|
-
* @returns {Promise<FetchWebPluginManifestResult>}
|
|
451
|
+
* 将裸 `{ plugins }` 与 `{ code, data: { plugins } }` 式响应解包为清单对象。
|
|
422
452
|
*/
|
|
453
|
+
function unwrapNestedManifestBody(body) {
|
|
454
|
+
if (!body || typeof body !== 'object') {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const o = body;
|
|
458
|
+
if (Array.isArray(o.plugins)) {
|
|
459
|
+
return o;
|
|
460
|
+
}
|
|
461
|
+
const d = o.data;
|
|
462
|
+
if (d && typeof d === 'object') {
|
|
463
|
+
const inner = d;
|
|
464
|
+
if (Array.isArray(inner.plugins)) {
|
|
465
|
+
return inner;
|
|
466
|
+
}
|
|
467
|
+
if ('plugins' in inner) {
|
|
468
|
+
return inner;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return d !== undefined && d !== null && typeof d === 'object' ? d : o;
|
|
472
|
+
}
|
|
423
473
|
|
|
424
|
-
/**
|
|
425
|
-
* 默认清单请求(未配置 `fetchManifest` 时)。
|
|
426
|
-
* @param {FetchWebPluginManifestContext} ctx
|
|
427
|
-
* @returns {Promise<FetchWebPluginManifestResult>}
|
|
428
|
-
*/
|
|
429
474
|
async function defaultFetchWebPluginManifest$1(ctx) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
475
|
+
const { manifestUrl, credentials } = ctx;
|
|
476
|
+
try {
|
|
477
|
+
const res = await fetch(manifestUrl, { credentials });
|
|
478
|
+
if (!res.ok) {
|
|
479
|
+
return { ok: false, status: res.status, data: null };
|
|
480
|
+
}
|
|
481
|
+
const body = await res.json();
|
|
482
|
+
const data = unwrapNestedManifestBody(body);
|
|
483
|
+
if (!data || typeof data !== 'object') {
|
|
484
|
+
return {
|
|
485
|
+
ok: false,
|
|
486
|
+
error: new Error('[wep] invalid manifest response body'),
|
|
487
|
+
data: null
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return { ok: true, data };
|
|
491
|
+
}
|
|
492
|
+
catch (e) {
|
|
493
|
+
return { ok: false, error: e, data: null };
|
|
435
494
|
}
|
|
436
|
-
const data = await res.json();
|
|
437
|
-
return { ok: true, data }
|
|
438
|
-
} catch (e) {
|
|
439
|
-
return { ok: false, error: e, data: null }
|
|
440
|
-
}
|
|
441
495
|
}
|
|
442
496
|
|
|
443
497
|
function getDefaultExportFromCjs (x) {
|
|
@@ -3165,1368 +3219,1378 @@ function requireSemver () {
|
|
|
3165
3219
|
var semverExports = requireSemver();
|
|
3166
3220
|
var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
|
|
3167
3221
|
|
|
3168
|
-
/**
|
|
3169
|
-
* 与 `plugin-web-starter`(WebPluginsResponse)返回的 `hostPluginApiVersion` 保持一致,用于契约校验。
|
|
3170
|
-
* @type {string}
|
|
3171
|
-
*/
|
|
3172
|
-
const HOST_PLUGIN_API_VERSION = '1.0.0';
|
|
3173
|
-
|
|
3174
3222
|
/**
|
|
3175
|
-
*
|
|
3176
|
-
* @module runtime/dev-map
|
|
3223
|
+
* `disposeWebPlugin` 使用的菜单撤销钩子:在 `bootstrapPlugins` 中用 `resolveRuntimeOptions` 结果注册。
|
|
3177
3224
|
*/
|
|
3225
|
+
let revokePluginMenuItems;
|
|
3226
|
+
function setRevokePluginMenuItems(fn) {
|
|
3227
|
+
revokePluginMenuItems = fn;
|
|
3228
|
+
}
|
|
3229
|
+
function revokePluginMenusIfConfigured(pluginId) {
|
|
3230
|
+
if (typeof revokePluginMenuItems === 'function') {
|
|
3231
|
+
try {
|
|
3232
|
+
revokePluginMenuItems(pluginId);
|
|
3233
|
+
}
|
|
3234
|
+
catch (e) {
|
|
3235
|
+
console.warn('[wep] revokePluginMenuItems failed', pluginId, e);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3178
3239
|
|
|
3179
3240
|
/**
|
|
3180
|
-
*
|
|
3241
|
+
* 开发模式插件 URL 映射(显式 JSON + 隐式 dev 探测)。
|
|
3181
3242
|
*/
|
|
3182
3243
|
function parseWebPluginDevMapExplicit(opts) {
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3244
|
+
if (!opts.isDev) {
|
|
3245
|
+
return null;
|
|
3246
|
+
}
|
|
3247
|
+
const raw = opts.webPluginDevMapJson;
|
|
3248
|
+
if (raw === undefined || raw === null || String(raw).trim() === '') {
|
|
3249
|
+
return null;
|
|
3250
|
+
}
|
|
3251
|
+
try {
|
|
3252
|
+
const map = JSON.parse(String(raw));
|
|
3253
|
+
return map && typeof map === 'object' ? map : null;
|
|
3254
|
+
}
|
|
3255
|
+
catch {
|
|
3256
|
+
console.warn('[wep] invalid webPluginDevMapJson / VITE_WEB_PLUGIN_DEV_MAP');
|
|
3257
|
+
return null;
|
|
3258
|
+
}
|
|
3197
3259
|
}
|
|
3198
|
-
|
|
3199
|
-
/**
|
|
3200
|
-
* @param {Record<string, string>} implicit
|
|
3201
|
-
* @param {Record<string, string>|null} explicit
|
|
3202
|
-
*/
|
|
3203
3260
|
function mergeDevMaps(implicit, explicit) {
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3261
|
+
const i = implicit && typeof implicit === 'object' ? implicit : {};
|
|
3262
|
+
const e = explicit && typeof explicit === 'object' ? explicit : {};
|
|
3263
|
+
return { ...i, ...e };
|
|
3207
3264
|
}
|
|
3208
|
-
|
|
3209
|
-
/**
|
|
3210
|
-
* @param {object} opts
|
|
3211
|
-
* @param {boolean} opts.isDev
|
|
3212
|
-
* @param {string|undefined|null} opts.webPluginDevOrigin
|
|
3213
|
-
* @param {string|undefined|null} opts.webPluginDevIds
|
|
3214
|
-
* @param {string[]} opts.defaultImplicitDevPluginIds
|
|
3215
|
-
* @param {string} opts.devPingPath
|
|
3216
|
-
* @param {number} opts.devPingTimeoutMs
|
|
3217
|
-
* @param {string} opts.webPluginDevEntryPath
|
|
3218
|
-
* @param {Set<string>} hostSet
|
|
3219
|
-
*/
|
|
3220
3265
|
async function buildImplicitWebPluginDevMap(opts, hostSet) {
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3266
|
+
if (!opts.isDev) {
|
|
3267
|
+
return {};
|
|
3268
|
+
}
|
|
3269
|
+
const origin = opts.webPluginDevOrigin === undefined || opts.webPluginDevOrigin === null
|
|
3270
|
+
? ''
|
|
3271
|
+
: String(opts.webPluginDevOrigin).trim();
|
|
3272
|
+
if (!origin) {
|
|
3273
|
+
return {};
|
|
3274
|
+
}
|
|
3275
|
+
if (!isScriptHostAllowed(`${origin}/`, hostSet)) {
|
|
3276
|
+
return {};
|
|
3277
|
+
}
|
|
3278
|
+
const idsRaw = opts.webPluginDevIds;
|
|
3279
|
+
const ids = idsRaw !== undefined && idsRaw !== null && String(idsRaw).trim() !== ''
|
|
3280
|
+
? String(idsRaw)
|
|
3281
|
+
.split(',')
|
|
3282
|
+
.map((s) => s.trim())
|
|
3283
|
+
.filter(Boolean)
|
|
3284
|
+
: [...opts.defaultImplicitDevPluginIds];
|
|
3285
|
+
if (ids.length === 0) {
|
|
3286
|
+
return {};
|
|
3287
|
+
}
|
|
3288
|
+
const base = origin.replace(/\/$/, '');
|
|
3289
|
+
const pingUrl = `${base}${opts.devPingPath}`;
|
|
3290
|
+
try {
|
|
3291
|
+
const ctrl = new AbortController();
|
|
3292
|
+
const timer = setTimeout(() => ctrl.abort(), opts.devPingTimeoutMs);
|
|
3293
|
+
const r = await fetch(pingUrl, {
|
|
3294
|
+
mode: 'cors',
|
|
3295
|
+
cache: 'no-store',
|
|
3296
|
+
signal: ctrl.signal
|
|
3297
|
+
});
|
|
3298
|
+
clearTimeout(timer);
|
|
3299
|
+
if (!r.ok) {
|
|
3300
|
+
return {};
|
|
3301
|
+
}
|
|
3302
|
+
const body = (await r.text()).trim();
|
|
3303
|
+
if (body !== 'ok') {
|
|
3304
|
+
return {};
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
catch {
|
|
3308
|
+
return {};
|
|
3261
3309
|
}
|
|
3262
|
-
const
|
|
3263
|
-
|
|
3264
|
-
|
|
3310
|
+
const pathPart = opts.webPluginDevEntryPath;
|
|
3311
|
+
const map = {};
|
|
3312
|
+
for (const id of ids) {
|
|
3313
|
+
map[id] = `${base}${pathPart}`;
|
|
3265
3314
|
}
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
const pathPart = opts.webPluginDevEntryPath;
|
|
3271
|
-
const map = {};
|
|
3272
|
-
for (const id of ids) {
|
|
3273
|
-
map[id] = `${base}${pathPart}`;
|
|
3274
|
-
}
|
|
3275
|
-
if (ids.length) {
|
|
3276
|
-
console.info(
|
|
3277
|
-
'[plugins] 已检测到插件 dev 服务(',
|
|
3278
|
-
base,
|
|
3279
|
-
'),下列 id 将加载隐式 dev 入口(',
|
|
3280
|
-
pathPart,
|
|
3281
|
-
')而非清单 dist:',
|
|
3282
|
-
ids.join(', ')
|
|
3283
|
-
);
|
|
3284
|
-
}
|
|
3285
|
-
return map
|
|
3315
|
+
if (ids.length) {
|
|
3316
|
+
console.info('[wep] plugin dev server', base, '→ implicit entries', pathPart, ids.join(', '));
|
|
3317
|
+
}
|
|
3318
|
+
return map;
|
|
3286
3319
|
}
|
|
3287
3320
|
|
|
3288
3321
|
/**
|
|
3289
3322
|
* 开发模式下插件热更新 SSE(按 dev 映射中的 origin 连接)。
|
|
3290
|
-
* @module runtime/dev-reload-sse
|
|
3291
3323
|
*/
|
|
3292
|
-
|
|
3293
|
-
/** @type {Map<string, EventSource>} */
|
|
3294
3324
|
const pluginDevEventSources = new Map();
|
|
3295
|
-
|
|
3296
3325
|
let pluginDevBeforeUnloadRegistered = false;
|
|
3297
|
-
|
|
3298
3326
|
function closeAllPluginDevEventSources() {
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3327
|
+
for (const es of pluginDevEventSources.values()) {
|
|
3328
|
+
try {
|
|
3329
|
+
es.close();
|
|
3330
|
+
}
|
|
3331
|
+
catch {
|
|
3332
|
+
/* ignore */
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
pluginDevEventSources.clear();
|
|
3305
3336
|
}
|
|
3306
|
-
|
|
3307
3337
|
function ensurePluginDevBeforeUnload() {
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3338
|
+
if (pluginDevBeforeUnloadRegistered || typeof window === 'undefined') {
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
pluginDevBeforeUnloadRegistered = true;
|
|
3342
|
+
window.addEventListener('beforeunload', closeAllPluginDevEventSources);
|
|
3313
3343
|
}
|
|
3314
|
-
|
|
3315
|
-
/**
|
|
3316
|
-
* @param {string} origin
|
|
3317
|
-
* @param {Set<string>} hostSet
|
|
3318
|
-
*/
|
|
3319
3344
|
function isDevOriginAllowedForSse(origin, hostSet) {
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3345
|
+
try {
|
|
3346
|
+
const u = new URL(origin);
|
|
3347
|
+
return hostSet.has(normalizeHost(u.hostname));
|
|
3348
|
+
}
|
|
3349
|
+
catch {
|
|
3350
|
+
return false;
|
|
3351
|
+
}
|
|
3326
3352
|
}
|
|
3327
|
-
|
|
3328
|
-
/**
|
|
3329
|
-
* @param {string} origin
|
|
3330
|
-
* @param {boolean} isDev
|
|
3331
|
-
* @param {Set<string>} hostSet
|
|
3332
|
-
* @param {string} ssePath
|
|
3333
|
-
*/
|
|
3334
3353
|
function startPluginDevReloadSse(origin, isDev, hostSet, ssePath) {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3354
|
+
if (!isDev || pluginDevEventSources.has(origin)) {
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
if (!isDevOriginAllowedForSse(origin, hostSet)) {
|
|
3358
|
+
return;
|
|
3359
|
+
}
|
|
3360
|
+
ensurePluginDevBeforeUnload();
|
|
3361
|
+
const base = origin.replace(/\/$/, '');
|
|
3362
|
+
const url = `${base}${ssePath}`;
|
|
3363
|
+
try {
|
|
3364
|
+
const es = new EventSource(url);
|
|
3365
|
+
pluginDevEventSources.set(origin, es);
|
|
3366
|
+
es.addEventListener('reload', () => {
|
|
3367
|
+
window.location.reload();
|
|
3368
|
+
});
|
|
3369
|
+
es.onopen = () => {
|
|
3370
|
+
console.info('[wep] dev reload SSE', url);
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
catch (e) {
|
|
3374
|
+
console.warn('[wep] EventSource failed', url, e);
|
|
3375
|
+
}
|
|
3356
3376
|
}
|
|
3357
|
-
|
|
3358
|
-
/**
|
|
3359
|
-
* @param {Record<string, string>|null|undefined} devMap
|
|
3360
|
-
* @param {boolean} isDev
|
|
3361
|
-
* @param {Set<string>} hostSet
|
|
3362
|
-
* @param {string} ssePath
|
|
3363
|
-
*/
|
|
3364
3377
|
function startPluginDevSseForMap(devMap, isDev, hostSet, ssePath) {
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
}
|
|
3368
|
-
const origins = new Set();
|
|
3369
|
-
for (const entry of Object.values(devMap)) {
|
|
3370
|
-
if (typeof entry !== 'string') {
|
|
3371
|
-
continue
|
|
3378
|
+
if (!isDev || !devMap || typeof window === 'undefined') {
|
|
3379
|
+
return;
|
|
3372
3380
|
}
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
|
|
3381
|
+
const origins = new Set();
|
|
3382
|
+
for (const entry of Object.values(devMap)) {
|
|
3383
|
+
if (typeof entry !== 'string') {
|
|
3384
|
+
continue;
|
|
3385
|
+
}
|
|
3386
|
+
const t = entry.trim();
|
|
3387
|
+
if (!t) {
|
|
3388
|
+
continue;
|
|
3389
|
+
}
|
|
3390
|
+
try {
|
|
3391
|
+
origins.add(new URL(t, window.location.href).origin);
|
|
3392
|
+
}
|
|
3393
|
+
catch {
|
|
3394
|
+
/* skip */
|
|
3395
|
+
}
|
|
3376
3396
|
}
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
} catch {
|
|
3380
|
-
/* skip */
|
|
3397
|
+
for (const o of origins) {
|
|
3398
|
+
startPluginDevReloadSse(o, isDev, hostSet, ssePath);
|
|
3381
3399
|
}
|
|
3382
|
-
}
|
|
3383
|
-
for (const o of origins) {
|
|
3384
|
-
startPluginDevReloadSse(o, isDev, hostSet, ssePath);
|
|
3385
|
-
}
|
|
3386
3400
|
}
|
|
3387
3401
|
|
|
3388
3402
|
/**
|
|
3389
3403
|
* 动态加载脚本(去重与并发合并)。
|
|
3390
|
-
* @module runtime/load-script
|
|
3391
3404
|
*/
|
|
3392
|
-
|
|
3393
3405
|
const loadScriptMemo = new Map();
|
|
3406
|
+
function loadScript(src) {
|
|
3407
|
+
if (typeof document === 'undefined') {
|
|
3408
|
+
return Promise.reject(new Error('loadScript: no document'));
|
|
3409
|
+
}
|
|
3410
|
+
if (loadScriptMemo.has(src)) {
|
|
3411
|
+
return loadScriptMemo.get(src);
|
|
3412
|
+
}
|
|
3413
|
+
const p = new Promise((resolve, reject) => {
|
|
3414
|
+
const scripts = document.getElementsByTagName('script');
|
|
3415
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
3416
|
+
const el = scripts[i];
|
|
3417
|
+
if (el.src === src) {
|
|
3418
|
+
if (el.getAttribute('data-wep-loaded') === 'true') {
|
|
3419
|
+
resolve();
|
|
3420
|
+
return;
|
|
3421
|
+
}
|
|
3422
|
+
el.addEventListener('load', () => {
|
|
3423
|
+
el.setAttribute('data-wep-loaded', 'true');
|
|
3424
|
+
resolve();
|
|
3425
|
+
}, { once: true });
|
|
3426
|
+
el.addEventListener('error', () => reject(new Error('loadScript failed: ' + src)), { once: true });
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
const s = document.createElement('script');
|
|
3431
|
+
s.async = true;
|
|
3432
|
+
s.src = src;
|
|
3433
|
+
s.onload = () => {
|
|
3434
|
+
s.setAttribute('data-wep-loaded', 'true');
|
|
3435
|
+
resolve();
|
|
3436
|
+
};
|
|
3437
|
+
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3438
|
+
document.head.appendChild(s);
|
|
3439
|
+
});
|
|
3440
|
+
loadScriptMemo.set(src, p);
|
|
3441
|
+
p.catch(() => loadScriptMemo.delete(src));
|
|
3442
|
+
return p;
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
let _printed = false;
|
|
3446
|
+
/** 在首次引导插件时打印一行运行时标识(非大块 ASCII art)。 */
|
|
3447
|
+
function printRuntimeBannerOnce() {
|
|
3448
|
+
if (_printed) {
|
|
3449
|
+
return;
|
|
3450
|
+
}
|
|
3451
|
+
_printed = true;
|
|
3452
|
+
if (typeof console !== 'undefined' && typeof console.info === 'function') {
|
|
3453
|
+
console.info(`[wep] ${RUNTIME_CONSOLE_LABEL} · host API ${HOST_PLUGIN_API_VERSION}`);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3394
3456
|
|
|
3395
3457
|
/**
|
|
3396
|
-
*
|
|
3397
|
-
* @returns {Promise<void>}
|
|
3458
|
+
* 浏览器 fetch 静态 JSON 清单(与 defaultFetchWebPluginManifest 相同解包规则)。
|
|
3398
3459
|
*/
|
|
3399
|
-
function
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3460
|
+
async function fetchStaticManifestViaHttp(ctx) {
|
|
3461
|
+
const { manifestUrl, credentials } = ctx;
|
|
3462
|
+
try {
|
|
3463
|
+
const creds = credentials === 'omit' ? 'omit' : 'same-origin';
|
|
3464
|
+
const res = await fetch(manifestUrl, {
|
|
3465
|
+
method: 'GET',
|
|
3466
|
+
credentials: creds,
|
|
3467
|
+
cache: 'no-store'
|
|
3468
|
+
});
|
|
3469
|
+
if (!res.ok) {
|
|
3470
|
+
return {
|
|
3471
|
+
ok: false,
|
|
3472
|
+
status: res.status,
|
|
3473
|
+
error: new Error('[wep] static manifest HTTP ' + res.status),
|
|
3474
|
+
data: null
|
|
3475
|
+
};
|
|
3414
3476
|
}
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
return
|
|
3425
|
-
|
|
3477
|
+
const body = await res.json();
|
|
3478
|
+
const data = unwrapNestedManifestBody(body);
|
|
3479
|
+
if (!data || typeof data !== 'object') {
|
|
3480
|
+
return {
|
|
3481
|
+
ok: false,
|
|
3482
|
+
error: new Error('[wep] invalid static manifest JSON'),
|
|
3483
|
+
data: null
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
return { ok: true, data };
|
|
3487
|
+
}
|
|
3488
|
+
catch (e) {
|
|
3489
|
+
return { ok: false, error: e, data: null };
|
|
3426
3490
|
}
|
|
3427
|
-
const s = document.createElement('script');
|
|
3428
|
-
s.async = true;
|
|
3429
|
-
s.src = src;
|
|
3430
|
-
s.onload = () => {
|
|
3431
|
-
s.setAttribute('data-wep-loaded', 'true');
|
|
3432
|
-
resolve();
|
|
3433
|
-
};
|
|
3434
|
-
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3435
|
-
document.head.appendChild(s);
|
|
3436
|
-
});
|
|
3437
|
-
loadScriptMemo.set(src, p);
|
|
3438
|
-
p.catch(() => loadScriptMemo.delete(src));
|
|
3439
|
-
return p
|
|
3440
3491
|
}
|
|
3441
3492
|
|
|
3442
3493
|
/**
|
|
3443
|
-
*
|
|
3444
|
-
* @module runtime/bootstrap-plugins
|
|
3494
|
+
* 开箱:在未手工配置时,注册 `/plugin` + 宿主 Layout 的命名父路由,供 `router.addRoute(parentName, child)` 挂载插件页。
|
|
3445
3495
|
*/
|
|
3496
|
+
function routeNameExists(router, name) {
|
|
3497
|
+
if (!name) {
|
|
3498
|
+
return false;
|
|
3499
|
+
}
|
|
3500
|
+
if (typeof router.getRoutes === 'function') {
|
|
3501
|
+
return router.getRoutes().some((r) => r.name === name);
|
|
3502
|
+
}
|
|
3503
|
+
return walkRouteNames(router.options && router.options.routes, name);
|
|
3504
|
+
}
|
|
3505
|
+
function walkRouteNames(routes, name) {
|
|
3506
|
+
if (!Array.isArray(routes)) {
|
|
3507
|
+
return false;
|
|
3508
|
+
}
|
|
3509
|
+
for (const r of routes) {
|
|
3510
|
+
if (r && typeof r === 'object' && r.name === name) {
|
|
3511
|
+
return true;
|
|
3512
|
+
}
|
|
3513
|
+
const ch = r && typeof r === 'object' ? r.children : null;
|
|
3514
|
+
if (walkRouteNames(ch, name)) {
|
|
3515
|
+
return true;
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
return false;
|
|
3519
|
+
}
|
|
3520
|
+
function ensurePluginHostRoute$1(router, opts) {
|
|
3521
|
+
if (opts.ensurePluginHostRoute === false) {
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3524
|
+
if (!router || typeof router.addRoute !== 'function') {
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
const parentName = String(opts.pluginRoutesParentName || '').trim();
|
|
3528
|
+
if (!parentName) {
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
if (routeNameExists(router, parentName)) {
|
|
3532
|
+
return;
|
|
3533
|
+
}
|
|
3534
|
+
const Layout = opts.hostLayoutComponent;
|
|
3535
|
+
if (!Layout) {
|
|
3536
|
+
console.warn('[wep] 缺少 hostLayoutComponent,未自动注册插件壳路由;请传入宿主 Layout,或在路由表中自行配置与 pluginRoutesParentName 一致的父路由');
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
const mountDefault = defaultWebExtendPluginRuntime.pluginMountPath;
|
|
3540
|
+
let pathRaw = String(opts.pluginMountPath || mountDefault).trim().replace(/\/$/, '') || mountDefault;
|
|
3541
|
+
if (!pathRaw.startsWith('/')) {
|
|
3542
|
+
pathRaw = `/${pathRaw}`;
|
|
3543
|
+
}
|
|
3544
|
+
const meta = opts.pluginHostRouteMeta && typeof opts.pluginHostRouteMeta === 'object'
|
|
3545
|
+
? { ...opts.pluginHostRouteMeta }
|
|
3546
|
+
: { requiresConfig: true, hidden: true };
|
|
3547
|
+
router.addRoute({
|
|
3548
|
+
path: pathRaw,
|
|
3549
|
+
name: parentName,
|
|
3550
|
+
component: Layout,
|
|
3551
|
+
redirect: 'noredirect',
|
|
3552
|
+
meta,
|
|
3553
|
+
children: []
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3446
3556
|
|
|
3447
3557
|
/**
|
|
3448
|
-
*
|
|
3558
|
+
* 宿主通过 `resolveRuntimeOptions({ hostContext })` 注入的只读上下文,
|
|
3559
|
+
* 经浅拷贝 + 浅冻结后挂到 `hostApi.hostContext`,供插件访问 store/i18n 等而不污染 HostApi 顶层命名。
|
|
3449
3560
|
*/
|
|
3450
|
-
function
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
return false
|
|
3456
|
-
}
|
|
3457
|
-
const env = resolveBundledEnv('VITE_PLUGINS_BOOTSTRAP_SUMMARY', '');
|
|
3458
|
-
if (env === '0' || env === 'false') {
|
|
3459
|
-
return false
|
|
3460
|
-
}
|
|
3461
|
-
if (env === '1' || env === 'true') {
|
|
3462
|
-
return true
|
|
3463
|
-
}
|
|
3464
|
-
return resolveBundledIsDev()
|
|
3561
|
+
function freezeShallowHostContext(input) {
|
|
3562
|
+
if (input == null || typeof input !== 'object' || Array.isArray(input)) {
|
|
3563
|
+
return Object.freeze({});
|
|
3564
|
+
}
|
|
3565
|
+
return Object.freeze({ ...input });
|
|
3465
3566
|
}
|
|
3466
3567
|
|
|
3467
3568
|
/**
|
|
3468
|
-
*
|
|
3469
|
-
* @param {string} [entryUrl]
|
|
3470
|
-
* @param {Record<string, string>|null|undefined} devMap
|
|
3471
|
-
* @param {Set<string>} hostSet
|
|
3569
|
+
* 将静态清单配置路径解析为用于 fetch 的绝对 URL(浏览器)。
|
|
3472
3570
|
*/
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3571
|
+
function resolveStaticManifestUrlForFetch(url, origin) {
|
|
3572
|
+
const u = String(url || '').trim();
|
|
3573
|
+
if (!u) {
|
|
3574
|
+
return '';
|
|
3575
|
+
}
|
|
3576
|
+
if (/^https?:\/\//i.test(u)) {
|
|
3577
|
+
return u;
|
|
3578
|
+
}
|
|
3579
|
+
const o = String(origin || '').trim();
|
|
3580
|
+
if (!o) {
|
|
3581
|
+
return u.startsWith('/') ? u : `/${u}`;
|
|
3479
3582
|
}
|
|
3480
3583
|
try {
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
} catch (e) {
|
|
3487
|
-
console.warn('[plugins] dev module import failed, try manifest entryUrl', p.id, e);
|
|
3488
|
-
if (entryUrl && isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3489
|
-
await loadScript(entryUrl);
|
|
3490
|
-
}
|
|
3491
|
-
return
|
|
3584
|
+
const pathPart = u.startsWith('/') ? u : `/${u}`;
|
|
3585
|
+
return new URL(pathPart, o).href;
|
|
3586
|
+
}
|
|
3587
|
+
catch {
|
|
3588
|
+
return u;
|
|
3492
3589
|
}
|
|
3493
|
-
return
|
|
3494
|
-
}
|
|
3495
|
-
if (!entryUrl || !isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3496
|
-
console.warn('[plugins] skip (entryUrl not allowed)', p.id, entryUrl);
|
|
3497
|
-
return
|
|
3498
|
-
}
|
|
3499
|
-
await loadScript(entryUrl);
|
|
3500
3590
|
}
|
|
3501
3591
|
|
|
3502
3592
|
/**
|
|
3503
|
-
*
|
|
3504
|
-
* @param {(pluginId: string, router: import('vue-router').default, hostKit?: { bridgeAllowedPathPrefixes: string[] }) => object} createHostApiFactory
|
|
3505
|
-
* @param {import('./resolve-runtime-options.js').WebExtendPluginRuntimeOptions} [runtimeOptions]
|
|
3593
|
+
* 拉取插件清单、加载入口脚本并调用各插件 `activator`。
|
|
3506
3594
|
*/
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3595
|
+
function shouldShowBootstrapSummary(opts) {
|
|
3596
|
+
if (opts.bootstrapSummary === true) {
|
|
3597
|
+
return true;
|
|
3598
|
+
}
|
|
3599
|
+
if (opts.bootstrapSummary === false) {
|
|
3600
|
+
return false;
|
|
3601
|
+
}
|
|
3602
|
+
const env = resolveBundledEnv(webExtendPluginEnvKeys.pluginsBootstrapSummary, '');
|
|
3603
|
+
if (env === '0' || env === 'false') {
|
|
3604
|
+
return false;
|
|
3605
|
+
}
|
|
3606
|
+
if (env === '1' || env === 'true') {
|
|
3607
|
+
return true;
|
|
3608
|
+
}
|
|
3609
|
+
return resolveBundledIsDev();
|
|
3610
|
+
}
|
|
3611
|
+
async function loadPluginEntry(p, entryUrl, devMap, hostSet) {
|
|
3612
|
+
const devEntry = devMap && typeof devMap[p.id] === 'string' ? devMap[p.id].trim() : '';
|
|
3613
|
+
if (devEntry) {
|
|
3614
|
+
if (!isScriptHostAllowed(devEntry, hostSet)) {
|
|
3615
|
+
console.warn('[wep] dev entry URL not allowed', p.id, devEntry);
|
|
3616
|
+
return;
|
|
3617
|
+
}
|
|
3618
|
+
try {
|
|
3619
|
+
await import(
|
|
3620
|
+
/* webpackIgnore: true */
|
|
3621
|
+
/* @vite-ignore */
|
|
3622
|
+
devEntry);
|
|
3527
3623
|
}
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
const devMap = mergeDevMaps(implicit, explicit);
|
|
3537
|
-
startPluginDevSseForMap(devMap, opts.isDev, hostSet, opts.devReloadSsePath);
|
|
3538
|
-
|
|
3539
|
-
const hostKit = { bridgeAllowedPathPrefixes: opts.bridgeAllowedPathPrefixes };
|
|
3540
|
-
|
|
3541
|
-
if (!manifestResult.ok) {
|
|
3542
|
-
if (manifestResult.error) {
|
|
3543
|
-
console.warn('[plugins] fetch manifest failed', manifestResult.error);
|
|
3544
|
-
} else {
|
|
3545
|
-
console.warn('[plugins] manifest HTTP', manifestResult.status, manifestUrl);
|
|
3624
|
+
catch (e) {
|
|
3625
|
+
console.warn('[wep] dev import failed, trying manifest entryUrl', p.id, e);
|
|
3626
|
+
if (entryUrl && isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3627
|
+
await loadScript(entryUrl);
|
|
3628
|
+
}
|
|
3629
|
+
return;
|
|
3630
|
+
}
|
|
3631
|
+
return;
|
|
3546
3632
|
}
|
|
3547
|
-
if (
|
|
3548
|
-
|
|
3633
|
+
if (!entryUrl || !isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3634
|
+
console.warn('[wep] skip (entryUrl not allowed)', p.id, entryUrl);
|
|
3635
|
+
return;
|
|
3549
3636
|
}
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3637
|
+
await loadScript(entryUrl);
|
|
3638
|
+
}
|
|
3639
|
+
async function bootstrapPlugins$1(
|
|
3640
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3641
|
+
router,
|
|
3642
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3643
|
+
createHostApiFactory, runtimeOptions) {
|
|
3644
|
+
if (typeof window === 'undefined') {
|
|
3645
|
+
console.warn('[wep] bootstrapPlugins skipped (no window)');
|
|
3646
|
+
return;
|
|
3557
3647
|
}
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
const
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3648
|
+
printRuntimeBannerOnce();
|
|
3649
|
+
const opts = resolveRuntimeOptions$1(runtimeOptions || {});
|
|
3650
|
+
setRevokePluginMenuItems(typeof opts.revokePluginMenuItems === 'function' ? opts.revokePluginMenuItems : undefined);
|
|
3651
|
+
ensurePluginHostRoute$1(router, opts);
|
|
3652
|
+
const base = String(opts.manifestBase).replace(/\/$/, '');
|
|
3653
|
+
const isStatic = opts.manifestMode === 'static';
|
|
3654
|
+
let manifestUrl;
|
|
3655
|
+
if (isStatic) {
|
|
3656
|
+
const raw = String(opts.staticManifestUrl || '').trim();
|
|
3657
|
+
if (!raw) {
|
|
3658
|
+
console.warn('[wep] manifestMode=static requires non-empty staticManifestUrl (or env VITE_WEB_PLUGIN_STATIC_MANIFEST_URL)');
|
|
3659
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3660
|
+
console.info('[wep] bootstrap_summary', { ok: false, reason: 'static_manifest_url_missing' });
|
|
3661
|
+
}
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
manifestUrl = resolveStaticManifestUrlForFetch(raw, window.location.origin);
|
|
3573
3665
|
}
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
window.__PLUGIN_ACTIVATORS__ = window.__PLUGIN_ACTIVATORS__ || {};
|
|
3577
|
-
|
|
3578
|
-
const plugins = data.plugins || [];
|
|
3579
|
-
if (plugins.length === 0) {
|
|
3580
|
-
console.info(
|
|
3581
|
-
'[plugins] 清单为空。请检查:① 后端清单服务(plugin-web-starter)是否已接入;② web-plugin.web-plugins-dir 是否指向含各插件子目录及 manifest.json 的路径;③ 浏览器直接访问',
|
|
3582
|
-
manifestUrl,
|
|
3583
|
-
'是否返回 plugins 条目。'
|
|
3584
|
-
);
|
|
3585
|
-
}
|
|
3586
|
-
|
|
3587
|
-
const summary = {
|
|
3588
|
-
manifestCount: plugins.length,
|
|
3589
|
-
activated: 0,
|
|
3590
|
-
skipEngines: 0,
|
|
3591
|
-
skipLoad: 0,
|
|
3592
|
-
skipNoActivator: 0,
|
|
3593
|
-
activateFail: 0
|
|
3594
|
-
};
|
|
3595
|
-
|
|
3596
|
-
for (const p of plugins) {
|
|
3597
|
-
const range = p.engines && p.engines.host;
|
|
3598
|
-
if (range && !semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3599
|
-
console.warn('[plugins] skip (engines.host)', p.id, range);
|
|
3600
|
-
summary.skipEngines++;
|
|
3601
|
-
continue
|
|
3666
|
+
else {
|
|
3667
|
+
manifestUrl = `${base}${opts.manifestListPath}`;
|
|
3602
3668
|
}
|
|
3603
|
-
const
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3669
|
+
const hostSet = buildAllowedScriptHostsSet(opts.allowedScriptHosts);
|
|
3670
|
+
const explicit = parseWebPluginDevMapExplicit(opts);
|
|
3671
|
+
const manifestCtx = {
|
|
3672
|
+
manifestUrl,
|
|
3673
|
+
credentials: opts.manifestFetchCredentials
|
|
3674
|
+
};
|
|
3675
|
+
const [primaryResult, implicit] = await Promise.all([
|
|
3676
|
+
(async () => {
|
|
3677
|
+
try {
|
|
3678
|
+
if (typeof opts.fetchManifest === 'function') {
|
|
3679
|
+
return await opts.fetchManifest(manifestCtx);
|
|
3680
|
+
}
|
|
3681
|
+
return await defaultFetchWebPluginManifest$1(manifestCtx);
|
|
3682
|
+
}
|
|
3683
|
+
catch (e) {
|
|
3684
|
+
return { ok: false, error: e, data: null };
|
|
3685
|
+
}
|
|
3686
|
+
})(),
|
|
3687
|
+
buildImplicitWebPluginDevMap(opts, hostSet)
|
|
3688
|
+
]);
|
|
3689
|
+
let manifestResult = primaryResult;
|
|
3690
|
+
let manifestUrlUsed = manifestUrl;
|
|
3691
|
+
if (!isStatic && opts.devManifestFallback && opts.isDev) {
|
|
3692
|
+
const dataObj = primaryResult.ok && primaryResult.data && typeof primaryResult.data === 'object'
|
|
3693
|
+
? primaryResult.data
|
|
3694
|
+
: null;
|
|
3695
|
+
const plen = dataObj && Array.isArray(dataObj.plugins) ? dataObj.plugins.length : 0;
|
|
3696
|
+
const needFallback = !primaryResult.ok || plen === 0;
|
|
3697
|
+
const fallbackRaw = String(opts.devFallbackStaticManifestUrl || '').trim();
|
|
3698
|
+
if (needFallback && fallbackRaw) {
|
|
3699
|
+
const fallbackUrl = resolveStaticManifestUrlForFetch(fallbackRaw, window.location.origin);
|
|
3700
|
+
const fallbackCtx = {
|
|
3701
|
+
manifestUrl: fallbackUrl,
|
|
3702
|
+
credentials: opts.manifestFetchCredentials
|
|
3703
|
+
};
|
|
3704
|
+
const fr = await fetchStaticManifestViaHttp(fallbackCtx);
|
|
3705
|
+
const fdata = fr.ok && fr.data && typeof fr.data === 'object' ? fr.data : null;
|
|
3706
|
+
const flen = fdata && Array.isArray(fdata.plugins) ? fdata.plugins.length : 0;
|
|
3707
|
+
if (fr.ok && flen > 0) {
|
|
3708
|
+
manifestResult = fr;
|
|
3709
|
+
manifestUrlUsed = fallbackUrl;
|
|
3710
|
+
console.info('[wep] dev manifest fallback', { url: fallbackUrl, plugins: flen });
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3610
3713
|
}
|
|
3611
|
-
const
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3714
|
+
const devMap = mergeDevMaps(implicit, explicit);
|
|
3715
|
+
startPluginDevSseForMap(devMap, opts.isDev, hostSet, opts.devReloadSsePath);
|
|
3716
|
+
const frozenHostContext = freezeShallowHostContext(opts.hostContext !== undefined ? opts.hostContext : undefined);
|
|
3717
|
+
const hostKit = {
|
|
3718
|
+
hostContext: frozenHostContext,
|
|
3719
|
+
bridgeAllowedPathPrefixes: opts.bridgeAllowedPathPrefixes,
|
|
3720
|
+
...(opts.pluginRoutesParentName ? { pluginRoutesParentName: opts.pluginRoutesParentName } : {}),
|
|
3721
|
+
...(typeof opts.transformRoutes === 'function' ? { transformRoutes: opts.transformRoutes } : {}),
|
|
3722
|
+
...(typeof opts.interceptRegisterRoutes === 'function'
|
|
3723
|
+
? { interceptRegisterRoutes: opts.interceptRegisterRoutes }
|
|
3724
|
+
: {}),
|
|
3725
|
+
...(typeof opts.adaptRouteDeclarations === 'function'
|
|
3726
|
+
? { adaptRouteDeclarations: opts.adaptRouteDeclarations }
|
|
3727
|
+
: {}),
|
|
3728
|
+
...(typeof opts.applyPluginMenuItems === 'function'
|
|
3729
|
+
? { applyPluginMenuItems: opts.applyPluginMenuItems }
|
|
3730
|
+
: {}),
|
|
3731
|
+
...(typeof opts.revokePluginMenuItems === 'function'
|
|
3732
|
+
? { revokePluginMenuItems: opts.revokePluginMenuItems }
|
|
3733
|
+
: {})
|
|
3734
|
+
};
|
|
3735
|
+
if (!manifestResult.ok) {
|
|
3736
|
+
if (manifestResult.error) {
|
|
3737
|
+
console.warn('[wep] fetch manifest failed', manifestResult.error);
|
|
3738
|
+
}
|
|
3739
|
+
else {
|
|
3740
|
+
const label = isStatic ? 'static manifest' : 'manifest HTTP';
|
|
3741
|
+
console.warn(`[wep] ${label}`, manifestResult.status, manifestUrlUsed);
|
|
3742
|
+
}
|
|
3743
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3744
|
+
console.info('[wep] bootstrap_summary', { ok: false, reason: 'manifest_fetch' });
|
|
3745
|
+
}
|
|
3746
|
+
return;
|
|
3616
3747
|
}
|
|
3617
|
-
const
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3748
|
+
const data = manifestResult.data;
|
|
3749
|
+
if (!data) {
|
|
3750
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3751
|
+
console.info('[wep] bootstrap_summary', { ok: false, reason: 'manifest_empty_body' });
|
|
3752
|
+
}
|
|
3753
|
+
return;
|
|
3754
|
+
}
|
|
3755
|
+
const apiVer = data.hostPluginApiVersion;
|
|
3756
|
+
if (apiVer) {
|
|
3757
|
+
const coerced = semver.coerce(apiVer);
|
|
3758
|
+
const maj = coerced ? coerced.major : 0;
|
|
3759
|
+
const range = `^${maj}.0.0`;
|
|
3760
|
+
if (!semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3761
|
+
console.warn('[wep] host API version mismatch', {
|
|
3762
|
+
host: HOST_PLUGIN_API_VERSION,
|
|
3763
|
+
manifest: apiVer
|
|
3764
|
+
});
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
window.__PLUGIN_ACTIVATORS__ = window.__PLUGIN_ACTIVATORS__ || {};
|
|
3768
|
+
const plugins = data.plugins || [];
|
|
3769
|
+
if (plugins.length === 0) {
|
|
3770
|
+
const hint = isStatic ? 'check static JSON file and plugins[]' : 'check backend and URL';
|
|
3771
|
+
console.info('[wep] empty plugin manifest — ' + hint, manifestUrlUsed);
|
|
3772
|
+
}
|
|
3773
|
+
const summary = {
|
|
3774
|
+
manifestCount: plugins.length,
|
|
3775
|
+
activated: 0,
|
|
3776
|
+
skipEngines: 0,
|
|
3777
|
+
skipLoad: 0,
|
|
3778
|
+
skipNoActivator: 0,
|
|
3779
|
+
activateFail: 0
|
|
3780
|
+
};
|
|
3781
|
+
for (const p of plugins) {
|
|
3782
|
+
const range = p.engines && p.engines.host;
|
|
3783
|
+
if (range && !semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3784
|
+
console.warn('[wep] skip plugin (engines.host)', p.id, range);
|
|
3785
|
+
summary.skipEngines++;
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const entryUrl = p.entryUrl;
|
|
3789
|
+
try {
|
|
3790
|
+
await loadPluginEntry(p, entryUrl, devMap, hostSet);
|
|
3791
|
+
}
|
|
3792
|
+
catch (e) {
|
|
3793
|
+
console.warn('[wep] script load failed', p.id, e);
|
|
3794
|
+
summary.skipLoad++;
|
|
3795
|
+
continue;
|
|
3796
|
+
}
|
|
3797
|
+
const activator = window.__PLUGIN_ACTIVATORS__[p.id];
|
|
3798
|
+
if (typeof activator !== 'function') {
|
|
3799
|
+
console.warn('[wep] no activator for', p.id);
|
|
3800
|
+
summary.skipNoActivator++;
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
const pluginRecord = Object.freeze({ ...p });
|
|
3804
|
+
try {
|
|
3805
|
+
if (typeof opts.onBeforePluginActivate === 'function') {
|
|
3806
|
+
await Promise.resolve(opts.onBeforePluginActivate({
|
|
3807
|
+
pluginId: p.id,
|
|
3808
|
+
router,
|
|
3809
|
+
pluginRecord
|
|
3810
|
+
}));
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
catch (e) {
|
|
3814
|
+
console.warn('[wep] activate skipped (onBeforePluginActivate)', p.id, e);
|
|
3815
|
+
summary.activateFail++;
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
const hostApi = createHostApiFactory(p.id, router, hostKit);
|
|
3819
|
+
try {
|
|
3820
|
+
await Promise.resolve(activator(hostApi, { pluginRecord }));
|
|
3821
|
+
summary.activated++;
|
|
3822
|
+
if (typeof opts.onAfterPluginActivate === 'function') {
|
|
3823
|
+
await Promise.resolve(opts.onAfterPluginActivate({
|
|
3824
|
+
pluginId: p.id,
|
|
3825
|
+
router,
|
|
3826
|
+
pluginRecord,
|
|
3827
|
+
hostApi
|
|
3828
|
+
}));
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
catch (e) {
|
|
3832
|
+
console.error('[wep] activate failed', p.id, e);
|
|
3833
|
+
summary.activateFail++;
|
|
3834
|
+
if (typeof opts.onPluginActivateError === 'function') {
|
|
3835
|
+
try {
|
|
3836
|
+
await Promise.resolve(opts.onPluginActivateError({
|
|
3837
|
+
pluginId: p.id,
|
|
3838
|
+
error: e,
|
|
3839
|
+
pluginRecord,
|
|
3840
|
+
hostApi
|
|
3841
|
+
}));
|
|
3842
|
+
}
|
|
3843
|
+
catch (hookErr) {
|
|
3844
|
+
console.warn('[wep] onPluginActivateError hook failed', p.id, hookErr);
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3850
|
+
console.info('[wep] bootstrap_summary', { ok: true, ...summary });
|
|
3624
3851
|
}
|
|
3625
|
-
}
|
|
3626
|
-
|
|
3627
|
-
if (shouldShowBootstrapSummary(opts)) {
|
|
3628
|
-
console.info('[plugins] bootstrap_summary', { ok: true, ...summary });
|
|
3629
|
-
}
|
|
3630
3852
|
}
|
|
3631
3853
|
|
|
3632
3854
|
/**
|
|
3633
|
-
*
|
|
3634
|
-
* 实现已拆至 `src/runtime/` 下各模块;本文件保留模块说明与对外类型约定,并 re-export 稳定 API。
|
|
3635
|
-
*
|
|
3636
|
-
* **Webpack 宿主**:用 `DefinePlugin` 注入 `process.env.VITE_*` 或 **`PLUGIN_*`**(等价键),或 `resolveRuntimeOptions` 显式传参。
|
|
3637
|
-
* **Vite 宿主**:入口调用 `setWebExtendPluginEnv(import.meta.env)`,或 `installWebExtendPluginVue2(..., { env: import.meta.env })`。
|
|
3638
|
-
*
|
|
3639
|
-
* @module PluginRuntime
|
|
3855
|
+
* 运行时引导相关 API 的聚合导出。
|
|
3640
3856
|
*/
|
|
3641
3857
|
|
|
3642
3858
|
var pluginRuntime = /*#__PURE__*/Object.freeze({
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3859
|
+
__proto__: null,
|
|
3860
|
+
bootstrapPlugins: bootstrapPlugins$1,
|
|
3861
|
+
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
3862
|
+
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
3863
|
+
resolveRuntimeOptions: resolveRuntimeOptions$1
|
|
3647
3864
|
});
|
|
3648
3865
|
|
|
3649
3866
|
/**
|
|
3650
3867
|
* 清单拉取函数的组合工具:缓存、埋点等以**中间件**形式扩展,不侵入 `bootstrapPlugins` 核心逻辑,
|
|
3651
|
-
*
|
|
3652
|
-
*
|
|
3653
|
-
* @module runtime/manifest-fetch-composer
|
|
3654
|
-
*/
|
|
3655
|
-
|
|
3656
|
-
/**
|
|
3657
|
-
* @typedef {object} FetchWebPluginManifestContext
|
|
3658
|
-
* @property {string} manifestUrl
|
|
3659
|
-
* @property {RequestCredentials} credentials
|
|
3660
|
-
*/
|
|
3661
|
-
|
|
3662
|
-
/**
|
|
3663
|
-
* @typedef {object} FetchWebPluginManifestResult
|
|
3664
|
-
* @property {boolean} ok
|
|
3665
|
-
* @property {number} [status]
|
|
3666
|
-
* @property {{ hostPluginApiVersion?: string, plugins?: object[] }|null} [data]
|
|
3667
|
-
* @property {unknown} [error]
|
|
3668
|
-
*/
|
|
3669
|
-
|
|
3670
|
-
/**
|
|
3671
|
-
* @callback FetchWebPluginManifestFn
|
|
3672
|
-
* @param {FetchWebPluginManifestContext} ctx
|
|
3673
|
-
* @returns {Promise<FetchWebPluginManifestResult>}
|
|
3674
|
-
*/
|
|
3675
|
-
|
|
3676
|
-
/**
|
|
3677
|
-
* 将内层 `fetchManifest` 包装为带缓存的版本。等价于
|
|
3678
|
-
* `composeManifestFetch(inner, manifestFetchCacheMiddleware(options))`。
|
|
3679
|
-
*
|
|
3680
|
-
* @param {FetchWebPluginManifestFn} inner 内层实现(如预设生成的 `fetchManifest` 或 `defaultFetchWebPluginManifest`)
|
|
3681
|
-
* @param {ManifestFetchCacheOptions} [options]
|
|
3682
|
-
* @returns {FetchWebPluginManifestFn}
|
|
3868
|
+
* 可组合的 `fetchManifest` 包装;入参/出参与 `resolveRuntimeOptions({ fetchManifest })` 一致。
|
|
3683
3869
|
*/
|
|
3684
3870
|
function wrapManifestFetchWithCache$1(inner, options = {}) {
|
|
3685
|
-
|
|
3871
|
+
return composeManifestFetch$1(inner, manifestFetchCacheMiddleware$1(options));
|
|
3686
3872
|
}
|
|
3687
|
-
|
|
3688
|
-
/**
|
|
3689
|
-
* @typedef {object} ManifestFetchCacheOptions
|
|
3690
|
-
* @property {number} [ttlMs] 缓存时长(毫秒)。`<= 0` 或未传时**不缓存**(直接透传 `inner`)。
|
|
3691
|
-
* @property {'memory'|'session'|'local'} [storage='memory'] `session`/`local` 依赖 `JSON.stringify`,仅适合可序列化的 `data`。
|
|
3692
|
-
* @property {string} [storageKeyPrefix='wep.manifestFetch.v1'] Web Storage 键前缀(仅 storage 非 memory 时生效)。
|
|
3693
|
-
* @property {(ctx: FetchWebPluginManifestContext) => string} [cacheKey] 默认 `manifestUrl + '\0' + credentials`。
|
|
3694
|
-
* @property {(result: FetchWebPluginManifestResult) => boolean} [shouldCache] 默认:`ok === true` 且 `data` 非空。
|
|
3695
|
-
* @property {number} [maxEntries=50] 仅 `memory`:超过条数时淘汰最久未读条目。
|
|
3696
|
-
* @property {() => number} [now] 测试注入时间戳。
|
|
3697
|
-
*/
|
|
3698
|
-
|
|
3699
|
-
/**
|
|
3700
|
-
* 清单拉取**中间件工厂**:`next` 为内层 `fetchManifest`。
|
|
3701
|
-
*
|
|
3702
|
-
* @param {ManifestFetchCacheOptions} options
|
|
3703
|
-
* @returns {(next: FetchWebPluginManifestFn) => FetchWebPluginManifestFn}
|
|
3704
|
-
*/
|
|
3705
3873
|
function manifestFetchCacheMiddleware$1(options = {}) {
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
}
|
|
3710
|
-
|
|
3711
|
-
const storage = options.storage || 'memory';
|
|
3712
|
-
const prefix = options.storageKeyPrefix || 'wep.manifestFetch.v1';
|
|
3713
|
-
const maxEntries = typeof options.maxEntries === 'number' && options.maxEntries > 0 ? options.maxEntries : 50;
|
|
3714
|
-
const getNow = typeof options.now === 'function' ? options.now : () => Date.now();
|
|
3715
|
-
const cacheKeyFn =
|
|
3716
|
-
typeof options.cacheKey === 'function'
|
|
3717
|
-
? options.cacheKey
|
|
3718
|
-
: (ctx) => `${String(ctx.manifestUrl)}\0${String(ctx.credentials)}`;
|
|
3719
|
-
|
|
3720
|
-
const shouldCache =
|
|
3721
|
-
typeof options.shouldCache === 'function'
|
|
3722
|
-
? options.shouldCache
|
|
3723
|
-
: (r) => !!(r && r.ok === true && r.data != null);
|
|
3724
|
-
|
|
3725
|
-
/** @type {Map<string, { expiresAt: number, result: FetchWebPluginManifestResult, lastRead: number }>} */
|
|
3726
|
-
const memory = new Map();
|
|
3727
|
-
|
|
3728
|
-
function cloneResult(r) {
|
|
3729
|
-
try {
|
|
3730
|
-
if (typeof structuredClone === 'function') {
|
|
3731
|
-
return structuredClone(r)
|
|
3732
|
-
}
|
|
3733
|
-
} catch (_) {}
|
|
3734
|
-
try {
|
|
3735
|
-
return JSON.parse(JSON.stringify(r))
|
|
3736
|
-
} catch (_) {
|
|
3737
|
-
return { ...r, data: r.data }
|
|
3738
|
-
}
|
|
3739
|
-
}
|
|
3740
|
-
|
|
3741
|
-
function touchMemory(key) {
|
|
3742
|
-
const e = memory.get(key);
|
|
3743
|
-
if (e) {
|
|
3744
|
-
e.lastRead = getNow();
|
|
3745
|
-
}
|
|
3746
|
-
}
|
|
3747
|
-
|
|
3748
|
-
function pruneMemory() {
|
|
3749
|
-
if (memory.size <= maxEntries) {
|
|
3750
|
-
return
|
|
3874
|
+
const ttlMs = typeof options.ttlMs === 'number' && Number.isFinite(options.ttlMs) ? options.ttlMs : 0;
|
|
3875
|
+
if (ttlMs <= 0) {
|
|
3876
|
+
return (next) => next;
|
|
3751
3877
|
}
|
|
3752
|
-
const
|
|
3753
|
-
const
|
|
3754
|
-
|
|
3755
|
-
|
|
3878
|
+
const storage = options.storage || 'memory';
|
|
3879
|
+
const prefix = options.storageKeyPrefix || defaultManifestFetchCache.storageKeyPrefix;
|
|
3880
|
+
const maxEntries = typeof options.maxEntries === 'number' && options.maxEntries > 0
|
|
3881
|
+
? options.maxEntries
|
|
3882
|
+
: defaultManifestFetchCache.maxEntries;
|
|
3883
|
+
const getNow = typeof options.now === 'function' ? options.now : () => Date.now();
|
|
3884
|
+
const cacheKeyFn = typeof options.cacheKey === 'function'
|
|
3885
|
+
? options.cacheKey
|
|
3886
|
+
: (ctx) => `${String(ctx.manifestUrl)}\0${String(ctx.credentials)}`;
|
|
3887
|
+
const shouldCache = typeof options.shouldCache === 'function'
|
|
3888
|
+
? options.shouldCache
|
|
3889
|
+
: (r) => !!(r && r.ok === true && r.data != null);
|
|
3890
|
+
const memory = new Map();
|
|
3891
|
+
function cloneResult(r) {
|
|
3892
|
+
try {
|
|
3893
|
+
if (typeof structuredClone === 'function') {
|
|
3894
|
+
return structuredClone(r);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
catch {
|
|
3898
|
+
/* ignore */
|
|
3899
|
+
}
|
|
3900
|
+
try {
|
|
3901
|
+
return JSON.parse(JSON.stringify(r));
|
|
3902
|
+
}
|
|
3903
|
+
catch {
|
|
3904
|
+
return { ...r, data: r.data };
|
|
3905
|
+
}
|
|
3756
3906
|
}
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
if (!raw) {
|
|
3763
|
-
return null
|
|
3764
|
-
}
|
|
3765
|
-
const o = JSON.parse(raw);
|
|
3766
|
-
if (!o || typeof o !== 'object') {
|
|
3767
|
-
return null
|
|
3768
|
-
}
|
|
3769
|
-
const exp = o.expiresAt;
|
|
3770
|
-
const res = o.result;
|
|
3771
|
-
if (typeof exp !== 'number' || getNow() > exp) {
|
|
3772
|
-
store.removeItem(key);
|
|
3773
|
-
return null
|
|
3774
|
-
}
|
|
3775
|
-
return /** @type {FetchWebPluginManifestResult} */ (res)
|
|
3776
|
-
} catch (_) {
|
|
3777
|
-
return null
|
|
3907
|
+
function touchMemory(key) {
|
|
3908
|
+
const e = memory.get(key);
|
|
3909
|
+
if (e) {
|
|
3910
|
+
e.lastRead = getNow();
|
|
3911
|
+
}
|
|
3778
3912
|
}
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3913
|
+
function pruneMemory() {
|
|
3914
|
+
if (memory.size <= maxEntries) {
|
|
3915
|
+
return;
|
|
3916
|
+
}
|
|
3917
|
+
const entries = [...memory.entries()].sort((a, b) => a[1].lastRead - b[1].lastRead);
|
|
3918
|
+
const drop = memory.size - maxEntries;
|
|
3919
|
+
for (let i = 0; i < drop; i++) {
|
|
3920
|
+
memory.delete(entries[i][0]);
|
|
3921
|
+
}
|
|
3786
3922
|
}
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3923
|
+
function readWebStorage(store, key) {
|
|
3924
|
+
try {
|
|
3925
|
+
const raw = store.getItem(key);
|
|
3926
|
+
if (!raw) {
|
|
3927
|
+
return null;
|
|
3928
|
+
}
|
|
3929
|
+
const o = JSON.parse(raw);
|
|
3930
|
+
if (!o || typeof o !== 'object') {
|
|
3931
|
+
return null;
|
|
3932
|
+
}
|
|
3933
|
+
const exp = o.expiresAt;
|
|
3934
|
+
const res = o.result;
|
|
3935
|
+
if (typeof exp !== 'number' || getNow() > exp) {
|
|
3936
|
+
store.removeItem(key);
|
|
3937
|
+
return null;
|
|
3938
|
+
}
|
|
3939
|
+
return res;
|
|
3940
|
+
}
|
|
3941
|
+
catch {
|
|
3942
|
+
return null;
|
|
3799
3943
|
}
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
return cloneResult(hit)
|
|
3944
|
+
}
|
|
3945
|
+
function writeWebStorage(store, key, result, expiresAt) {
|
|
3946
|
+
try {
|
|
3947
|
+
store.setItem(key, JSON.stringify({ expiresAt, result }));
|
|
3805
3948
|
}
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
const hit = readWebStorage(localStorage, sk);
|
|
3809
|
-
if (hit) {
|
|
3810
|
-
return cloneResult(hit)
|
|
3949
|
+
catch {
|
|
3950
|
+
/* Quota / 不可序列化 */
|
|
3811
3951
|
}
|
|
3812
|
-
}
|
|
3813
|
-
|
|
3814
|
-
const result = await next(ctx);
|
|
3815
|
-
|
|
3816
|
-
if (!shouldCache(result)) {
|
|
3817
|
-
return result
|
|
3818
|
-
}
|
|
3819
|
-
|
|
3820
|
-
const expiresAt = now + ttlMs;
|
|
3821
|
-
const frozen = cloneResult(result);
|
|
3822
|
-
|
|
3823
|
-
if (storage === 'memory') {
|
|
3824
|
-
memory.set(key, { expiresAt, result: frozen, lastRead: now });
|
|
3825
|
-
pruneMemory();
|
|
3826
|
-
} else if (storage === 'session' && typeof sessionStorage !== 'undefined') {
|
|
3827
|
-
writeWebStorage(sessionStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3828
|
-
} else if (storage === 'local' && typeof localStorage !== 'undefined') {
|
|
3829
|
-
writeWebStorage(localStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3830
|
-
}
|
|
3831
|
-
|
|
3832
|
-
return result
|
|
3833
3952
|
}
|
|
3834
|
-
|
|
3953
|
+
return (next) => {
|
|
3954
|
+
return async (ctx) => {
|
|
3955
|
+
const key = cacheKeyFn(ctx);
|
|
3956
|
+
const now = getNow();
|
|
3957
|
+
if (storage === 'memory') {
|
|
3958
|
+
const hit = memory.get(key);
|
|
3959
|
+
if (hit && hit.expiresAt > now) {
|
|
3960
|
+
touchMemory(key);
|
|
3961
|
+
return cloneResult(hit.result);
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
else if (storage === 'session' && typeof sessionStorage !== 'undefined') {
|
|
3965
|
+
const sk = `${prefix}:${key}`;
|
|
3966
|
+
const hit = readWebStorage(sessionStorage, sk);
|
|
3967
|
+
if (hit) {
|
|
3968
|
+
return cloneResult(hit);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
else if (storage === 'local' && typeof localStorage !== 'undefined') {
|
|
3972
|
+
const sk = `${prefix}:${key}`;
|
|
3973
|
+
const hit = readWebStorage(localStorage, sk);
|
|
3974
|
+
if (hit) {
|
|
3975
|
+
return cloneResult(hit);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
const result = await next(ctx);
|
|
3979
|
+
if (!shouldCache(result)) {
|
|
3980
|
+
return result;
|
|
3981
|
+
}
|
|
3982
|
+
const expiresAt = now + ttlMs;
|
|
3983
|
+
const frozen = cloneResult(result);
|
|
3984
|
+
if (storage === 'memory') {
|
|
3985
|
+
memory.set(key, { expiresAt, result: frozen, lastRead: now });
|
|
3986
|
+
pruneMemory();
|
|
3987
|
+
}
|
|
3988
|
+
else if (storage === 'session' && typeof sessionStorage !== 'undefined') {
|
|
3989
|
+
writeWebStorage(sessionStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3990
|
+
}
|
|
3991
|
+
else if (storage === 'local' && typeof localStorage !== 'undefined') {
|
|
3992
|
+
writeWebStorage(localStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3993
|
+
}
|
|
3994
|
+
return result;
|
|
3995
|
+
};
|
|
3996
|
+
};
|
|
3835
3997
|
}
|
|
3836
|
-
|
|
3837
|
-
/**
|
|
3838
|
-
* 自右向左组合中间件(与 Koa/Redux 习惯一致:`compose(f,g,h)(inner)` = f(g(h(inner))))。
|
|
3839
|
-
*
|
|
3840
|
-
* @param {FetchWebPluginManifestFn} inner 最内层拉取实现
|
|
3841
|
-
* @param {...function(FetchWebPluginManifestFn): FetchWebPluginManifestFn} middlewares 每个元素签名 `(next) => async (ctx) => result`
|
|
3842
|
-
* @returns {FetchWebPluginManifestFn}
|
|
3843
|
-
*/
|
|
3844
3998
|
function composeManifestFetch$1(inner, ...middlewares) {
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
}
|
|
3848
|
-
let f = inner;
|
|
3849
|
-
for (let i = middlewares.length - 1; i >= 0; i--) {
|
|
3850
|
-
const mw = middlewares[i];
|
|
3851
|
-
if (typeof mw !== 'function') {
|
|
3852
|
-
throw new Error('[web-extend-plugin-vue2] composeManifestFetch 中间件须为函数')
|
|
3999
|
+
if (typeof inner !== 'function') {
|
|
4000
|
+
throw new Error('[web-extend-plugin-vue2] composeManifestFetch 需要 inner 为函数');
|
|
3853
4001
|
}
|
|
3854
|
-
f =
|
|
3855
|
-
|
|
3856
|
-
|
|
4002
|
+
let f = inner;
|
|
4003
|
+
for (let i = middlewares.length - 1; i >= 0; i--) {
|
|
4004
|
+
const mw = middlewares[i];
|
|
4005
|
+
if (typeof mw !== 'function') {
|
|
4006
|
+
throw new Error('[web-extend-plugin-vue2] composeManifestFetch 中间件须为函数');
|
|
4007
|
+
}
|
|
4008
|
+
f = mw(f);
|
|
4009
|
+
}
|
|
4010
|
+
return f;
|
|
3857
4011
|
}
|
|
3858
4012
|
|
|
3859
4013
|
var manifestComposer = /*#__PURE__*/Object.freeze({
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
4014
|
+
__proto__: null,
|
|
4015
|
+
composeManifestFetch: composeManifestFetch$1,
|
|
4016
|
+
manifestFetchCacheMiddleware: manifestFetchCacheMiddleware$1,
|
|
4017
|
+
wrapManifestFetchWithCache: wrapManifestFetchWithCache$1
|
|
3864
4018
|
});
|
|
3865
4019
|
|
|
3866
|
-
/**
|
|
3867
|
-
*
|
|
3868
|
-
*
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
/**
|
|
3874
|
-
* @param {{ allowedPathPrefixes?: string[] }} [config]
|
|
3875
|
-
*/
|
|
3876
|
-
function createRequestBridge(config = {}) {
|
|
3877
|
-
const raw =
|
|
3878
|
-
Array.isArray(config.allowedPathPrefixes) && config.allowedPathPrefixes.length > 0
|
|
3879
|
-
? config.allowedPathPrefixes
|
|
3880
|
-
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
3881
|
-
const allowedPathPrefixes = raw.map((p) => ensureLeadingPath(p));
|
|
3882
|
-
|
|
3883
|
-
return {
|
|
3884
|
-
/**
|
|
3885
|
-
* 发起受控 `fetch`。
|
|
3886
|
-
* @param {string} path 必须以 `/` 开头,且匹配某一 `allowedPathPrefixes` 前缀
|
|
3887
|
-
* @param {RequestInit} [init] 会与默认 `{ credentials: 'same-origin' }` 合并(后者可被覆盖)
|
|
3888
|
-
*/
|
|
3889
|
-
async request(path, init = {}) {
|
|
3890
|
-
if (typeof path !== 'string' || !path.startsWith('/')) {
|
|
3891
|
-
throw new Error('[bridge] path must be a string starting with /')
|
|
3892
|
-
}
|
|
3893
|
-
const allowed = allowedPathPrefixes.some((p) => path.startsWith(p));
|
|
3894
|
-
if (!allowed) {
|
|
3895
|
-
throw new Error('[bridge] path not allowed: ' + path)
|
|
3896
|
-
}
|
|
3897
|
-
return fetch(path, {
|
|
3898
|
-
credentials: 'same-origin',
|
|
3899
|
-
...init
|
|
3900
|
-
})
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
}
|
|
3904
|
-
|
|
3905
|
-
/**
|
|
3906
|
-
* 宿主全局响应式注册表:菜单与扩展点槽位,供布局与 `ExtensionPoint` 订阅。
|
|
3907
|
-
*
|
|
3908
|
-
* @module registries
|
|
3909
|
-
*/
|
|
3910
|
-
|
|
3911
|
-
/**
|
|
3912
|
-
* @type {{
|
|
3913
|
-
* menus: object[],
|
|
3914
|
-
* slots: Record<string, Array<{ pluginId: string, component: import('vue').Component, priority: number, key: string }>>
|
|
3915
|
-
* }}
|
|
3916
|
-
*/
|
|
3917
|
-
const registries = Vue__default.default.observable({
|
|
3918
|
-
menus: [],
|
|
3919
|
-
/** 扩展点 id → 已注册组件列表(内容区 / 工具栏等共用模型) */
|
|
3920
|
-
slots: {},
|
|
3921
|
-
/**
|
|
3922
|
-
* 每次变更 slots 时递增,供 ExtensionPoint 计算属性显式依赖。
|
|
3923
|
-
* Vue 2 对「先访问不存在的 slots[key]、后 Vue.set 补 key」的依赖收集不可靠,会导致扩展点不刷新。
|
|
3924
|
-
*/
|
|
3925
|
-
slotRevision: 0
|
|
4020
|
+
/**
|
|
4021
|
+
* 宿主侧响应式注册表:扩展点槽位(供布局与 `ExtensionPoint` 消费)。
|
|
4022
|
+
* 菜单数据由宿主在 `applyPluginMenuItems` 中自行并入其路由/菜单 state,框架不维护平行菜单列表。
|
|
4023
|
+
*/
|
|
4024
|
+
const registries = Vue__default.default.observable({
|
|
4025
|
+
slots: {},
|
|
4026
|
+
slotRevision: 0
|
|
3926
4027
|
});
|
|
3927
4028
|
|
|
3928
|
-
/**
|
|
3929
|
-
*
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
function registerPluginTeardown(pluginId, fn) {
|
|
3943
|
-
if (typeof fn !== 'function') {
|
|
3944
|
-
return
|
|
3945
|
-
}
|
|
3946
|
-
let arr = byPlugin.get(pluginId);
|
|
3947
|
-
if (!arr) {
|
|
3948
|
-
arr = [];
|
|
3949
|
-
byPlugin.set(pluginId, arr);
|
|
3950
|
-
}
|
|
3951
|
-
arr.push(fn);
|
|
3952
|
-
}
|
|
3953
|
-
|
|
3954
|
-
/**
|
|
3955
|
-
* 执行并清空该插件已登记的全部 teardown;调用后 Map 中不再保留该 id。
|
|
3956
|
-
* @param {string} pluginId
|
|
3957
|
-
*/
|
|
3958
|
-
function runPluginTeardowns(pluginId) {
|
|
3959
|
-
const arr = byPlugin.get(pluginId);
|
|
3960
|
-
if (!arr) {
|
|
3961
|
-
return
|
|
3962
|
-
}
|
|
3963
|
-
byPlugin.delete(pluginId);
|
|
3964
|
-
for (const fn of arr) {
|
|
3965
|
-
try {
|
|
3966
|
-
fn();
|
|
3967
|
-
} catch (e) {
|
|
3968
|
-
console.warn('[plugins] teardown failed', pluginId, e);
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
4029
|
+
/**
|
|
4030
|
+
* 按插件 id 收集 `onTeardown` 回调,供 `disposeWebPlugin` 统一执行。
|
|
4031
|
+
*/
|
|
4032
|
+
const _byPlugin = new Map();
|
|
4033
|
+
function registerPluginTeardown(pluginId, fn) {
|
|
4034
|
+
if (typeof fn !== 'function') {
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
let list = _byPlugin.get(pluginId);
|
|
4038
|
+
if (!list) {
|
|
4039
|
+
list = [];
|
|
4040
|
+
_byPlugin.set(pluginId, list);
|
|
4041
|
+
}
|
|
4042
|
+
list.push(fn);
|
|
3971
4043
|
}
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
* @typedef {object} RegisterSlotEntry
|
|
3987
|
-
* @property {import('vue').Component} component
|
|
3988
|
-
* @property {number} [priority]
|
|
3989
|
-
*/
|
|
3990
|
-
|
|
3991
|
-
/**
|
|
3992
|
-
* @typedef {object} HostApi
|
|
3993
|
-
* @property {string} hostPluginApiVersion 宿主实现的协议版本
|
|
3994
|
-
* @property {(routes: import('vue-router').RouteConfig[]) => void} registerRoutes 注册路由;无 `name` 时自动生成稳定合成名;优先 `router.addRoute`
|
|
3995
|
-
* @property {(items: object[]) => void} registerMenuItems 注册菜单并按 `order` 排序
|
|
3996
|
-
* @property {(pointId: string, components: RegisterSlotEntry[]) => void} registerSlotComponents 向扩展点挂载组件
|
|
3997
|
-
* @property {(urls?: string[]) => void} registerStylesheetUrls 注入 `link[rel=stylesheet]`,带 `data-plugin-asset`
|
|
3998
|
-
* @property {(urls?: string[]) => void} registerScriptUrls 顺序注入外链脚本
|
|
3999
|
-
* @property {() => void} registerSanitizedHtmlSnippet MVP 未实现,调用即抛错
|
|
4000
|
-
* @property {() => ReturnType<typeof createRequestBridge>} getBridge 受控 `fetch` 代理
|
|
4001
|
-
* @property {(pluginId: string, fn: () => void) => void} onTeardown 注册卸载回调;由 `disposeWebPlugin(pluginId)` 触发
|
|
4002
|
-
*/
|
|
4003
|
-
|
|
4004
|
-
/**
|
|
4005
|
-
* @typedef {object} HostKitOptions
|
|
4006
|
-
* @property {string[]} [bridgeAllowedPathPrefixes] 覆盖 `getBridge().request` 允许的 URL 前缀;默认见 `defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes`
|
|
4007
|
-
*/
|
|
4008
|
-
|
|
4009
|
-
/**
|
|
4010
|
-
* 创建单个插件在宿主侧的 API 句柄,传入插件 `activator(hostApi)`。
|
|
4011
|
-
*
|
|
4012
|
-
* `bootstrapPlugins` 始终以 `(pluginId, router, hostKitOptions)` 调用工厂;请使用 `(id, r, kit) => createHostApi(id, r, kit)` 传入 `bridgeAllowedPathPrefixes`。
|
|
4013
|
-
* 单参工厂 `(id) => createHostApi(id, router)` 仍可用(忽略后两个实参),此时 bridge 仅用包内默认前缀。
|
|
4014
|
-
*
|
|
4015
|
-
* @param {string} pluginId 与 manifest.id 一致
|
|
4016
|
-
* @param {import('vue-router').default} router 宿主 Vue Router 实例(vue-router@3)
|
|
4017
|
-
* @param {HostKitOptions} [hostKitOptions]
|
|
4018
|
-
* @returns {HostApi}
|
|
4019
|
-
*/
|
|
4020
|
-
function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
4021
|
-
const bridgePrefixes =
|
|
4022
|
-
Array.isArray(hostKitOptions.bridgeAllowedPathPrefixes) &&
|
|
4023
|
-
hostKitOptions.bridgeAllowedPathPrefixes.length > 0
|
|
4024
|
-
? hostKitOptions.bridgeAllowedPathPrefixes
|
|
4025
|
-
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
4026
|
-
const bridge = createRequestBridge({ allowedPathPrefixes: bridgePrefixes });
|
|
4027
|
-
|
|
4028
|
-
/**
|
|
4029
|
-
* 注入样式表;`disposeWebPlugin` 会按 `data-plugin-asset` 移除对应节点。
|
|
4030
|
-
* @param {string} href
|
|
4031
|
-
*/
|
|
4032
|
-
function injectStylesheet(href) {
|
|
4033
|
-
const link = document.createElement('link');
|
|
4034
|
-
link.rel = 'stylesheet';
|
|
4035
|
-
link.href = href;
|
|
4036
|
-
link.setAttribute('data-plugin-asset', pluginId);
|
|
4037
|
-
document.head.appendChild(link);
|
|
4038
|
-
}
|
|
4039
|
-
|
|
4040
|
-
/**
|
|
4041
|
-
* 注入外链脚本(用于插件额外资源,非清单主入口)。
|
|
4042
|
-
* @param {string} src
|
|
4043
|
-
* @returns {Promise<void>}
|
|
4044
|
-
*/
|
|
4045
|
-
function injectScript(src) {
|
|
4046
|
-
return new Promise((resolve, reject) => {
|
|
4047
|
-
const s = document.createElement('script');
|
|
4048
|
-
s.async = true;
|
|
4049
|
-
s.src = src;
|
|
4050
|
-
s.setAttribute('data-plugin-asset', pluginId);
|
|
4051
|
-
s.onload = () => resolve();
|
|
4052
|
-
s.onerror = () => reject(new Error('script failed: ' + src));
|
|
4053
|
-
document.head.appendChild(s);
|
|
4054
|
-
})
|
|
4055
|
-
}
|
|
4056
|
-
|
|
4057
|
-
return {
|
|
4058
|
-
hostPluginApiVersion: HOST_PLUGIN_API_VERSION,
|
|
4059
|
-
|
|
4060
|
-
/**
|
|
4061
|
-
* 动态注册路由。Vue Router 3.5+ 推荐 `addRoute`;若不存在则回退已弃用的 `addRoutes`。
|
|
4062
|
-
* @param {import('vue-router').RouteConfig[]} routes
|
|
4063
|
-
*/
|
|
4064
|
-
registerRoutes(routes) {
|
|
4065
|
-
const wrapped = routes.map((r) => ({
|
|
4066
|
-
...r,
|
|
4067
|
-
name: r.name || `__wep_${pluginId}_${routeSynthSeq++}`,
|
|
4068
|
-
meta: { ...(r.meta || {}), pluginId }
|
|
4069
|
-
}));
|
|
4070
|
-
if (typeof router.addRoute === 'function') {
|
|
4071
|
-
for (const r of wrapped) {
|
|
4072
|
-
router.addRoute(r);
|
|
4073
|
-
}
|
|
4074
|
-
} else {
|
|
4075
|
-
router.addRoutes(wrapped);
|
|
4076
|
-
}
|
|
4077
|
-
},
|
|
4078
|
-
|
|
4079
|
-
/**
|
|
4080
|
-
* 写入全局菜单注册表(响应式);按 `order` 升序排列。
|
|
4081
|
-
* @param {object[]} items
|
|
4082
|
-
*/
|
|
4083
|
-
registerMenuItems(items) {
|
|
4084
|
-
for (const item of items) {
|
|
4085
|
-
registries.menus.push({ ...item, pluginId });
|
|
4086
|
-
}
|
|
4087
|
-
registries.menus.sort(
|
|
4088
|
-
(a, b) => (a.order != null ? a.order : 0) - (b.order != null ? b.order : 0)
|
|
4089
|
-
);
|
|
4090
|
-
},
|
|
4091
|
-
|
|
4092
|
-
/**
|
|
4093
|
-
* 向指定扩展点 id 注册 Vue 组件;`ExtensionPoint` 按 `priority` 降序渲染。
|
|
4094
|
-
* @param {string} pointId
|
|
4095
|
-
* @param {RegisterSlotEntry[]} components
|
|
4096
|
-
*/
|
|
4097
|
-
registerSlotComponents(pointId, components) {
|
|
4098
|
-
if (!pointId) {
|
|
4099
|
-
return
|
|
4100
|
-
}
|
|
4101
|
-
if (!registries.slots[pointId]) {
|
|
4102
|
-
Vue__default.default.set(registries.slots, pointId, []);
|
|
4103
|
-
}
|
|
4104
|
-
const list = registries.slots[pointId];
|
|
4105
|
-
for (const c of components) {
|
|
4106
|
-
list.push({
|
|
4107
|
-
pluginId,
|
|
4108
|
-
component: c.component,
|
|
4109
|
-
priority: c.priority != null ? c.priority : 0,
|
|
4110
|
-
key: `${pluginId}-${pointId}-${++slotItemKeySeq}`
|
|
4111
|
-
});
|
|
4112
|
-
}
|
|
4113
|
-
list.sort((a, b) => b.priority - a.priority);
|
|
4114
|
-
registries.slotRevision++;
|
|
4115
|
-
},
|
|
4116
|
-
|
|
4117
|
-
/**
|
|
4118
|
-
* @param {string[]|undefined} urls
|
|
4119
|
-
*/
|
|
4120
|
-
registerStylesheetUrls(urls) {
|
|
4121
|
-
for (const u of urls || []) {
|
|
4122
|
-
if (typeof u === 'string' && u) {
|
|
4123
|
-
injectStylesheet(u);
|
|
4124
|
-
}
|
|
4125
|
-
}
|
|
4126
|
-
},
|
|
4127
|
-
|
|
4128
|
-
/**
|
|
4129
|
-
* 串行加载多个脚本,失败仅告警不中断宿主。
|
|
4130
|
-
* @param {string[]|undefined} urls
|
|
4131
|
-
*/
|
|
4132
|
-
registerScriptUrls(urls) {
|
|
4133
|
-
const chain = (urls || []).filter((u) => typeof u === 'string' && u).reduce(
|
|
4134
|
-
(p, u) => p.then(() => injectScript(u)),
|
|
4135
|
-
Promise.resolve()
|
|
4136
|
-
);
|
|
4137
|
-
chain.catch((e) => console.warn('[plugins] registerScriptUrls', pluginId, e));
|
|
4138
|
-
},
|
|
4139
|
-
|
|
4140
|
-
registerSanitizedHtmlSnippet() {
|
|
4141
|
-
throw new Error('registerSanitizedHtmlSnippet is not enabled in MVP')
|
|
4142
|
-
},
|
|
4143
|
-
|
|
4144
|
-
getBridge: () => bridge,
|
|
4145
|
-
|
|
4146
|
-
/**
|
|
4147
|
-
* 插件卸载前清理逻辑;第一个参数为预留与协议对齐,实际以创建 API 时的 `pluginId` 为准。
|
|
4148
|
-
* @param {string} _pluginId 预留,与 manifest.id 一致时可传入
|
|
4149
|
-
* @param {() => void} fn
|
|
4150
|
-
*/
|
|
4151
|
-
onTeardown(_pluginId, fn) {
|
|
4152
|
-
if (typeof fn === 'function') {
|
|
4153
|
-
registerPluginTeardown(pluginId, fn);
|
|
4154
|
-
}
|
|
4155
|
-
}
|
|
4156
|
-
}
|
|
4044
|
+
function runPluginTeardowns(pluginId) {
|
|
4045
|
+
const list = _byPlugin.get(pluginId);
|
|
4046
|
+
if (!list) {
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
_byPlugin.delete(pluginId);
|
|
4050
|
+
for (const fn of list) {
|
|
4051
|
+
try {
|
|
4052
|
+
fn();
|
|
4053
|
+
}
|
|
4054
|
+
catch (e) {
|
|
4055
|
+
console.warn('[wep] teardown failed', pluginId, e);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4157
4058
|
}
|
|
4158
4059
|
|
|
4159
|
-
/**
|
|
4160
|
-
*
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
}
|
|
4183
|
-
}
|
|
4184
|
-
|
|
4185
|
-
const slots = registries.slots;
|
|
4186
|
-
for (const pointId of Object.keys(slots)) {
|
|
4187
|
-
const list = slots[pointId];
|
|
4188
|
-
if (!Array.isArray(list)) {
|
|
4189
|
-
continue
|
|
4190
|
-
}
|
|
4191
|
-
const next = list.filter((x) => x.pluginId !== pluginId);
|
|
4192
|
-
if (next.length === 0) {
|
|
4193
|
-
Vue__default.default.delete(slots, pointId);
|
|
4194
|
-
} else if (next.length !== list.length) {
|
|
4195
|
-
Vue__default.default.set(slots, pointId, next);
|
|
4196
|
-
}
|
|
4197
|
-
}
|
|
4198
|
-
registries.slotRevision++;
|
|
4199
|
-
|
|
4200
|
-
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
4201
|
-
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
4202
|
-
}
|
|
4203
|
-
|
|
4204
|
-
if (typeof document !== 'undefined') {
|
|
4205
|
-
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
4206
|
-
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
4207
|
-
el.remove();
|
|
4208
|
-
}
|
|
4209
|
-
});
|
|
4210
|
-
}
|
|
4060
|
+
/**
|
|
4061
|
+
* 插件访问后端的受控通道:`fetch` 仅允许落在配置的路径前缀下(默认 `/api/`),默认 `credentials: 'same-origin'`。
|
|
4062
|
+
*/
|
|
4063
|
+
function createRequestBridge(config = {}) {
|
|
4064
|
+
const raw = Array.isArray(config.allowedPathPrefixes) && config.allowedPathPrefixes.length > 0
|
|
4065
|
+
? config.allowedPathPrefixes
|
|
4066
|
+
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
4067
|
+
const allowedPathPrefixes = raw.map((p) => ensureLeadingPath(p));
|
|
4068
|
+
return {
|
|
4069
|
+
async request(path, init = {}) {
|
|
4070
|
+
if (typeof path !== 'string' || !path.startsWith('/')) {
|
|
4071
|
+
throw new Error('[wep:bridge] path must start with /');
|
|
4072
|
+
}
|
|
4073
|
+
const allowed = allowedPathPrefixes.some((p) => path.startsWith(p));
|
|
4074
|
+
if (!allowed) {
|
|
4075
|
+
throw new Error('[wep:bridge] path not allowed: ' + path);
|
|
4076
|
+
}
|
|
4077
|
+
return fetch(path, {
|
|
4078
|
+
credentials: 'same-origin',
|
|
4079
|
+
...init
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
4082
|
+
};
|
|
4211
4083
|
}
|
|
4212
4084
|
|
|
4213
4085
|
/**
|
|
4214
|
-
*
|
|
4215
|
-
* 使用纯 render 函数,便于 Rollup 发布 dist,宿主无需再转译 .vue。
|
|
4086
|
+
* 构造插件 `activator(hostApi)` 使用的宿主 API:路由、菜单、扩展点、资源与受控请求桥。
|
|
4216
4087
|
*/
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4088
|
+
let slotItemKeySeq = 0;
|
|
4089
|
+
let routeSynthSeq = 0;
|
|
4090
|
+
function analyzeRouteInputTree(nodes) {
|
|
4091
|
+
let hasDecl = false;
|
|
4092
|
+
let hasCfg = false;
|
|
4093
|
+
function walk(r) {
|
|
4094
|
+
if (!r || typeof r !== 'object') {
|
|
4095
|
+
return;
|
|
4096
|
+
}
|
|
4097
|
+
const o = r;
|
|
4098
|
+
const cfg = o.component != null || o.components != null;
|
|
4099
|
+
const decl = typeof o.componentRef === 'string';
|
|
4100
|
+
if (cfg) {
|
|
4101
|
+
hasCfg = true;
|
|
4102
|
+
}
|
|
4103
|
+
else if (decl) {
|
|
4104
|
+
hasDecl = true;
|
|
4105
|
+
}
|
|
4106
|
+
const ch = o.children;
|
|
4107
|
+
if (Array.isArray(ch)) {
|
|
4108
|
+
for (const c of ch) {
|
|
4109
|
+
walk(c);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4236
4112
|
}
|
|
4237
|
-
const
|
|
4238
|
-
|
|
4239
|
-
}
|
|
4240
|
-
};
|
|
4241
|
-
|
|
4242
|
-
var ExtensionPoint = {
|
|
4243
|
-
name: 'ExtensionPoint',
|
|
4244
|
-
components: { SlotErrorBoundary },
|
|
4245
|
-
props: {
|
|
4246
|
-
pointId: { type: String, required: true },
|
|
4247
|
-
slotProps: { type: Object, default: () => ({}) }
|
|
4248
|
-
},
|
|
4249
|
-
computed: {
|
|
4250
|
-
items() {
|
|
4251
|
-
void registries.slotRevision;
|
|
4252
|
-
return registries.slots[this.pointId] || []
|
|
4253
|
-
},
|
|
4254
|
-
forwardProps() {
|
|
4255
|
-
return this.slotProps || {}
|
|
4113
|
+
for (const n of nodes) {
|
|
4114
|
+
walk(n);
|
|
4256
4115
|
}
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
return h(
|
|
4260
|
-
'div',
|
|
4261
|
-
{
|
|
4262
|
-
class: 'extension-point',
|
|
4263
|
-
style: { minHeight: '8px' },
|
|
4264
|
-
attrs: { 'data-point-id': this.pointId }
|
|
4265
|
-
},
|
|
4266
|
-
this.items.map((item) =>
|
|
4267
|
-
h(
|
|
4268
|
-
SlotErrorBoundary,
|
|
4269
|
-
{
|
|
4270
|
-
key: item.key,
|
|
4271
|
-
props: { label: item.pluginId }
|
|
4272
|
-
},
|
|
4273
|
-
[h(item.component, { props: this.forwardProps })]
|
|
4274
|
-
)
|
|
4275
|
-
)
|
|
4276
|
-
)
|
|
4277
|
-
}
|
|
4278
|
-
};
|
|
4279
|
-
|
|
4116
|
+
return { hasDecl, hasCfg };
|
|
4117
|
+
}
|
|
4280
4118
|
/**
|
|
4281
|
-
*
|
|
4282
|
-
* @module install
|
|
4119
|
+
* 单插件在宿主侧的 API 句柄。工厂请传 `(id, router, kit) => createHostApi(id, r, kit)` 以便 bridge 白名单等到位。
|
|
4283
4120
|
*/
|
|
4121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4122
|
+
function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
4123
|
+
const bridgePrefixes = Array.isArray(hostKitOptions.bridgeAllowedPathPrefixes) &&
|
|
4124
|
+
hostKitOptions.bridgeAllowedPathPrefixes.length > 0
|
|
4125
|
+
? hostKitOptions.bridgeAllowedPathPrefixes
|
|
4126
|
+
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
4127
|
+
const bridge = createRequestBridge({ allowedPathPrefixes: bridgePrefixes });
|
|
4128
|
+
const parentName = typeof hostKitOptions.pluginRoutesParentName === 'string'
|
|
4129
|
+
? hostKitOptions.pluginRoutesParentName.trim()
|
|
4130
|
+
: '';
|
|
4131
|
+
function applyInternalRegister(rawRouteConfigs) {
|
|
4132
|
+
const wrapped = rawRouteConfigs.map((r) => ({
|
|
4133
|
+
...r,
|
|
4134
|
+
name: r.name || `${routeSynthNamePrefix}${pluginId}_${routeSynthSeq++}`,
|
|
4135
|
+
meta: { ...(r.meta || {}), pluginId }
|
|
4136
|
+
}));
|
|
4137
|
+
if (typeof router.addRoute !== 'function') {
|
|
4138
|
+
throw new Error('[wep] vue-router 3.5+ 必需:请使用 router.addRoute(不再支持 addRoutes)');
|
|
4139
|
+
}
|
|
4140
|
+
if (parentName) {
|
|
4141
|
+
for (const r of wrapped) {
|
|
4142
|
+
router.addRoute(parentName, r);
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
else {
|
|
4146
|
+
for (const r of wrapped) {
|
|
4147
|
+
router.addRoute(r);
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
function injectStylesheet(href) {
|
|
4152
|
+
const link = document.createElement('link');
|
|
4153
|
+
link.rel = 'stylesheet';
|
|
4154
|
+
link.href = href;
|
|
4155
|
+
link.setAttribute('data-plugin-asset', pluginId);
|
|
4156
|
+
document.head.appendChild(link);
|
|
4157
|
+
}
|
|
4158
|
+
function injectScript(src) {
|
|
4159
|
+
return new Promise((resolve, reject) => {
|
|
4160
|
+
const s = document.createElement('script');
|
|
4161
|
+
s.async = true;
|
|
4162
|
+
s.src = src;
|
|
4163
|
+
s.setAttribute('data-plugin-asset', pluginId);
|
|
4164
|
+
s.onload = () => resolve();
|
|
4165
|
+
s.onerror = () => reject(new Error('script failed: ' + src));
|
|
4166
|
+
document.head.appendChild(s);
|
|
4167
|
+
});
|
|
4168
|
+
}
|
|
4169
|
+
const hostContext = hostKitOptions.hostContext != null && typeof hostKitOptions.hostContext === 'object'
|
|
4170
|
+
? hostKitOptions.hostContext
|
|
4171
|
+
: Object.freeze({});
|
|
4172
|
+
return {
|
|
4173
|
+
hostPluginApiVersion: HOST_PLUGIN_API_VERSION,
|
|
4174
|
+
/** 宿主注入的只读依赖(store、router、开放能力等),见 `resolveRuntimeOptions.hostContext` */
|
|
4175
|
+
hostContext,
|
|
4176
|
+
registerRoutes(routes) {
|
|
4177
|
+
const list = Array.isArray(routes) ? routes : [];
|
|
4178
|
+
if (list.length === 0) {
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
const { hasDecl, hasCfg } = analyzeRouteInputTree(list);
|
|
4182
|
+
if (hasDecl && hasCfg) {
|
|
4183
|
+
throw new Error('[wep] registerRoutes: cannot mix RouteDeclaration (componentRef) with RouteConfig (component)');
|
|
4184
|
+
}
|
|
4185
|
+
let configs;
|
|
4186
|
+
if (hasDecl) {
|
|
4187
|
+
const adapt = hostKitOptions.adaptRouteDeclarations;
|
|
4188
|
+
if (typeof adapt !== 'function') {
|
|
4189
|
+
throw new Error('[wep] registerRoutes: RouteDeclaration (componentRef) requires adaptRouteDeclarations on the host');
|
|
4190
|
+
}
|
|
4191
|
+
configs = adapt({
|
|
4192
|
+
pluginId,
|
|
4193
|
+
router,
|
|
4194
|
+
declarations: list
|
|
4195
|
+
});
|
|
4196
|
+
}
|
|
4197
|
+
else {
|
|
4198
|
+
configs = list;
|
|
4199
|
+
}
|
|
4200
|
+
if (typeof hostKitOptions.transformRoutes === 'function') {
|
|
4201
|
+
configs = hostKitOptions.transformRoutes({
|
|
4202
|
+
pluginId,
|
|
4203
|
+
router,
|
|
4204
|
+
routes: configs
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
if (typeof hostKitOptions.interceptRegisterRoutes === 'function') {
|
|
4208
|
+
hostKitOptions.interceptRegisterRoutes({
|
|
4209
|
+
pluginId,
|
|
4210
|
+
router,
|
|
4211
|
+
routes: configs,
|
|
4212
|
+
applyInternalRegister
|
|
4213
|
+
});
|
|
4214
|
+
}
|
|
4215
|
+
else {
|
|
4216
|
+
applyInternalRegister(configs);
|
|
4217
|
+
}
|
|
4218
|
+
},
|
|
4219
|
+
registerMenuItems(items) {
|
|
4220
|
+
const apply = hostKitOptions.applyPluginMenuItems;
|
|
4221
|
+
if (typeof apply !== 'function') {
|
|
4222
|
+
throw new Error('[wep] registerMenuItems 需要宿主在 resolveRuntimeOptions 中提供 applyPluginMenuItems,将菜单数据并入宿主侧栏/目录 state(框架不再维护 registries.menus)');
|
|
4223
|
+
}
|
|
4224
|
+
const list = Array.isArray(items) ? items : [];
|
|
4225
|
+
const enriched = list.map((item) => ({ ...item, pluginId }));
|
|
4226
|
+
enriched.sort((a, b) => (a.order != null ? Number(a.order) : 0) - (b.order != null ? Number(b.order) : 0));
|
|
4227
|
+
apply({ pluginId, items: enriched });
|
|
4228
|
+
},
|
|
4229
|
+
registerSlotComponents(pointId, components) {
|
|
4230
|
+
if (!pointId) {
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
if (!registries.slots[pointId]) {
|
|
4234
|
+
Vue__default.default.set(registries.slots, pointId, []);
|
|
4235
|
+
}
|
|
4236
|
+
const list = registries.slots[pointId];
|
|
4237
|
+
for (const c of components) {
|
|
4238
|
+
list.push({
|
|
4239
|
+
pluginId,
|
|
4240
|
+
component: c.component,
|
|
4241
|
+
priority: c.priority != null ? c.priority : 0,
|
|
4242
|
+
key: `${pluginId}-${pointId}-${++slotItemKeySeq}`
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
list.sort((a, b) => b.priority - a.priority);
|
|
4246
|
+
registries.slotRevision++;
|
|
4247
|
+
},
|
|
4248
|
+
registerStylesheetUrls(urls) {
|
|
4249
|
+
for (const u of urls || []) {
|
|
4250
|
+
if (typeof u === 'string' && u) {
|
|
4251
|
+
injectStylesheet(u);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
},
|
|
4255
|
+
registerScriptUrls(urls) {
|
|
4256
|
+
const chain = (urls || []).filter((u) => typeof u === 'string' && u).reduce((p, u) => p.then(() => injectScript(u)), Promise.resolve());
|
|
4257
|
+
chain.catch((e) => console.warn('[wep] registerScriptUrls', pluginId, e));
|
|
4258
|
+
},
|
|
4259
|
+
registerSanitizedHtmlSnippet() {
|
|
4260
|
+
throw new Error('registerSanitizedHtmlSnippet is not enabled');
|
|
4261
|
+
},
|
|
4262
|
+
getBridge: () => bridge,
|
|
4263
|
+
onTeardown(_pluginId, fn) {
|
|
4264
|
+
if (typeof fn === 'function') {
|
|
4265
|
+
registerPluginTeardown(pluginId, fn);
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
};
|
|
4269
|
+
}
|
|
4284
4270
|
|
|
4285
4271
|
/**
|
|
4286
|
-
*
|
|
4287
|
-
*
|
|
4288
|
-
* @param {*} Vue
|
|
4289
|
-
* @param {*} router vue-router 实例
|
|
4290
|
-
* @param {Record<string, unknown>} [options] 传给 `resolveRuntimeOptions` 的字段;可含 `env`(Vite 传入 `import.meta.env`)以读取 `VITE_*`。
|
|
4291
|
-
* @returns {Promise<void>}
|
|
4272
|
+
* 卸载单个插件:执行 teardown、清理注册表与 activator、移除带 `data-plugin-asset` 的 DOM。
|
|
4273
|
+
* 注意:Vue Router 3 无公开 `removeRoute`,动态路由通常需整页刷新或宿主自行维护。
|
|
4292
4274
|
*/
|
|
4293
|
-
function
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4275
|
+
function disposeWebPlugin(pluginId) {
|
|
4276
|
+
if (!pluginId || typeof pluginId !== 'string') {
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
runPluginTeardowns(pluginId);
|
|
4280
|
+
revokePluginMenusIfConfigured(pluginId);
|
|
4281
|
+
const slots = registries.slots;
|
|
4282
|
+
for (const pointId of Object.keys(slots)) {
|
|
4283
|
+
const list = slots[pointId];
|
|
4284
|
+
if (!Array.isArray(list)) {
|
|
4285
|
+
continue;
|
|
4286
|
+
}
|
|
4287
|
+
const next = list.filter((x) => x.pluginId !== pluginId);
|
|
4288
|
+
if (next.length === 0) {
|
|
4289
|
+
Vue__default.default.delete(slots, pointId);
|
|
4290
|
+
}
|
|
4291
|
+
else if (next.length !== list.length) {
|
|
4292
|
+
Vue__default.default.set(slots, pointId, next);
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
registries.slotRevision++;
|
|
4296
|
+
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
4297
|
+
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
4298
|
+
}
|
|
4299
|
+
if (typeof document !== 'undefined') {
|
|
4300
|
+
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
4301
|
+
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
4302
|
+
el.remove();
|
|
4303
|
+
}
|
|
4304
|
+
});
|
|
4305
|
+
}
|
|
4304
4306
|
}
|
|
4305
4307
|
|
|
4306
4308
|
/**
|
|
4307
|
-
*
|
|
4308
|
-
*
|
|
4309
|
-
*
|
|
4310
|
-
* @module presets/vue-cli-axios
|
|
4309
|
+
* 布局中的扩展点占位;插件通过 `hostApi.registerSlotComponents(pointId, ...)` 注入组件。
|
|
4310
|
+
* 样式由宿主全局 CSS 覆盖 `.extension-point` / `.plugin-point-error`。
|
|
4311
4311
|
*/
|
|
4312
|
+
const SlotErrorBoundary = Vue__default.default.extend({
|
|
4313
|
+
name: 'SlotErrorBoundary',
|
|
4314
|
+
props: { label: String },
|
|
4315
|
+
data() {
|
|
4316
|
+
return { error: null };
|
|
4317
|
+
},
|
|
4318
|
+
errorCaptured(err) {
|
|
4319
|
+
this.error = err instanceof Error && err.message ? err.message : String(err);
|
|
4320
|
+
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4321
|
+
return false;
|
|
4322
|
+
},
|
|
4323
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4324
|
+
render(h) {
|
|
4325
|
+
if (this.error) {
|
|
4326
|
+
return h('div', { class: 'plugin-point-error' }, `[插件 ${this.label}] 渲染失败`);
|
|
4327
|
+
}
|
|
4328
|
+
const d = this.$slots.default;
|
|
4329
|
+
return d && d[0] ? d[0] : h('span');
|
|
4330
|
+
}
|
|
4331
|
+
});
|
|
4332
|
+
var ExtensionPoint = Vue__default.default.extend({
|
|
4333
|
+
name: 'ExtensionPoint',
|
|
4334
|
+
components: { SlotErrorBoundary },
|
|
4335
|
+
props: {
|
|
4336
|
+
pointId: { type: String, required: true },
|
|
4337
|
+
slotProps: { type: Object, default: () => ({}) }
|
|
4338
|
+
},
|
|
4339
|
+
computed: {
|
|
4340
|
+
items() {
|
|
4341
|
+
void registries.slotRevision;
|
|
4342
|
+
return registries.slots[this.pointId] || [];
|
|
4343
|
+
},
|
|
4344
|
+
forwardProps() {
|
|
4345
|
+
return this.slotProps || {};
|
|
4346
|
+
}
|
|
4347
|
+
},
|
|
4348
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4349
|
+
render(h) {
|
|
4350
|
+
return h('div', {
|
|
4351
|
+
class: 'extension-point',
|
|
4352
|
+
attrs: { 'data-point-id': this.pointId }
|
|
4353
|
+
}, this.items.map((item) => h(SlotErrorBoundary, {
|
|
4354
|
+
key: item.key,
|
|
4355
|
+
props: { label: item.pluginId }
|
|
4356
|
+
}, [h(item.component, { props: this.forwardProps })])));
|
|
4357
|
+
}
|
|
4358
|
+
});
|
|
4312
4359
|
|
|
4313
4360
|
/**
|
|
4314
|
-
*
|
|
4315
|
-
* @property {(config: { url: string, method?: string, [key: string]: unknown }) => Promise<unknown>} request 宿主 axios 封装(已含 baseURL、Token 等)
|
|
4361
|
+
* 注册全局 `ExtensionPoint` 并异步拉取清单、激活插件。
|
|
4316
4362
|
*/
|
|
4317
|
-
|
|
4318
4363
|
/**
|
|
4319
|
-
*
|
|
4320
|
-
* @param
|
|
4321
|
-
* @param
|
|
4364
|
+
* @param Vue Vue 构造函数
|
|
4365
|
+
* @param router vue-router 实例
|
|
4366
|
+
* @param options 传给 `resolveRuntimeOptions`;可含 `env` 以读取 `VITE_*`
|
|
4322
4367
|
*/
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
path = path.slice(base.length) || '/';
|
|
4336
|
-
}
|
|
4337
|
-
return path
|
|
4368
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4369
|
+
function installWebExtendPluginVue2(Vue, router, options) {
|
|
4370
|
+
const opts = options || {};
|
|
4371
|
+
const { env: injectedEnv, ...runtimeUser } = opts;
|
|
4372
|
+
if (injectedEnv && typeof injectedEnv === 'object') {
|
|
4373
|
+
setWebExtendPluginEnv(injectedEnv);
|
|
4374
|
+
}
|
|
4375
|
+
if (Vue && ExtensionPoint) {
|
|
4376
|
+
Vue.component('ExtensionPoint', ExtensionPoint);
|
|
4377
|
+
}
|
|
4378
|
+
const runtime = resolveRuntimeOptions$1(runtimeUser);
|
|
4379
|
+
return bootstrapPlugins$1(router, (id, r, kit) => createHostApi(id, r, kit || {}), runtime);
|
|
4338
4380
|
}
|
|
4339
4381
|
|
|
4340
4382
|
/**
|
|
4341
|
-
*
|
|
4342
|
-
* @param {unknown} body
|
|
4343
|
-
* @returns {object|null}
|
|
4383
|
+
* Vue CLI + 统一 axios(如 RuoYi `utils/request`)场景的 `install` 预设。
|
|
4344
4384
|
*/
|
|
4345
|
-
function
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
const d = o.data;
|
|
4354
|
-
if (d && typeof d === 'object') {
|
|
4355
|
-
const inner = /** @type {Record<string, unknown>} */ (d);
|
|
4356
|
-
if (Array.isArray(inner.plugins)) {
|
|
4357
|
-
return inner
|
|
4385
|
+
function resolveManifestPathUnderApiBase(manifestUrl, apiBase) {
|
|
4386
|
+
const base = String(apiBase !== undefined
|
|
4387
|
+
? apiBase
|
|
4388
|
+
: typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4389
|
+
? String(process.env.VUE_APP_BASE_API)
|
|
4390
|
+
: '').replace(/\/$/, '');
|
|
4391
|
+
if (typeof window === 'undefined') {
|
|
4392
|
+
return '/api/frontend-plugins';
|
|
4358
4393
|
}
|
|
4359
|
-
|
|
4360
|
-
|
|
4394
|
+
const u = new URL(manifestUrl, window.location.origin);
|
|
4395
|
+
let path = u.pathname + u.search;
|
|
4396
|
+
if (base && path.startsWith(base)) {
|
|
4397
|
+
path = path.slice(base.length) || '/';
|
|
4361
4398
|
}
|
|
4362
|
-
|
|
4363
|
-
return d !== undefined && d !== null && typeof d === 'object' ? /** @type {object} */ (d) : o
|
|
4399
|
+
return path;
|
|
4364
4400
|
}
|
|
4365
|
-
|
|
4366
|
-
function bridgePrefixesFromVueCliEnv() {
|
|
4367
|
-
const base = (
|
|
4368
|
-
typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4369
|
-
? String(process.env.VUE_APP_BASE_API)
|
|
4370
|
-
: ''
|
|
4371
|
-
).replace(/\/$/, '');
|
|
4372
|
-
const raw = [base ? `${base}/` : '', '/api/', '/dev-api/'].filter(Boolean);
|
|
4373
|
-
return [...new Set(raw)]
|
|
4374
|
-
}
|
|
4375
|
-
|
|
4376
4401
|
/**
|
|
4377
|
-
*
|
|
4378
|
-
*
|
|
4379
|
-
* @param {Record<string, unknown>} [extra] 合并覆盖,如 `manifestListPath`、`env` 等
|
|
4380
|
-
* @returns {Record<string, unknown>}
|
|
4402
|
+
* 常见 Java 清单路径:与 `VUE_APP_BASE_API` 拼接为 `${base}/frontend-plugins`。
|
|
4403
|
+
* 若后端使用 `/api/frontend-plugins` 段,请在 extra 中显式传入 `manifestListPath`。
|
|
4381
4404
|
*/
|
|
4405
|
+
const defaultVueCliJavaManifestListPath = '/frontend-plugins';
|
|
4406
|
+
function bridgePrefixesFromVueCliEnv() {
|
|
4407
|
+
const base = (typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4408
|
+
? String(process.env.VUE_APP_BASE_API)
|
|
4409
|
+
: '').replace(/\/$/, '');
|
|
4410
|
+
const raw = [base ? `${base}/` : '', '/api/', '/dev-api/'].filter(Boolean);
|
|
4411
|
+
return [...new Set(raw)];
|
|
4412
|
+
}
|
|
4382
4413
|
function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4414
|
+
const { request } = deps;
|
|
4415
|
+
if (typeof request !== 'function') {
|
|
4416
|
+
throw new Error('[wep] createVueCliAxiosInstallOptions requires deps.request');
|
|
4417
|
+
}
|
|
4418
|
+
const { fetchManifest: userFetchManifest, manifestMode: extraManifestMode, staticManifestUrl: extraStaticManifestUrl, ...restExtra } = extra;
|
|
4419
|
+
const envBase = (typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4420
|
+
? String(process.env.VUE_APP_BASE_API)
|
|
4421
|
+
: '').replace(/\/$/, '');
|
|
4422
|
+
const userBase = extra.manifestBase !== undefined && String(extra.manifestBase).trim() !== ''
|
|
4423
|
+
? String(extra.manifestBase).replace(/\/$/, '')
|
|
4424
|
+
: '';
|
|
4425
|
+
const stripBase = userBase || envBase;
|
|
4426
|
+
const manifestMode = resolveManifestModeFromInputs(extraManifestMode);
|
|
4427
|
+
const staticManifestUrl = resolveStaticManifestUrlFromInputs(extraStaticManifestUrl);
|
|
4428
|
+
const fetchManifestApi = async (ctx) => {
|
|
4429
|
+
try {
|
|
4430
|
+
const url = resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase);
|
|
4431
|
+
const body = await request({
|
|
4432
|
+
url,
|
|
4433
|
+
method: 'get'
|
|
4434
|
+
});
|
|
4435
|
+
const data = unwrapNestedManifestBody(body);
|
|
4436
|
+
if (!data || typeof data !== 'object') {
|
|
4437
|
+
return {
|
|
4438
|
+
ok: false,
|
|
4439
|
+
error: new Error('[wep] invalid manifest response'),
|
|
4440
|
+
data: null
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
return { ok: true, data };
|
|
4444
|
+
}
|
|
4445
|
+
catch (e) {
|
|
4446
|
+
return { ok: false, error: e, data: null };
|
|
4411
4447
|
}
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4448
|
+
};
|
|
4449
|
+
const fetchManifestStatic = (ctx) => fetchStaticManifestViaHttp(ctx);
|
|
4450
|
+
const fetchManifest = typeof userFetchManifest === 'function'
|
|
4451
|
+
? userFetchManifest
|
|
4452
|
+
: manifestMode === 'static'
|
|
4453
|
+
? fetchManifestStatic
|
|
4454
|
+
: fetchManifestApi;
|
|
4455
|
+
const opts = {
|
|
4456
|
+
manifestBase: stripBase || undefined,
|
|
4457
|
+
bridgeAllowedPathPrefixes: bridgePrefixesFromVueCliEnv(),
|
|
4458
|
+
manifestMode,
|
|
4459
|
+
staticManifestUrl,
|
|
4460
|
+
...restExtra,
|
|
4461
|
+
fetchManifest
|
|
4462
|
+
};
|
|
4463
|
+
const listPath = typeof process !== 'undefined' &&
|
|
4464
|
+
process.env &&
|
|
4465
|
+
process.env[webExtendPluginEnvKeys.manifestPathAlt];
|
|
4466
|
+
if (listPath && opts.manifestListPath === undefined && extra.manifestListPath === undefined) {
|
|
4467
|
+
opts.manifestListPath = String(process.env[webExtendPluginEnvKeys.manifestPathAlt]);
|
|
4416
4468
|
}
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
const opts = {
|
|
4420
|
-
manifestBase: stripBase || undefined,
|
|
4421
|
-
bridgeAllowedPathPrefixes: bridgePrefixesFromVueCliEnv(),
|
|
4422
|
-
fetchManifest,
|
|
4423
|
-
...extra
|
|
4424
|
-
};
|
|
4425
|
-
|
|
4426
|
-
const listPath =
|
|
4427
|
-
typeof process !== 'undefined' && process.env && process.env.VUE_APP_WEB_PLUGIN_MANIFEST_PATH;
|
|
4428
|
-
if (listPath && opts.manifestListPath === undefined && extra.manifestListPath === undefined) {
|
|
4429
|
-
opts.manifestListPath = String(listPath);
|
|
4430
|
-
}
|
|
4431
|
-
|
|
4432
|
-
return opts
|
|
4469
|
+
return opts;
|
|
4433
4470
|
}
|
|
4434
|
-
|
|
4435
4471
|
/**
|
|
4436
|
-
*
|
|
4437
|
-
*
|
|
4472
|
+
* 少样板接入:`hostContext` 自动含 `router`(及可选 `store`)、`isDev` 与常见 `manifestListPath` 默认值。
|
|
4473
|
+
* 更多运行时字段仍可通过 `extra` 覆盖。
|
|
4438
4474
|
*/
|
|
4475
|
+
function createVueCliAxiosQuickInstallOptions(
|
|
4476
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4477
|
+
router, deps, extra = {}) {
|
|
4478
|
+
const { request, hostLayoutComponent, store, hostContext: depHostContext, applyPluginMenuItems, revokePluginMenuItems } = deps;
|
|
4479
|
+
const { hostContext: extraHostContext, isDev: extraIsDev, manifestListPath: extraListPath, ...restExtra } = extra;
|
|
4480
|
+
const hostContext = {
|
|
4481
|
+
router,
|
|
4482
|
+
...(store !== undefined ? { store } : {}),
|
|
4483
|
+
...(depHostContext && typeof depHostContext === 'object' ? depHostContext : {}),
|
|
4484
|
+
...(extraHostContext && typeof extraHostContext === 'object' ? extraHostContext : {})
|
|
4485
|
+
};
|
|
4486
|
+
const manifestListPath = extraListPath !== undefined && String(extraListPath).trim() !== ''
|
|
4487
|
+
? String(extraListPath)
|
|
4488
|
+
: defaultVueCliJavaManifestListPath;
|
|
4489
|
+
return createVueCliAxiosInstallOptions({ request }, {
|
|
4490
|
+
hostLayoutComponent,
|
|
4491
|
+
hostContext,
|
|
4492
|
+
applyPluginMenuItems,
|
|
4493
|
+
revokePluginMenuItems,
|
|
4494
|
+
isDev: extraIsDev !== undefined ? Boolean(extraIsDev) : resolveBundledIsDev(),
|
|
4495
|
+
manifestListPath,
|
|
4496
|
+
...restExtra
|
|
4497
|
+
});
|
|
4498
|
+
}
|
|
4439
4499
|
const presetVueCliAxios = Object.freeze({
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4500
|
+
id: 'vue-cli-axios',
|
|
4501
|
+
description: 'Vue CLI + axios request for API manifest; optional manifestMode=static uses fetch',
|
|
4502
|
+
createInstallOptions: createVueCliAxiosInstallOptions,
|
|
4503
|
+
createQuickInstallOptions: createVueCliAxiosQuickInstallOptions,
|
|
4504
|
+
defaultJavaManifestListPath: defaultVueCliJavaManifestListPath,
|
|
4505
|
+
manifestPathForApiBase: resolveManifestPathUnderApiBase,
|
|
4506
|
+
unwrapManifestBody: unwrapNestedManifestBody
|
|
4445
4507
|
});
|
|
4446
4508
|
|
|
4447
4509
|
/**
|
|
4448
|
-
*
|
|
4449
|
-
* 语义化版本内保持本文件导出的符号与聚合结构兼容。
|
|
4450
|
-
*
|
|
4451
|
-
* `PluginRuntime` 与 `manifest-fetch-composer` 通过 namespace 绑定到 `WebExtendPluginVue2.runtime`,避免具名导出与聚合对象重复列举。
|
|
4452
|
-
*
|
|
4453
|
-
* @module public
|
|
4510
|
+
* Vue CLI + axios 场景的一键安装:合并 hostContext、默认清单路径与 IIFE 全局 Vue。
|
|
4454
4511
|
*/
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
const
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
} =
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
manifestFetchCacheMiddleware,
|
|
4466
|
-
wrapManifestFetchWithCache
|
|
4467
|
-
} = manifestComposer;
|
|
4512
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4513
|
+
function installVueCliAxiosWebPlugins(Vue, router, deps, extra) {
|
|
4514
|
+
const exposeGlobalVue = extra?.exposeGlobalVue !== false;
|
|
4515
|
+
if (exposeGlobalVue && typeof window !== 'undefined') {
|
|
4516
|
+
window.Vue = Vue;
|
|
4517
|
+
}
|
|
4518
|
+
const { exposeGlobalVue: _skip, ...restExtra } = extra || {};
|
|
4519
|
+
const opts = createVueCliAxiosQuickInstallOptions(router, deps, restExtra);
|
|
4520
|
+
return installWebExtendPluginVue2(Vue, router, opts);
|
|
4521
|
+
}
|
|
4468
4522
|
|
|
4469
4523
|
/**
|
|
4470
|
-
*
|
|
4471
|
-
* @type {Readonly<{
|
|
4472
|
-
* install: typeof installWebExtendPluginVue2,
|
|
4473
|
-
* runtime: Readonly<{ bootstrapPlugins: typeof pluginRuntime.bootstrapPlugins, resolveRuntimeOptions: typeof pluginRuntime.resolveRuntimeOptions, defaultFetchWebPluginManifest: typeof pluginRuntime.defaultFetchWebPluginManifest, composeManifestFetch: typeof manifestComposer.composeManifestFetch, manifestFetchCacheMiddleware: typeof manifestComposer.manifestFetchCacheMiddleware, wrapManifestFetchWithCache: typeof manifestComposer.wrapManifestFetchWithCache }>,
|
|
4474
|
-
* host: Readonly<{ createHostApi: typeof createHostApi, disposeWebPlugin: typeof disposeWebPlugin, createRequestBridge: typeof createRequestBridge, registries: typeof registries }>,
|
|
4475
|
-
* config: Readonly<{ defaultWebExtendPluginRuntime: typeof defaultWebExtendPluginRuntime, setWebExtendPluginEnv: typeof setWebExtendPluginEnv }>,
|
|
4476
|
-
* constants: Readonly<{ HOST_PLUGIN_API_VERSION: typeof HOST_PLUGIN_API_VERSION }>,
|
|
4477
|
-
* components: Readonly<{ ExtensionPoint: typeof ExtensionPoint }>,
|
|
4478
|
-
* presets: Readonly<{ vueCliAxios: typeof presetVueCliAxios }>
|
|
4479
|
-
* }>}
|
|
4524
|
+
* 包对外稳定导出与 `WebExtendPluginVue2` 命名空间。
|
|
4480
4525
|
*/
|
|
4526
|
+
const { bootstrapPlugins, defaultFetchWebPluginManifest, resolveRuntimeOptions, ensurePluginHostRoute } = pluginRuntime;
|
|
4527
|
+
const { composeManifestFetch, manifestFetchCacheMiddleware, wrapManifestFetchWithCache } = manifestComposer;
|
|
4481
4528
|
const WebExtendPluginVue2 = Object.freeze({
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4529
|
+
install: installWebExtendPluginVue2,
|
|
4530
|
+
runtime: Object.freeze({
|
|
4531
|
+
bootstrapPlugins: bootstrapPlugins$1,
|
|
4532
|
+
resolveRuntimeOptions: resolveRuntimeOptions$1,
|
|
4533
|
+
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
4534
|
+
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
4535
|
+
composeManifestFetch: composeManifestFetch$1,
|
|
4536
|
+
manifestFetchCacheMiddleware: manifestFetchCacheMiddleware$1,
|
|
4537
|
+
wrapManifestFetchWithCache: wrapManifestFetchWithCache$1
|
|
4538
|
+
}),
|
|
4539
|
+
host: Object.freeze({
|
|
4540
|
+
createHostApi,
|
|
4541
|
+
disposeWebPlugin,
|
|
4542
|
+
createRequestBridge,
|
|
4543
|
+
registries
|
|
4544
|
+
}),
|
|
4545
|
+
config: Object.freeze({
|
|
4546
|
+
defaultWebExtendPluginRuntime,
|
|
4547
|
+
setWebExtendPluginEnv,
|
|
4548
|
+
webExtendPluginEnvKeys,
|
|
4549
|
+
defaultManifestFetchCache,
|
|
4550
|
+
defaultManifestMode,
|
|
4551
|
+
routeSynthNamePrefix,
|
|
4552
|
+
peerMinimumVersions
|
|
4553
|
+
}),
|
|
4554
|
+
constants: Object.freeze({
|
|
4555
|
+
HOST_PLUGIN_API_VERSION,
|
|
4556
|
+
RUNTIME_CONSOLE_LABEL
|
|
4557
|
+
}),
|
|
4558
|
+
components: Object.freeze({
|
|
4559
|
+
ExtensionPoint
|
|
4560
|
+
}),
|
|
4561
|
+
presets: Object.freeze({
|
|
4562
|
+
vueCliAxios: presetVueCliAxios
|
|
4563
|
+
})
|
|
4510
4564
|
});
|
|
4511
4565
|
|
|
4512
4566
|
exports.ExtensionPoint = ExtensionPoint;
|
|
4513
4567
|
exports.HOST_PLUGIN_API_VERSION = HOST_PLUGIN_API_VERSION;
|
|
4568
|
+
exports.RUNTIME_CONSOLE_LABEL = RUNTIME_CONSOLE_LABEL;
|
|
4514
4569
|
exports.WebExtendPluginVue2 = WebExtendPluginVue2;
|
|
4515
4570
|
exports.bootstrapPlugins = bootstrapPlugins;
|
|
4516
4571
|
exports.composeManifestFetch = composeManifestFetch;
|
|
4517
4572
|
exports.createHostApi = createHostApi;
|
|
4518
4573
|
exports.createRequestBridge = createRequestBridge;
|
|
4519
4574
|
exports.createVueCliAxiosInstallOptions = createVueCliAxiosInstallOptions;
|
|
4575
|
+
exports.createVueCliAxiosQuickInstallOptions = createVueCliAxiosQuickInstallOptions;
|
|
4520
4576
|
exports.defaultFetchWebPluginManifest = defaultFetchWebPluginManifest;
|
|
4577
|
+
exports.defaultManifestFetchCache = defaultManifestFetchCache;
|
|
4578
|
+
exports.defaultManifestMode = defaultManifestMode;
|
|
4579
|
+
exports.defaultVueCliJavaManifestListPath = defaultVueCliJavaManifestListPath;
|
|
4521
4580
|
exports.defaultWebExtendPluginRuntime = defaultWebExtendPluginRuntime;
|
|
4522
4581
|
exports.disposeWebPlugin = disposeWebPlugin;
|
|
4582
|
+
exports.ensurePluginHostRoute = ensurePluginHostRoute;
|
|
4583
|
+
exports.installVueCliAxiosWebPlugins = installVueCliAxiosWebPlugins;
|
|
4523
4584
|
exports.installWebExtendPluginVue2 = installWebExtendPluginVue2;
|
|
4524
4585
|
exports.manifestFetchCacheMiddleware = manifestFetchCacheMiddleware;
|
|
4525
|
-
exports.
|
|
4586
|
+
exports.peerMinimumVersions = peerMinimumVersions;
|
|
4526
4587
|
exports.presetVueCliAxios = presetVueCliAxios;
|
|
4527
4588
|
exports.registries = registries;
|
|
4589
|
+
exports.resolveManifestPathUnderApiBase = resolveManifestPathUnderApiBase;
|
|
4528
4590
|
exports.resolveRuntimeOptions = resolveRuntimeOptions;
|
|
4591
|
+
exports.routeSynthNamePrefix = routeSynthNamePrefix;
|
|
4529
4592
|
exports.setWebExtendPluginEnv = setWebExtendPluginEnv;
|
|
4530
|
-
exports.
|
|
4593
|
+
exports.unwrapNestedManifestBody = unwrapNestedManifestBody;
|
|
4594
|
+
exports.webExtendPluginEnvKeys = webExtendPluginEnvKeys;
|
|
4531
4595
|
exports.wrapManifestFetchWithCache = wrapManifestFetchWithCache;
|
|
4532
4596
|
//# sourceMappingURL=index.cjs.map
|