@fulgurjs/federation 5.0.4 → 5.1.1
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 +18 -0
- package/DESIGN.md +11 -0
- package/README.en.md +356 -0
- package/README.md +117 -4
- package/dist/chunk-4PLNAYZX.js +84 -0
- package/dist/cli.js +4 -1
- package/dist/host-pages-core-BFxp-IOj.d.cts +37 -0
- package/dist/host-pages-core-DLfMeDrO.d.ts +37 -0
- package/dist/index.cjs +282 -27
- package/dist/index.js +282 -27
- package/dist/react-adapter.cjs +521 -0
- package/dist/react-adapter.d.cts +117 -0
- package/dist/react-adapter.d.ts +117 -0
- package/dist/react-adapter.js +295 -0
- package/dist/react.d.ts +442 -0
- package/dist/react.js +10 -0
- package/dist/runtime-entry.d.ts +29 -17
- package/dist/runtime.js +13 -13
- package/dist/vue-adapter.cjs +66 -50
- package/dist/vue-adapter.d.cts +4 -25
- package/dist/vue-adapter.d.ts +4 -25
- package/dist/vue-adapter.js +14 -66
- package/examples/README.md +5 -0
- package/examples/react-host/README.md +28 -0
- package/examples/react-host/fulgurjs.config.ts +19 -0
- package/examples/react-host/index.html +11 -0
- package/examples/react-host/package.json +23 -0
- package/examples/react-host/src/App.tsx +66 -0
- package/examples/react-host/src/federation/pages.data.ts +21 -0
- package/examples/react-host/src/main.tsx +8 -0
- package/examples/react-host/tsconfig.json +14 -0
- package/examples/react-host/vite.config.ts +8 -0
- package/examples/react-remote/fulgurjs.config.ts +17 -0
- package/examples/react-remote/index.html +11 -0
- package/examples/react-remote/package.json +22 -0
- package/examples/react-remote/src/Button.tsx +15 -0
- package/examples/react-remote/src/main.tsx +6 -0
- package/examples/react-remote/src/pages/Detail.tsx +10 -0
- package/examples/react-remote/src/pages/Home.tsx +14 -0
- package/examples/react-remote/src/utils.ts +3 -0
- package/examples/react-remote/tsconfig.json +14 -0
- package/examples/react-remote/vite.config.ts +8 -0
- package/package.json +30 -3
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePages
|
|
3
|
+
} from "./chunk-TSXZ7TWA.js";
|
|
4
|
+
|
|
5
|
+
// src/host-pages-core.ts
|
|
6
|
+
var cleanCompName = (spec) => "Fulgurjs_" + spec.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
7
|
+
function defaultDeriveSpec(route) {
|
|
8
|
+
return route.split("/").filter(Boolean).slice(1).filter((seg) => !seg.startsWith(":")).join("/");
|
|
9
|
+
}
|
|
10
|
+
function readSessionKey() {
|
|
11
|
+
const sk = (globalThis.__FULGURJS_APP_CONFIG__ ?? {}).sessionKey;
|
|
12
|
+
return typeof sk === "string" && sk !== "" ? sk : void 0;
|
|
13
|
+
}
|
|
14
|
+
function shouldResetSessionCache(sessionKey, cached) {
|
|
15
|
+
return sessionKey !== void 0 && sessionKey !== cached;
|
|
16
|
+
}
|
|
17
|
+
function noRenderableExportError(spec, inner) {
|
|
18
|
+
return new Error(
|
|
19
|
+
`[fulgurjs] \u8FDC\u7A0B\u6A21\u5757 "${spec}" \u6CA1\u6709\u5BFC\u51FA\u53EF\u6E32\u67D3\u7EC4\u4EF6\uFF1B\u5F53\u524D\u503C\u7C7B\u578B\u4E3A ${inner === null ? "null" : typeof inner}\u3002
|
|
20
|
+
\u4FEE\u6CD5: \u6838\u5BF9\u8FDC\u7A0B exposes \u952E\u540D\u4E0E\u9875\u9762\u8868 spec \u662F\u5426\u4E00\u81F4\uFF08MFU-006 \u8BED\u4E49\uFF09`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function createHostPagesCore(options) {
|
|
24
|
+
const { pages, remotePrefixes, deriveSpec, schema, strict, base } = options;
|
|
25
|
+
definePages(pages, { deriveSpec, remotes: remotePrefixes, schema, strict });
|
|
26
|
+
const prefixes = Object.entries(remotePrefixes).map(([p, name]) => [p.endsWith("/") ? p : `${p}/`, name]).sort((a, b) => b[0].length - a[0].length);
|
|
27
|
+
const remoteOf = (route) => {
|
|
28
|
+
for (const [prefix, name] of prefixes) {
|
|
29
|
+
if (route.startsWith(prefix) || `${route}/` === prefix) return name;
|
|
30
|
+
}
|
|
31
|
+
throw new Error(
|
|
32
|
+
`[fulgurjs] \u9875\u9762\u8DEF\u7531 "${route}" \u672A\u5339\u914D\u4EFB\u4F55 remotePrefixes\uFF1A{ ${Object.entries(remotePrefixes).map(([k, v]) => `'${k}': '${v}'`).join(", ")} }
|
|
33
|
+
\u6839\u56E0: \u9875\u9762\u8868\u6761\u76EE\u7684\u8DEF\u7531\u4E0D\u5728\u4EFB\u4F55\u5DF2\u58F0\u660E\u7684\u8FDC\u7A0B\u524D\u7F00\u4E0B\u3002
|
|
34
|
+
\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`
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
for (const page of pages) remoteOf(page.route);
|
|
38
|
+
const specOf = (page) => {
|
|
39
|
+
const s = page.spec ?? (deriveSpec ? deriveSpec(page.route) : defaultDeriveSpec(page.route));
|
|
40
|
+
return `${remoteOf(page.route)}/${String(s).replace(/^[./]+/, "")}`;
|
|
41
|
+
};
|
|
42
|
+
const baseNorm = base && base !== "/" ? base.endsWith("/") ? base : `${base}/` : "";
|
|
43
|
+
const matchSegments = (pattern, path) => {
|
|
44
|
+
const patSegs = pattern.split("/").filter(Boolean);
|
|
45
|
+
const pathSegs = path.split("/").filter(Boolean);
|
|
46
|
+
if (patSegs.length !== pathSegs.length) return null;
|
|
47
|
+
const params = {};
|
|
48
|
+
for (let i = 0; i < patSegs.length; i++) {
|
|
49
|
+
const seg = patSegs[i];
|
|
50
|
+
if (seg.startsWith(":")) {
|
|
51
|
+
try {
|
|
52
|
+
params[seg.slice(1)] = decodeURIComponent(pathSegs[i]);
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
} else if (seg !== pathSegs[i]) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return params;
|
|
61
|
+
};
|
|
62
|
+
const resolve = (rawPath) => {
|
|
63
|
+
let clean = String(rawPath ?? "");
|
|
64
|
+
if (baseNorm) {
|
|
65
|
+
if (clean === baseNorm.replace(/\/$/, "")) clean = "/";
|
|
66
|
+
else if (clean.startsWith(baseNorm)) clean = `/${clean.slice(baseNorm.length)}`;
|
|
67
|
+
}
|
|
68
|
+
clean = (clean.replace(/\/+$/, "") || "/").split("?")[0].split("#")[0];
|
|
69
|
+
for (const page of pages) {
|
|
70
|
+
const params = matchSegments(page.route, clean);
|
|
71
|
+
if (params) return { page, remote: remoteOf(page.route), spec: specOf(page), params };
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
return { pages, resolve, remoteOf, specOf };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
cleanCompName,
|
|
80
|
+
readSessionKey,
|
|
81
|
+
shouldResetSessionCache,
|
|
82
|
+
noRenderableExportError,
|
|
83
|
+
createHostPagesCore
|
|
84
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -346,7 +346,10 @@ async function loadAppConfig(configPath) {
|
|
|
346
346
|
try {
|
|
347
347
|
const bundleFile = path2.join(tmpDir, "fulgurjs.config.bundle.mjs");
|
|
348
348
|
fs3.writeFileSync(bundleFile, out.outputFiles[0].text);
|
|
349
|
-
const mod = await import(
|
|
349
|
+
const mod = await import(
|
|
350
|
+
/* @vite-ignore */
|
|
351
|
+
`${pathToFileURL(bundleFile).href}?t=${Date.now()}`
|
|
352
|
+
);
|
|
350
353
|
if (isAggregateShape(mod.default)) aggregateMigrationError();
|
|
351
354
|
return finishAppShape(mod.default, { hostPages: mod.hostPages }, abs, path2.dirname(abs), fs3, path2);
|
|
352
355
|
} finally {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PageRouteLike, RemoteSchemaEntry } from './pages.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 宿主页面表纯解析核心(框架无关)。
|
|
5
|
+
*
|
|
6
|
+
* 5.1.0 从 vue-adapter 提取:definePages 校验接入、最长前缀远程归属、缺省 spec 推导、
|
|
7
|
+
* base 剥离、段式参数匹配——Vue 与 React 适配器共用同一份解析,组件缓存/错误边界/
|
|
8
|
+
* 保活策略由各框架适配器自行负责(不把 Vue 的卸载策略强加给 React,反之亦然)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** 页面表数据项(两个框架适配器共用;展示项 fallback/error 等在各适配器定义) */
|
|
12
|
+
interface HostPagesCoreOptions {
|
|
13
|
+
/** 页面表(宿主路由与布局共用的唯一数据源;definePages R1–R5 校验照常执行) */
|
|
14
|
+
pages: PageRouteLike[];
|
|
15
|
+
/** 路由前缀 → remote 名(最长前缀匹配;页面路由无匹配前缀时创建期即报错) */
|
|
16
|
+
remotePrefixes: Record<string, string>;
|
|
17
|
+
/** 缺省 spec 推导(缺省 = 去首段前缀 + 剥 :参数 段,同 definePages 默认) */
|
|
18
|
+
deriveSpec?: (route: string) => string;
|
|
19
|
+
/** 远程 exposes 清单(dev 由 remoteSchema 提供;build 为空表时按语义诚实降级) */
|
|
20
|
+
schema?: Record<string, RemoteSchemaEntry>;
|
|
21
|
+
/** ERROR 级校验失败是否 throw(默认 true) */
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
/** 站点 base 前缀(如 '/main'):resolve 时先剥离再匹配路由空间 */
|
|
24
|
+
base?: string;
|
|
25
|
+
}
|
|
26
|
+
interface ResolvedHostPage {
|
|
27
|
+
/** 原页面记录(含宿主自由扩展字段 name/title/keepAlive/...) */
|
|
28
|
+
page: PageRouteLike;
|
|
29
|
+
/** 命中的 remote 名 */
|
|
30
|
+
remote: string;
|
|
31
|
+
/** 完整加载 spec(`<remote>/<exposes 键>`,loadRemote 直用) */
|
|
32
|
+
spec: string;
|
|
33
|
+
/** 路径参数(解码失败只让该次匹配失败,不让页面初始化崩溃) */
|
|
34
|
+
params: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type { HostPagesCoreOptions as H, ResolvedHostPage as R };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PageRouteLike, RemoteSchemaEntry } from './pages.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 宿主页面表纯解析核心(框架无关)。
|
|
5
|
+
*
|
|
6
|
+
* 5.1.0 从 vue-adapter 提取:definePages 校验接入、最长前缀远程归属、缺省 spec 推导、
|
|
7
|
+
* base 剥离、段式参数匹配——Vue 与 React 适配器共用同一份解析,组件缓存/错误边界/
|
|
8
|
+
* 保活策略由各框架适配器自行负责(不把 Vue 的卸载策略强加给 React,反之亦然)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** 页面表数据项(两个框架适配器共用;展示项 fallback/error 等在各适配器定义) */
|
|
12
|
+
interface HostPagesCoreOptions {
|
|
13
|
+
/** 页面表(宿主路由与布局共用的唯一数据源;definePages R1–R5 校验照常执行) */
|
|
14
|
+
pages: PageRouteLike[];
|
|
15
|
+
/** 路由前缀 → remote 名(最长前缀匹配;页面路由无匹配前缀时创建期即报错) */
|
|
16
|
+
remotePrefixes: Record<string, string>;
|
|
17
|
+
/** 缺省 spec 推导(缺省 = 去首段前缀 + 剥 :参数 段,同 definePages 默认) */
|
|
18
|
+
deriveSpec?: (route: string) => string;
|
|
19
|
+
/** 远程 exposes 清单(dev 由 remoteSchema 提供;build 为空表时按语义诚实降级) */
|
|
20
|
+
schema?: Record<string, RemoteSchemaEntry>;
|
|
21
|
+
/** ERROR 级校验失败是否 throw(默认 true) */
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
/** 站点 base 前缀(如 '/main'):resolve 时先剥离再匹配路由空间 */
|
|
24
|
+
base?: string;
|
|
25
|
+
}
|
|
26
|
+
interface ResolvedHostPage {
|
|
27
|
+
/** 原页面记录(含宿主自由扩展字段 name/title/keepAlive/...) */
|
|
28
|
+
page: PageRouteLike;
|
|
29
|
+
/** 命中的 remote 名 */
|
|
30
|
+
remote: string;
|
|
31
|
+
/** 完整加载 spec(`<remote>/<exposes 键>`,loadRemote 直用) */
|
|
32
|
+
spec: string;
|
|
33
|
+
/** 路径参数(解码失败只让该次匹配失败,不让页面初始化崩溃) */
|
|
34
|
+
params: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type { HostPagesCoreOptions as H, ResolvedHostPage as R };
|