@fulgurjs/federation 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +286 -95
- package/dist/{chunk-X4LZDAZQ.js → chunk-75PP6EAK.js} +1 -1
- package/dist/chunk-NUZKZUTD.js +144 -0
- package/dist/chunk-TSXZ7TWA.js +144 -0
- package/dist/{chunk-3VBYOVH4.js → chunk-WBCXLR2N.js} +9 -1
- package/dist/cli.js +584 -127
- package/dist/config.cjs +63 -4
- package/dist/config.d.cts +31 -4
- package/dist/config.d.ts +31 -4
- package/dist/config.js +62 -4
- package/dist/context.cjs +12 -2
- package/dist/context.d.cts +21 -5
- package/dist/context.d.ts +21 -5
- package/dist/context.js +12 -3
- package/dist/index.cjs +72 -21
- package/dist/index.d.cts +2 -92
- package/dist/index.d.ts +2 -92
- package/dist/index.js +72 -21
- package/dist/options-CRGpSmVj.d.cts +100 -0
- package/dist/options-CRGpSmVj.d.ts +100 -0
- package/dist/pages.js +4 -139
- package/dist/runtime-entry.d.ts +86 -5
- package/dist/runtime-entry.js +3 -3
- package/dist/runtime.js +10 -3
- package/dist/vue-adapter.cjs +241 -0
- package/dist/vue-adapter.d.cts +47 -1
- package/dist/vue-adapter.d.ts +47 -1
- package/dist/vue-adapter.js +4 -1
- package/dist/vue.cjs +402 -3
- package/dist/vue.d.cts +6 -2
- package/dist/vue.d.ts +6 -2
- package/dist/vue.js +2 -1
- package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +11 -8
- package/package.json +1 -1
- package/dist/chunk-AYS56IRF.js +0 -42
package/dist/vue-adapter.js
CHANGED
package/dist/vue.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/vue.ts
|
|
21
21
|
var vue_exports = {};
|
|
22
22
|
__export(vue_exports, {
|
|
23
|
+
createHostPages: () => createHostPages2,
|
|
23
24
|
remoteComponent: () => remoteComponent
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(vue_exports);
|
|
@@ -253,10 +254,20 @@ var ErrorCodes = {
|
|
|
253
254
|
/** 加载到的模块没有任何导出(常见:exposes 指向了不导出内容的文件) */
|
|
254
255
|
EMPTY_EXPORTS: "MFU-009",
|
|
255
256
|
/** singleton 共享协商版本与消费方 requiredVersion 不一致(使用作用域版本,仅告警) */
|
|
256
|
-
SINGLETON_SKEW: "MFU-010"
|
|
257
|
+
SINGLETON_SKEW: "MFU-010",
|
|
258
|
+
/** setup 生命周期入口导出形态非法(默认导出不是函数,或具名 onSession 不是函数) */
|
|
259
|
+
SETUP_INVALID_EXPORT: "MFU-011",
|
|
260
|
+
/** setup/onSession 执行抛错(该次 loadRemote 拒绝;仅清失败阶段缓存,允许重试) */
|
|
261
|
+
SETUP_RUN_FAILED: "MFU-012",
|
|
262
|
+
/** 远程声明了 onSession 但宿主 AppContext 未提供 sessionKey(登录代次) */
|
|
263
|
+
SETUP_SESSION_KEY_MISSING: "MFU-013",
|
|
264
|
+
/** setup/onSession 执行的同步段内递归 loadRemote 同一远程 */
|
|
265
|
+
SETUP_RECURSION: "MFU-014"
|
|
257
266
|
};
|
|
258
267
|
|
|
259
268
|
// src/runtime/index.ts
|
|
269
|
+
var SETUP_META_KEY = "__fulgurjsSetup";
|
|
270
|
+
var APP_CONTEXT_GLOBAL_KEY = "__FULGURJS_APP_CONFIG__";
|
|
260
271
|
var DEFAULT_TIMEOUT = 15e3;
|
|
261
272
|
var DEFAULT_RETRIES = 2;
|
|
262
273
|
var BREAKER_THRESHOLD = 5;
|
|
@@ -281,6 +292,140 @@ function createRuntime() {
|
|
|
281
292
|
}
|
|
282
293
|
}
|
|
283
294
|
};
|
|
295
|
+
const lifecycleStates = /* @__PURE__ */ new Map();
|
|
296
|
+
let lifecycleSyncRemote;
|
|
297
|
+
const lifecycleOf = (name) => {
|
|
298
|
+
let st = lifecycleStates.get(name);
|
|
299
|
+
if (!st) lifecycleStates.set(name, st = {});
|
|
300
|
+
return st;
|
|
301
|
+
};
|
|
302
|
+
const currentAppContext = () => globalThis[APP_CONTEXT_GLOBAL_KEY] ?? {};
|
|
303
|
+
async function invokeLifecycleFn(remoteName, fn, ctx, phase) {
|
|
304
|
+
const wrap = (err) => {
|
|
305
|
+
if (err instanceof FgError) return err;
|
|
306
|
+
return new FgError(
|
|
307
|
+
ErrorCodes.SETUP_RUN_FAILED,
|
|
308
|
+
`${phase} of "${remoteName}" threw: ${String(err?.message ?? err)}
|
|
309
|
+
\u4FEE\u6CD5: \u4FEE\u590D\u8BE5 ${phase} \u7684\u521D\u59CB\u5316\u903B\u8F91\u540E\u91CD\u65B0\u52A0\u8F7D\uFF08\u5931\u8D25\u7684\u521D\u59CB\u5316\u7F13\u5B58\u5DF2\u6E05\u9664\uFF0C\u53EF\u76F4\u63A5\u91CD\u8BD5\uFF09`,
|
|
310
|
+
{ remote: remoteName, phase }
|
|
311
|
+
);
|
|
312
|
+
};
|
|
313
|
+
lifecycleSyncRemote = remoteName;
|
|
314
|
+
let result;
|
|
315
|
+
try {
|
|
316
|
+
result = fn(ctx);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
throw wrap(err);
|
|
319
|
+
} finally {
|
|
320
|
+
lifecycleSyncRemote = void 0;
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
await result;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
throw wrap(err);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function runSetupPhase(remote, container, setupKey, st) {
|
|
329
|
+
remote.debug.setup = "pending";
|
|
330
|
+
try {
|
|
331
|
+
if (remote.manifestUrl) {
|
|
332
|
+
try {
|
|
333
|
+
await preloadRemote2(`${remote.name}/${setupKey}`);
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const mod = await container.get(setupKey);
|
|
338
|
+
const setupFn = mod?.default;
|
|
339
|
+
if (typeof setupFn !== "function") {
|
|
340
|
+
throw new FgError(
|
|
341
|
+
ErrorCodes.SETUP_INVALID_EXPORT,
|
|
342
|
+
`setup module "${setupKey}" of "${remote.name}": default export is ${mod == null ? String(mod) : typeof setupFn}, expected a function
|
|
343
|
+
expected: export default async function setup(context: RemoteSetupContext) { ... }
|
|
344
|
+
\u4FEE\u6CD5: \u5728 federation({ setup }) \u6307\u5411\u7684\u6587\u4EF6\u8865\u9ED8\u8BA4\u5BFC\u51FA\u51FD\u6570\uFF08\u5177\u540D onSession \u53EF\u9009\uFF1B\u5176\u4ED6\u5BFC\u51FA\u4E0D\u4F5C\u4E3A\u751F\u547D\u5468\u671F\u5165\u53E3\uFF09`,
|
|
345
|
+
{ remote: remote.name, module: setupKey }
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (mod.onSession !== void 0 && typeof mod.onSession !== "function") {
|
|
349
|
+
throw new FgError(
|
|
350
|
+
ErrorCodes.SETUP_INVALID_EXPORT,
|
|
351
|
+
`setup module "${setupKey}" of "${remote.name}": export "onSession" is ${typeof mod.onSession}, expected a function
|
|
352
|
+
\u4FEE\u6CD5: \u5C06 onSession \u4FEE\u6B63\u4E3A\u51FD\u6570\u5BFC\u51FA\uFF0C\u6216\u5220\u9664\u8BE5\u5177\u540D\u5BFC\u51FA\uFF08\u5176\u4ED6\u5BFC\u51FA\u540D\u4E0D\u4F5C\u4E3A\u751F\u547D\u5468\u671F\u5165\u53E3\uFF09`,
|
|
353
|
+
{ remote: remote.name, module: setupKey }
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
st.setupExports = mod;
|
|
357
|
+
await invokeLifecycleFn(
|
|
358
|
+
remote.name,
|
|
359
|
+
setupFn,
|
|
360
|
+
{ appContext: currentAppContext(), signal: (st.controller ??= new AbortController()).signal },
|
|
361
|
+
"setup"
|
|
362
|
+
);
|
|
363
|
+
remote.debug.setup = "ready";
|
|
364
|
+
} catch (err) {
|
|
365
|
+
remote.debug.setup = "failed";
|
|
366
|
+
st.setupExports = void 0;
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async function ensureSessionPhase(remote, st, onSession) {
|
|
371
|
+
const appContext = currentAppContext();
|
|
372
|
+
const sessionKey = appContext?.sessionKey;
|
|
373
|
+
if (typeof sessionKey !== "string" || sessionKey === "") {
|
|
374
|
+
throw new FgError(
|
|
375
|
+
ErrorCodes.SETUP_SESSION_KEY_MISSING,
|
|
376
|
+
`remote "${remote.name}" declares onSession but AppContext.sessionKey is ${sessionKey === void 0 ? "missing" : JSON.stringify(sessionKey)}
|
|
377
|
+
expected: \u5BBF\u4E3B\u767B\u5F55\u540E provideAppContext({ ..., sessionKey: '<\u975E\u654F\u611F\u767B\u5F55\u4EE3\u6B21 ID>' })
|
|
378
|
+
\u4FEE\u6CD5: \u5BBF\u4E3B\u767B\u5F55\u6D41\u7A0B\u6BCF\u6B21\u6210\u529F\u767B\u5F55/\u91CD\u767B\u751F\u6210\u65B0\u4EE3\u6B21 ID\uFF08\u7981\u6B62\u7528 token \u5145\u5F53\uFF09\uFF0C\u5E76\u5728 loadRemote \u524D\u63D0\u4F9B`,
|
|
379
|
+
{ remote: remote.name }
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
if (st.sessionPromise && st.sessionKey === sessionKey) return st.sessionPromise;
|
|
383
|
+
st.controller?.abort();
|
|
384
|
+
const controller = new AbortController();
|
|
385
|
+
st.controller = controller;
|
|
386
|
+
st.sessionKey = sessionKey;
|
|
387
|
+
const prev = st.sessionPromise ? st.sessionPromise.catch(() => {
|
|
388
|
+
}) : Promise.resolve();
|
|
389
|
+
const run = (async () => {
|
|
390
|
+
await prev;
|
|
391
|
+
if (controller.signal.aborted) return;
|
|
392
|
+
await invokeLifecycleFn(remote.name, onSession, { appContext, sessionKey, signal: controller.signal }, "onSession");
|
|
393
|
+
})();
|
|
394
|
+
st.sessionPromise = run;
|
|
395
|
+
run.catch(() => {
|
|
396
|
+
if (st.sessionKey === sessionKey) {
|
|
397
|
+
st.sessionKey = void 0;
|
|
398
|
+
st.sessionPromise = void 0;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return run;
|
|
402
|
+
}
|
|
403
|
+
async function ensureLifecycle(remote, container) {
|
|
404
|
+
const setupKey = container?.[SETUP_META_KEY];
|
|
405
|
+
if (typeof setupKey !== "string" || setupKey === "") return;
|
|
406
|
+
const st = lifecycleOf(remote.name);
|
|
407
|
+
if (!st.setupPromise) {
|
|
408
|
+
st.setupPromise = runSetupPhase(remote, container, setupKey, st).catch((err) => {
|
|
409
|
+
st.setupPromise = void 0;
|
|
410
|
+
throw err;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
await st.setupPromise;
|
|
414
|
+
const onSession = st.setupExports?.onSession;
|
|
415
|
+
if (typeof onSession === "function") {
|
|
416
|
+
await ensureSessionPhase(remote, st, onSession);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function clearSessionState2() {
|
|
420
|
+
for (const st of lifecycleStates.values()) {
|
|
421
|
+
try {
|
|
422
|
+
st.controller?.abort();
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
st.sessionKey = void 0;
|
|
426
|
+
st.sessionPromise = void 0;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
284
429
|
const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
|
|
285
430
|
function emitError(info) {
|
|
286
431
|
try {
|
|
@@ -340,7 +485,7 @@ function createRuntime() {
|
|
|
340
485
|
state: prev?.state ?? "idle",
|
|
341
486
|
breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
|
|
342
487
|
entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
|
|
343
|
-
debug: prev?.debug ?? { entry: r.entry, status: "idle" }
|
|
488
|
+
debug: prev?.debug ?? { entry: r.entry, status: "idle", setup: "none" }
|
|
344
489
|
});
|
|
345
490
|
}
|
|
346
491
|
}
|
|
@@ -609,6 +754,14 @@ function createRuntime() {
|
|
|
609
754
|
}
|
|
610
755
|
async function loadRemote2(spec, opts) {
|
|
611
756
|
const { remote: name, module: module2 } = parseSpec2(spec);
|
|
757
|
+
if (module2 && lifecycleSyncRemote === name) {
|
|
758
|
+
throw new FgError(
|
|
759
|
+
ErrorCodes.SETUP_RECURSION,
|
|
760
|
+
`loadRemote("${spec}") re-entered from setup/onSession of "${name}" \u2014 the initialization promise would await itself (deadlock)
|
|
761
|
+
\u4FEE\u6CD5: setup/onSession \u5185\u4E0D loadRemote \u540C\u4E00\u8FDC\u7A0B\u7684\u6A21\u5757\uFF1B\u9700\u8981\u7684\u4E1A\u52A1\u6A21\u5757\u6539\u4E3A\u5728\u9875\u9762\u7EC4\u4EF6\u5185\u52A0\u8F7D\uFF0C\u6216\u7ECF context \u901A\u9053\u89E3\u8026`,
|
|
762
|
+
{ remote: name }
|
|
763
|
+
);
|
|
764
|
+
}
|
|
612
765
|
try {
|
|
613
766
|
hooks.beforeLoadRemote?.({ remote: name, module: module2 });
|
|
614
767
|
} catch (hookErr) {
|
|
@@ -635,6 +788,7 @@ function createRuntime() {
|
|
|
635
788
|
throw err;
|
|
636
789
|
}
|
|
637
790
|
if (!module2) return container;
|
|
791
|
+
await ensureLifecycle(remote, container);
|
|
638
792
|
const cacheKey = `${name}@${remote.shareScope || "default"}#${module2}`;
|
|
639
793
|
if (!loadedModules.has(cacheKey)) {
|
|
640
794
|
loadedModules.set(
|
|
@@ -828,7 +982,8 @@ function createRuntime() {
|
|
|
828
982
|
loadRemote: loadRemote2,
|
|
829
983
|
getContainer: getContainer2,
|
|
830
984
|
preloadRemote: preloadRemote2,
|
|
831
|
-
parseSpec: parseSpec2
|
|
985
|
+
parseSpec: parseSpec2,
|
|
986
|
+
clearSessionState: clearSessionState2
|
|
832
987
|
};
|
|
833
988
|
}
|
|
834
989
|
var g = globalThis;
|
|
@@ -848,10 +1003,153 @@ var loadRemote = runtime.loadRemote;
|
|
|
848
1003
|
var getContainer = runtime.getContainer;
|
|
849
1004
|
var preloadRemote = runtime.preloadRemote;
|
|
850
1005
|
var parseSpec = runtime.parseSpec;
|
|
1006
|
+
var clearSessionState = runtime.clearSessionState;
|
|
851
1007
|
var shareScopeMap = runtime.shareScopeMap;
|
|
852
1008
|
|
|
853
1009
|
// src/vue-adapter.ts
|
|
854
1010
|
var import_vue = require("vue");
|
|
1011
|
+
|
|
1012
|
+
// src/pages.ts
|
|
1013
|
+
var normSpec = (s) => s.replace(/^[./]+/, "").replace(/\/+$/, "");
|
|
1014
|
+
var hasParam = (route) => /(^|\/):[\w]+/.test(route);
|
|
1015
|
+
var segs = (route) => route.split("/").filter(Boolean);
|
|
1016
|
+
function defaultDeriveSpec(route) {
|
|
1017
|
+
return segs(route).slice(1).filter((seg) => !seg.startsWith(":")).join("/");
|
|
1018
|
+
}
|
|
1019
|
+
function shadows(earlier, later) {
|
|
1020
|
+
const a = segs(earlier);
|
|
1021
|
+
const b = segs(later);
|
|
1022
|
+
if (a.length !== b.length) return null;
|
|
1023
|
+
if (earlier === later) return "full";
|
|
1024
|
+
let hasParam2 = false;
|
|
1025
|
+
for (let i = 0; i < a.length; i++) {
|
|
1026
|
+
if (a[i] === b[i]) continue;
|
|
1027
|
+
if (a[i].startsWith(":")) {
|
|
1028
|
+
hasParam2 = true;
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
return hasParam2 ? "param" : null;
|
|
1034
|
+
}
|
|
1035
|
+
function remoteOfRoute(route, remotes) {
|
|
1036
|
+
if (!remotes) return null;
|
|
1037
|
+
for (const [prefix, name] of Object.entries(remotes)) {
|
|
1038
|
+
if (route === prefix.replace(/\/+$/, "") || route.startsWith(prefix)) return name;
|
|
1039
|
+
}
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
function validatePages(pages, options = {}) {
|
|
1043
|
+
const deriveSpec = options.deriveSpec ?? defaultDeriveSpec;
|
|
1044
|
+
const violations = [];
|
|
1045
|
+
const effective = pages.map((p) => normSpec(p.spec ?? deriveSpec(p.route)));
|
|
1046
|
+
pages.forEach((p, i) => {
|
|
1047
|
+
if (p.spec || !hasParam(p.route)) return;
|
|
1048
|
+
pages.forEach((q, j) => {
|
|
1049
|
+
if (i === j) return;
|
|
1050
|
+
if (effective[j] !== effective[i]) return;
|
|
1051
|
+
violations.push({
|
|
1052
|
+
rule: "R1",
|
|
1053
|
+
level: "error",
|
|
1054
|
+
message: `[R1] \u8DEF\u7531 "${p.route}" \u5265\u53C2\u63A8\u5BFC spec "${effective[i]}" \u4E0E\u8DEF\u7531 "${q.route}"${q.spec ? "\uFF08\u663E\u5F0F spec\uFF09" : ""}\u6536\u655B\u76F8\u540C\u3002
|
|
1055
|
+
\u6839\u56E0\uFF1A\u53C2\u6570\u6BB5\u5265\u79BB\u540E expose \u952E\u4E0E\u5176\u5B83\u6761\u76EE\u76F8\u540C\uFF0C\u8BE5\u8DEF\u7531\u4F1A\u9759\u9ED8\u52A0\u8F7D\u9519\u8BEF\u7EC4\u4EF6
|
|
1056
|
+
\uFF08\u771F\u5B9E\u4E8B\u6545\uFF1A\u5E26\u53C2\u52A8\u4F5C\u9875\u8BEF\u8D70\u5217\u8868/\u66F4\u65B0\u8BED\u4E49\uFF0C\u6570\u636E\u98CE\u9669\uFF09\u3002
|
|
1057
|
+
\u4FEE\u6CD5\uFF1A\u4E3A\u8BE5\u6761\u76EE\u663E\u5F0F\u58F0\u660E spec: '...'\uFF08\u6307\u5411\u5E26\u53C2\u9875\u9762\u81EA\u5DF1\u7684 expose\uFF09\uFF0C\u6216\u8C03\u6574\u8DEF\u7531\u62C6\u5206\u3002`
|
|
1058
|
+
});
|
|
1059
|
+
});
|
|
1060
|
+
});
|
|
1061
|
+
const seenSpec = /* @__PURE__ */ new Map();
|
|
1062
|
+
pages.forEach((p, i) => {
|
|
1063
|
+
const key = effective[i];
|
|
1064
|
+
if (seenSpec.has(key)) {
|
|
1065
|
+
const first = pages[seenSpec.get(key)];
|
|
1066
|
+
const bothExplicit = !!p.spec && !!first.spec;
|
|
1067
|
+
violations.push({
|
|
1068
|
+
rule: "R2",
|
|
1069
|
+
level: "warn",
|
|
1070
|
+
message: `[R2] \u8DEF\u7531 "${p.route}" \u4E0E "${first.route}" \u6709\u6548 spec \u76F8\u540C\uFF08"${key}"\uFF09\u3002${bothExplicit ? "\u4E24\u6761\u5747\u4E3A\u663E\u5F0F spec" : "\u5176\u4E2D\u542B\u63A8\u5BFC spec"}\u2014\u2014\u82E5\u4E3A\u523B\u610F\u7684\u83DC\u5355\u522B\u540D\u53EF\u5FFD\u7565\u672C\u8B66\u544A\uFF0C\u5426\u5219\u8BF7\u6838\u5BF9\u3002`
|
|
1071
|
+
});
|
|
1072
|
+
} else {
|
|
1073
|
+
seenSpec.set(key, i);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
if (options.schema && options.remotes) {
|
|
1077
|
+
pages.forEach((p, i) => {
|
|
1078
|
+
const remote = remoteOfRoute(p.route, options.remotes);
|
|
1079
|
+
if (!remote) return;
|
|
1080
|
+
const entry = options.schema[remote];
|
|
1081
|
+
if (!entry || entry.exists === false) return;
|
|
1082
|
+
const exposes = new Set((entry.exposes ?? []).map(normSpec));
|
|
1083
|
+
if (!exposes.has(effective[i])) {
|
|
1084
|
+
violations.push({
|
|
1085
|
+
rule: "R3",
|
|
1086
|
+
level: "error",
|
|
1087
|
+
message: `[R3] \u8DEF\u7531 "${p.route}" \u7684 spec "${effective[i]}" \u4E0D\u5728\u8FDC\u7A0B "${remote}" \u7684 exposes \u6E05\u5355\u4E2D\u3002
|
|
1088
|
+
\u6839\u56E0\uFF1Aspec \u62FC\u5199\u4E0E\u8FDC\u7A0B exposes \u952E\u4E0D\u4E00\u81F4\uFF08\u8FD0\u884C\u65F6\u5C06 MFU-006 \u52A0\u8F7D\u5931\u8D25\uFF09\u3002
|
|
1089
|
+
\u4FEE\u6CD5\uFF1A\u6838\u5BF9\u8FDC\u7A0B vite.config exposes \u952E\u540D\uFF0C\u6216\u4FEE\u6B63\u672C\u8868 spec\u3002
|
|
1090
|
+
\u8FDC\u7A0B\u5B9E\u9645 exposes\uFF08\u524D 10\uFF09\uFF1A${[...exposes].slice(0, 10).join("\u3001") || "\uFF08\u7A7A\uFF09"}`
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
pages.forEach((p, i) => {
|
|
1096
|
+
pages.forEach((q, j) => {
|
|
1097
|
+
if (j <= i) return;
|
|
1098
|
+
const kind = shadows(p.route, q.route);
|
|
1099
|
+
if (!kind) return;
|
|
1100
|
+
violations.push({
|
|
1101
|
+
rule: "R4",
|
|
1102
|
+
level: "error",
|
|
1103
|
+
message: kind === "full" ? `[R4] \u8DEF\u7531\u5B8C\u5168\u91CD\u590D\uFF1A"${p.route}"\uFF08\u7B2C ${i + 1} \u6761\uFF09\u4E0E "${q.route}"\uFF08\u7B2C ${j + 1} \u6761\uFF09\u3002` : `[R4] \u8DEF\u7531 "${q.route}"\uFF08\u7B2C ${j + 1} \u6761\uFF09\u4F1A\u88AB\u66F4\u9760\u524D\u7684 "${p.route}" \u906E\u853D\uFF08\u5339\u914D\u5148\u5230\u5148\u5F97\uFF0C\u540E\u8005\u6210\u6B7B\u8DEF\u7531\uFF09\u3002
|
|
1104
|
+
\u4FEE\u6CD5\uFF1A\u8C03\u6574\u6570\u7EC4\u987A\u5E8F\uFF08\u66F4\u5177\u4F53\u7684\u9759\u6001\u8DEF\u7531\u653E\u524D\u9762\uFF09\u6216\u5408\u5E76\u6761\u76EE\u3002`
|
|
1105
|
+
});
|
|
1106
|
+
});
|
|
1107
|
+
});
|
|
1108
|
+
const seenName = /* @__PURE__ */ new Map();
|
|
1109
|
+
pages.forEach((p, i) => {
|
|
1110
|
+
const name = typeof p.name === "string" ? p.name : "";
|
|
1111
|
+
if (!name) return;
|
|
1112
|
+
if (seenName.has(name)) {
|
|
1113
|
+
violations.push({
|
|
1114
|
+
rule: "R5",
|
|
1115
|
+
level: "warn",
|
|
1116
|
+
message: `[R5] name "${name}" \u91CD\u590D\uFF08"${pages[seenName.get(name)].route}" \u4E0E "${p.route}"\uFF09\u2014\u2014vue-router \u547D\u540D\u8DF3\u8F6C push({name}) \u5C06\u547D\u4E2D\u524D\u8005\uFF0C\u8BF7\u6838\u5BF9\u3002`
|
|
1117
|
+
});
|
|
1118
|
+
} else {
|
|
1119
|
+
seenName.set(name, i);
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
return violations;
|
|
1123
|
+
}
|
|
1124
|
+
function formatViolations(violations) {
|
|
1125
|
+
const errors = violations.filter((v) => v.level === "error");
|
|
1126
|
+
const warns = violations.filter((v) => v.level === "warn");
|
|
1127
|
+
let out = "";
|
|
1128
|
+
if (errors.length) {
|
|
1129
|
+
out += `[fulgurjs] \u8DEF\u7531\u8868\u6821\u9A8C\u5931\u8D25\uFF08${errors.length} \u9879\uFF0CERROR\uFF09\uFF1A
|
|
1130
|
+
` + errors.map((v) => v.message).join("\n") + "\n";
|
|
1131
|
+
}
|
|
1132
|
+
if (warns.length) {
|
|
1133
|
+
out += `[fulgurjs] \u8DEF\u7531\u8868\u6821\u9A8C\u8B66\u544A\uFF08${warns.length} \u9879\uFF0CWARN\uFF09\uFF1A
|
|
1134
|
+
` + warns.map((v) => v.message).join("\n");
|
|
1135
|
+
}
|
|
1136
|
+
return out.trim();
|
|
1137
|
+
}
|
|
1138
|
+
function definePages(pages, options = {}) {
|
|
1139
|
+
const violations = validatePages(pages, options);
|
|
1140
|
+
const errors = violations.filter((v) => v.level === "error");
|
|
1141
|
+
const warns = violations.filter((v) => v.level === "warn");
|
|
1142
|
+
const text = formatViolations(violations);
|
|
1143
|
+
if (text) {
|
|
1144
|
+
if (errors.length && options.strict !== false) {
|
|
1145
|
+
throw new Error(text);
|
|
1146
|
+
}
|
|
1147
|
+
console[errors.length ? "error" : "warn"](text);
|
|
1148
|
+
}
|
|
1149
|
+
return pages;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// src/vue-adapter.ts
|
|
855
1153
|
var ERROR_STYLE = {
|
|
856
1154
|
padding: "16px",
|
|
857
1155
|
border: "1px solid #fde2e2",
|
|
@@ -888,10 +1186,111 @@ function createRemoteComponent(loadRemote2) {
|
|
|
888
1186
|
});
|
|
889
1187
|
};
|
|
890
1188
|
}
|
|
1189
|
+
var cleanCompName = (spec) => "Fulgurjs_" + spec.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
1190
|
+
function defaultDeriveSpec2(route) {
|
|
1191
|
+
return route.split("/").filter(Boolean).slice(1).filter((seg) => !seg.startsWith(":")).join("/");
|
|
1192
|
+
}
|
|
1193
|
+
function createHostPages(options, load) {
|
|
1194
|
+
const { pages, remotePrefixes, deriveSpec, schema, strict, base, beforeLoad, loadingComponent, errorComponent, delay } = options;
|
|
1195
|
+
definePages(pages, { deriveSpec, remotes: remotePrefixes, schema, strict });
|
|
1196
|
+
const prefixes = Object.entries(remotePrefixes).map(([p, name]) => [p.endsWith("/") ? p : `${p}/`, name]).sort((a, b) => b[0].length - a[0].length);
|
|
1197
|
+
const remoteOf = (route) => {
|
|
1198
|
+
for (const [prefix, name] of prefixes) {
|
|
1199
|
+
if (route.startsWith(prefix) || `${route}/` === prefix) return name;
|
|
1200
|
+
}
|
|
1201
|
+
throw new Error(
|
|
1202
|
+
`[fulgurjs] page route "${route}" matches none of remotePrefixes { ${Object.entries(remotePrefixes).map(([k, v]) => `'${k}': '${v}'`).join(", ")} }
|
|
1203
|
+
\u6839\u56E0: \u9875\u9762\u8868\u6761\u76EE\u7684\u8DEF\u7531\u4E0D\u5728\u4EFB\u4F55\u5DF2\u58F0\u660E\u7684\u8FDC\u7A0B\u524D\u7F00\u4E0B\u3002
|
|
1204
|
+
\u4FEE\u6CD5: \u5728 remotePrefixes \u8865\u5145\u8BE5\u8DEF\u7531\u524D\u7F00 \u2192 \u8FDC\u7A0B\u540D\u7684\u6620\u5C04\uFF0C\u6216\u4FEE\u6B63\u9875\u9762\u8868 route\u3002`
|
|
1205
|
+
);
|
|
1206
|
+
};
|
|
1207
|
+
for (const page of pages) remoteOf(page.route);
|
|
1208
|
+
const specOf = (page) => {
|
|
1209
|
+
const s = page.spec ?? (deriveSpec ? deriveSpec(page.route) : defaultDeriveSpec2(page.route));
|
|
1210
|
+
return `${remoteOf(page.route)}/${String(s).replace(/^[./]+/, "")}`;
|
|
1211
|
+
};
|
|
1212
|
+
const baseNorm = base && base !== "/" ? base.endsWith("/") ? base : `${base}/` : "";
|
|
1213
|
+
const matchSegments = (pattern, path) => {
|
|
1214
|
+
const patSegs = pattern.split("/").filter(Boolean);
|
|
1215
|
+
const pathSegs = path.split("/").filter(Boolean);
|
|
1216
|
+
if (patSegs.length !== pathSegs.length) return null;
|
|
1217
|
+
const params = {};
|
|
1218
|
+
for (let i = 0; i < patSegs.length; i++) {
|
|
1219
|
+
const seg = patSegs[i];
|
|
1220
|
+
if (seg.startsWith(":")) {
|
|
1221
|
+
try {
|
|
1222
|
+
params[seg.slice(1)] = decodeURIComponent(pathSegs[i]);
|
|
1223
|
+
} catch {
|
|
1224
|
+
return null;
|
|
1225
|
+
}
|
|
1226
|
+
} else if (seg !== pathSegs[i]) {
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
return params;
|
|
1231
|
+
};
|
|
1232
|
+
let cacheSession;
|
|
1233
|
+
const compCache = /* @__PURE__ */ new Map();
|
|
1234
|
+
const resolve = (rawPath) => {
|
|
1235
|
+
let clean = String(rawPath ?? "");
|
|
1236
|
+
if (baseNorm) {
|
|
1237
|
+
if (clean === baseNorm.replace(/\/$/, "")) clean = "/";
|
|
1238
|
+
else if (clean.startsWith(baseNorm)) clean = `/${clean.slice(baseNorm.length)}`;
|
|
1239
|
+
}
|
|
1240
|
+
clean = (clean.replace(/\/+$/, "") || "/").split("?")[0].split("#")[0];
|
|
1241
|
+
for (const page of pages) {
|
|
1242
|
+
const params = matchSegments(page.route, clean);
|
|
1243
|
+
if (params) return { page, remote: remoteOf(page.route), spec: specOf(page), params };
|
|
1244
|
+
}
|
|
1245
|
+
return null;
|
|
1246
|
+
};
|
|
1247
|
+
const component = (spec) => {
|
|
1248
|
+
const sk = (globalThis.__FULGURJS_APP_CONFIG__ ?? {}).sessionKey;
|
|
1249
|
+
const sessionKey = typeof sk === "string" ? sk : void 0;
|
|
1250
|
+
if (sessionKey !== cacheSession) {
|
|
1251
|
+
compCache.clear();
|
|
1252
|
+
cacheSession = sessionKey;
|
|
1253
|
+
}
|
|
1254
|
+
let comp = compCache.get(spec);
|
|
1255
|
+
if (!comp) {
|
|
1256
|
+
const name = cleanCompName(spec);
|
|
1257
|
+
const asyncComp = (0, import_vue.defineAsyncComponent)({
|
|
1258
|
+
loader: async () => {
|
|
1259
|
+
await beforeLoad?.();
|
|
1260
|
+
const mod = await load(spec);
|
|
1261
|
+
const inner = mod?.default ?? mod;
|
|
1262
|
+
if (!inner) {
|
|
1263
|
+
throw new Error(
|
|
1264
|
+
`[fulgurjs] remote module "${spec}" did not export a component (got ${inner === null ? "null" : typeof inner})\u3002
|
|
1265
|
+
\u4FEE\u6CD5: \u6838\u5BF9\u8FDC\u7A0B exposes \u952E\u540D\u4E0E\u9875\u9762\u8868 spec \u662F\u5426\u4E00\u81F4\uFF08MFU-006 \u8BED\u4E49\uFF09`
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
return inner;
|
|
1269
|
+
},
|
|
1270
|
+
loadingComponent,
|
|
1271
|
+
errorComponent: errorComponent ?? RemoteErrorPlaceholder,
|
|
1272
|
+
delay
|
|
1273
|
+
});
|
|
1274
|
+
comp = (0, import_vue.defineComponent)({
|
|
1275
|
+
name,
|
|
1276
|
+
inheritAttrs: false,
|
|
1277
|
+
setup: (_props, ctx) => () => (0, import_vue.h)(asyncComp, ctx.attrs, ctx.slots)
|
|
1278
|
+
});
|
|
1279
|
+
compCache.set(spec, comp);
|
|
1280
|
+
}
|
|
1281
|
+
return comp;
|
|
1282
|
+
};
|
|
1283
|
+
const keepAliveNames = pages.filter((p) => p.keepAlive).map((p) => cleanCompName(specOf(p)));
|
|
1284
|
+
return { pages, resolve, component, keepAliveNames };
|
|
1285
|
+
}
|
|
891
1286
|
|
|
892
1287
|
// src/vue.ts
|
|
893
1288
|
var remoteComponent = createRemoteComponent(loadRemote);
|
|
1289
|
+
function createHostPages2(options) {
|
|
1290
|
+
return createHostPages(options, loadRemote);
|
|
1291
|
+
}
|
|
894
1292
|
// Annotate the CommonJS export names for ESM import in node:
|
|
895
1293
|
0 && (module.exports = {
|
|
1294
|
+
createHostPages,
|
|
896
1295
|
remoteComponent
|
|
897
1296
|
});
|
package/dist/vue.d.cts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
|
-
import { RemoteComponentOptions } from './vue-adapter.cjs';
|
|
2
|
+
import { createHostPages as createHostPages$1, HostPages, RemoteComponentOptions } from './vue-adapter.cjs';
|
|
3
|
+
export { HostPagesOptions, ResolvedHostPage } from './vue-adapter.cjs';
|
|
4
|
+
import './pages.cjs';
|
|
3
5
|
|
|
4
6
|
declare const remoteComponent: (spec: string, opts?: RemoteComponentOptions) => vue.Component;
|
|
7
|
+
/** 宿主页面适配器(绑定本包运行时的 loadRemote;选项与返回值类型见 vue-adapter) */
|
|
8
|
+
declare function createHostPages(options: Parameters<typeof createHostPages$1>[0]): HostPages;
|
|
5
9
|
|
|
6
|
-
export { RemoteComponentOptions, remoteComponent };
|
|
10
|
+
export { HostPages, RemoteComponentOptions, createHostPages, remoteComponent };
|
package/dist/vue.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
|
-
import { RemoteComponentOptions } from './vue-adapter.js';
|
|
2
|
+
import { createHostPages as createHostPages$1, HostPages, RemoteComponentOptions } from './vue-adapter.js';
|
|
3
|
+
export { HostPagesOptions, ResolvedHostPage } from './vue-adapter.js';
|
|
4
|
+
import './pages.js';
|
|
3
5
|
|
|
4
6
|
declare const remoteComponent: (spec: string, opts?: RemoteComponentOptions) => vue.Component;
|
|
7
|
+
/** 宿主页面适配器(绑定本包运行时的 loadRemote;选项与返回值类型见 vue-adapter) */
|
|
8
|
+
declare function createHostPages(options: Parameters<typeof createHostPages$1>[0]): HostPages;
|
|
5
9
|
|
|
6
|
-
export { RemoteComponentOptions, remoteComponent };
|
|
10
|
+
export { HostPages, RemoteComponentOptions, createHostPages, remoteComponent };
|
package/dist/vue.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { loadRemote } from "./runtime.js";
|
|
2
|
-
import { createRemoteComponent } from "./vue-adapter.js";
|
|
2
|
+
import { createRemoteComponent, createHostPages as createHostPagesWithLoader } from "./vue-adapter.js";
|
|
3
3
|
export const remoteComponent = createRemoteComponent(loadRemote);
|
|
4
|
+
export const createHostPages = (options) => createHostPagesWithLoader(options, loadRemote);
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
|---------|--------------------|
|
|
9
9
|
| 主应用 registerMicroApps | 宿主 `federation({ remotes })` |
|
|
10
10
|
| 子应用 entry(HTML) | remote entry(dev: `@fulgurjs-entry.js` 中间件 / prod: `fulgurjs-remoteEntry.js`) |
|
|
11
|
-
| 子应用生命周期 mount/unmount | 页面级 exposes
|
|
11
|
+
| 子应用生命周期 mount/unmount | 页面级 exposes(组件即入口,无生命周期样板);启动期初始化 = 远程 `federation({ setup })`(setup/onSession,见三C 节) |
|
|
12
12
|
| 主子应用 window 隔离/沙箱 | 无沙箱:同 realm 直渲染(`window.xxx` 全局桥自然可用) |
|
|
13
13
|
| props 传递(qiankun 传参) | 组件 props(路由通道 = params+query 合并;弹窗通道 = 显式 v-bind) |
|
|
14
14
|
| 公共依赖 externals/主应用共享 | `shared`(singleton 协商,"已加载优先") |
|
|
15
|
-
| `qiankun` 运行时 + single-spa | `@fulgurjs/federation/runtime`(运行时内核
|
|
15
|
+
| `qiankun` 运行时 + single-spa | `@fulgurjs/federation/runtime`(运行时内核 20KB 级,无 single-spa) |
|
|
16
16
|
|
|
17
17
|
应用代码统一从 `@fulgurjs/federation/runtime` 导入。Vite 配置继续从 `@fulgurjs/federation` 导入插件。升级自 3.x 时,将 `virtual:fulgurjs-api` 替换为物理子路径;`remoteSchema` 仍按具名导入,开发态由插件填充,构建态为空清单。
|
|
18
18
|
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
1. **子应用装插件 + exposes**:把原菜单路由表逐页映射为 `exposes`(`./pages/bpm/task/todo` 对应路由 `bpm/task/todo`)。
|
|
22
22
|
2. **shared 声明**:vue/pinia/vue-router 设 singleton;**不要**把业务库(element-plus/avue/dayjs 等)放 shared——插件会在预构建与构建期自动处理其内部对 shared 键的引用(UMD/CJS-only 依赖支持)。
|
|
23
23
|
3. **宿主装插件 + remotes**:菜单 URL 通道由 `resolveFulgurjsPageFromPath` 按路由表解析 spec(见 `admin/src/qiankun/fulgurjsPages.ts`)。
|
|
24
|
-
4. **全局环境迁移**:子应用依赖宿主的全局(用户/字典 store、i18n
|
|
24
|
+
4. **全局环境迁移**:子应用依赖宿主的全局(用户/字典 store、i18n、全局组件、全局样式)→ 在子应用 `federation({ setup })` 声明初始化入口文件:默认导出 `setup(context)` 应用级执行一次(全局组件/样式/locale),可选具名导出 `onSession(context)` 按宿主 sessionKey 去重同步用户/权限/字典(换账号自动重跑);宿主正常 `loadRemote` 页面即可,**无需手写「loadRemote 启动器并调用」**;失败必须显式报错(H3,MFU-011~014)。
|
|
25
|
+
> 旧写法(`exposes` 启动器 + 宿主 `bootRemoteOnce` 手动调用)**继续可用**,属兼容形态,新迁移项目请用 `setup`。
|
|
25
26
|
5. **路由参数**:页面组件用 `defineProps` 声明参数(id/taskId 等);宿主路由通道把 params+query 作为 props 传入。
|
|
26
27
|
6. **跨应用弹窗**:原 iframe 嵌子应用页 → `fulgurjsPageComponent(spec)` 直渲染 + props(见 FormDetailsModal);真外链(数据驱动 URL、AMIS 站点)保留 iframe。
|
|
27
28
|
7. **公共依赖坑**:
|
|
@@ -82,15 +83,17 @@ realm 检测等特殊场景,常规集成无需使用。
|
|
|
82
83
|
原 qiankun `props` / `globalState` / `window.*` 裸口子,统一收编为 context 一等公民通道(对标乾坤 props,带类型与错误契约):
|
|
83
84
|
|
|
84
85
|
```ts
|
|
85
|
-
// 宿主桥(admin/src/fulgurjs/host/bridge.ts
|
|
86
|
-
import { provideAppContext } from '@fulgurjs/federation/runtime'
|
|
86
|
+
// 宿主桥(admin/src/fulgurjs/host/bridge.ts)——登录态同步(可多次调用,幂等 merge)
|
|
87
|
+
import { provideAppContext, clearAppContext } from '@fulgurjs/federation/runtime'
|
|
87
88
|
provideAppContext({
|
|
88
89
|
user, getToken, store, hostApp, locale, // 只传有真实消费的键(token 一律 getToken() 拉取)
|
|
90
|
+
sessionKey: 's-101-…', // 非敏感登录代次 ID:每次成功登录/重登生成新值(禁用 token 充当)
|
|
89
91
|
events: { main: mainEvents }, // 方法池:宿主提供 main.*,子应用反向注册 bpm.* / lowcode.*
|
|
90
92
|
// 项目扩展位:自定义键按需自行提供(如 baseUrl)
|
|
91
93
|
})
|
|
94
|
+
// 退出登录:clearAppContext()(清 context + 作废远程会话信号/onSession 去重)
|
|
92
95
|
|
|
93
|
-
//
|
|
96
|
+
// 远程初始化(xxx/src/fulgurjs/setup.ts,经 vite federation({ setup }) 声明)——显式校验消费
|
|
94
97
|
import { requireAppContext } from '@fulgurjs/federation/runtime'
|
|
95
98
|
const { store, user, hostApp } = requireAppContext('store', 'user', 'hostApp')
|
|
96
99
|
// 缺键 → [fulgurjs:CC-001](三段式,指向宿主桥);独立直开远程页 → [fulgurjs:CC-002]
|
|
@@ -102,8 +105,8 @@ const dict = getAppContext().events?.main?.getDictItems?.('sex')
|
|
|
102
105
|
|
|
103
106
|
要点:
|
|
104
107
|
|
|
105
|
-
- **时序契约**:bridge(provide)→
|
|
106
|
-
- **数据语义**:传输层快照 + 函数引用,非响应式(与乾坤 props 同语义)。"实时"
|
|
108
|
+
- **时序契约**:bridge(provide)→ 远程 setup/onSession(require)→ 页面模块返回;违反即在初始化处显式失败(CC-001 / MFU-013),无静默;
|
|
109
|
+
- **数据语义**:传输层快照 + 函数引用,非响应式(与乾坤 props 同语义)。"实时"两通道 = ①函数引用拉取(`getToken()` / `events.main.*` 每次执行宿主最新闭包)②宿主 pinia 共享响应式(`useUserStore(ctx.store)`);**同页换账号不依赖页面刷新**——宿主重新 provide 最新 context + 新 sessionKey,远程 onSession 检测到新代次自动重跑;
|
|
107
110
|
- **嵌套对象(events)是引用共享**:子应用挂属性即时可见(同 realm 直引用);
|
|
108
111
|
- **方法引用两条通道**:高频热路径(token/字典/文件 URL、反向注册)走 context 函数引用;低频/重逻辑跨应用调用走 exposes 方法模块——远程 `src/fulgurjs/exposes/api.ts` 导出纯函数(不挂 Vue 组件),对面 `await loadRemote('remote/api')`;
|
|
109
112
|
|
package/package.json
CHANGED
package/dist/chunk-AYS56IRF.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// src/vue-adapter.ts
|
|
2
|
-
import { defineAsyncComponent, defineComponent, h } from "vue";
|
|
3
|
-
var ERROR_STYLE = {
|
|
4
|
-
padding: "16px",
|
|
5
|
-
border: "1px solid #fde2e2",
|
|
6
|
-
borderRadius: "4px",
|
|
7
|
-
background: "#fef0f0",
|
|
8
|
-
color: "#c45656",
|
|
9
|
-
fontSize: "13px",
|
|
10
|
-
lineHeight: "1.6"
|
|
11
|
-
};
|
|
12
|
-
var RemoteErrorPlaceholder = defineComponent({
|
|
13
|
-
name: "FulgurjsRemoteError",
|
|
14
|
-
props: { error: { type: Object, default: void 0 } },
|
|
15
|
-
setup(props) {
|
|
16
|
-
return () => {
|
|
17
|
-
const err = props.error;
|
|
18
|
-
const code = err?.code ?? "UNKNOWN";
|
|
19
|
-
const message = err?.message ?? String(props.error ?? "unknown error");
|
|
20
|
-
return h("div", { style: ERROR_STYLE }, [
|
|
21
|
-
h("p", { style: "margin:0 0 4px;font-weight:600" }, `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
|
|
22
|
-
h("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
|
|
23
|
-
h("p", { style: "margin:0" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002")
|
|
24
|
-
]);
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
function createRemoteComponent(loadRemote) {
|
|
29
|
-
return function remoteComponent(spec, opts = {}) {
|
|
30
|
-
return defineAsyncComponent({
|
|
31
|
-
loader: () => loadRemote(spec, { retries: opts.retries }).then((m) => m.default ?? m),
|
|
32
|
-
loadingComponent: opts.loadingComponent,
|
|
33
|
-
errorComponent: opts.errorComponent ?? RemoteErrorPlaceholder,
|
|
34
|
-
delay: opts.delay,
|
|
35
|
-
timeout: opts.timeout
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export {
|
|
41
|
-
createRemoteComponent
|
|
42
|
-
};
|