@fulgurjs/federation 4.0.0 → 4.2.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 +49 -0
- package/README.md +369 -138
- 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/{chunk-X4LZDAZQ.js → chunk-XIO5Y2IM.js} +1 -1
- package/dist/cli.js +1149 -208
- package/dist/config.cjs +66 -6
- package/dist/config.d.cts +31 -4
- package/dist/config.d.ts +31 -4
- package/dist/config.js +65 -6
- 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 +20 -17
- package/examples/fulgurjs.config.example.ts +29 -37
- package/package.json +1 -1
- package/dist/chunk-AYS56IRF.js +0 -42
package/dist/cli.js
CHANGED
|
@@ -9,52 +9,208 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/manifest.ts
|
|
13
|
+
function validAssetRef(v) {
|
|
14
|
+
if (typeof v !== "string" || v.length === 0) return false;
|
|
15
|
+
if (/[\s\u0000-\u001f]/.test(v)) return false;
|
|
16
|
+
if (/^https?:\/\/[^/@\s]+:[^/@\s]+@/.test(v)) return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function validExposeName(v) {
|
|
20
|
+
return typeof v === "string" && /^\.[\w./-]+$/.test(v);
|
|
21
|
+
}
|
|
22
|
+
function parseManifest(input) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
if (!isPlainObject(input)) {
|
|
25
|
+
return { issues: [{ field: "$", message: "manifest \u5FC5\u987B\u662F JSON \u5BF9\u8C61", got: typeof input }] };
|
|
26
|
+
}
|
|
27
|
+
const m = input;
|
|
28
|
+
let schemaVersion = MANIFEST_SCHEMA_VERSION;
|
|
29
|
+
if (m.schemaVersion !== void 0) {
|
|
30
|
+
if (typeof m.schemaVersion !== "number" || !Number.isInteger(m.schemaVersion)) {
|
|
31
|
+
issues.push({ field: "schemaVersion", message: "\u5FC5\u987B\u662F\u6574\u6570", got: m.schemaVersion });
|
|
32
|
+
} else if (m.schemaVersion !== MANIFEST_SCHEMA_VERSION) {
|
|
33
|
+
return {
|
|
34
|
+
issues: [
|
|
35
|
+
{
|
|
36
|
+
field: "schemaVersion",
|
|
37
|
+
message: `\u4E0D\u652F\u6301\u7684 manifest \u4E3B\u7248\u672C\uFF08\u5F53\u524D\u6D88\u8D39\u7AEF\u652F\u6301 ${MANIFEST_SCHEMA_VERSION}\uFF09`,
|
|
38
|
+
got: m.schemaVersion
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
unsupportedVersion: m.schemaVersion
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (typeof m.name !== "string" || m.name.length === 0) {
|
|
46
|
+
issues.push({ field: "name", message: "\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32", got: m.name });
|
|
47
|
+
}
|
|
48
|
+
const exposes = m.exposes;
|
|
49
|
+
const shared = m.shared;
|
|
50
|
+
const sharedIssues = validateShared(shared);
|
|
51
|
+
issues.push(...sharedIssues);
|
|
52
|
+
if (Array.isArray(exposes)) {
|
|
53
|
+
if (m.devServer !== true) {
|
|
54
|
+
issues.push({ field: "devServer", message: "dev \u6570\u7EC4\u5F62\u6001 exposes \u5FC5\u987B\u4F34\u968F devServer: true", got: m.devServer });
|
|
55
|
+
}
|
|
56
|
+
if (typeof m.base !== "string" || !/^\/(?:.*\/)?$/.test(m.base)) {
|
|
57
|
+
issues.push({ field: "base", message: "\u5FC5\u987B\u662F\u4EE5 / \u5F00\u5934\u4E14 / \u7ED3\u5C3E\u7684\u8DEF\u5F84", got: m.base });
|
|
58
|
+
}
|
|
59
|
+
if (!validAssetRef(m.entry)) {
|
|
60
|
+
issues.push({ field: "entry", message: "\u5FC5\u987B\u662F\u5408\u6CD5\u8D44\u6E90\u5730\u5740", got: m.entry });
|
|
61
|
+
}
|
|
62
|
+
for (const [i, e] of exposes.entries()) {
|
|
63
|
+
if (!isPlainObject(e)) {
|
|
64
|
+
issues.push({ field: `exposes[${i}]`, message: "\u5FC5\u987B\u662F\u5BF9\u8C61", got: typeof e });
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!validExposeName(e.name)) {
|
|
68
|
+
issues.push({ field: `exposes[${i}].name`, message: '\u5FC5\u987B\u662F "./xxx" \u5F62\u6001\u7684 expose \u540D', got: e.name });
|
|
69
|
+
}
|
|
70
|
+
if (typeof e.src !== "string" || e.src.length === 0) {
|
|
71
|
+
issues.push({ field: `exposes[${i}].src`, message: "dev \u5F62\u6001\u5FC5\u987B\u63D0\u4F9B\u6E90\u7801\u76F8\u5BF9\u8DEF\u5F84", got: e.src });
|
|
72
|
+
}
|
|
73
|
+
if (!validAssetRef(e.file)) {
|
|
74
|
+
issues.push({ field: `exposes[${i}].file`, message: "\u5FC5\u987B\u662F\u53EF\u8BF7\u6C42\u7684\u6A21\u5757\u5730\u5740", got: e.file });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} else if (isPlainObject(exposes)) {
|
|
78
|
+
if (!validAssetRef(m.entry)) {
|
|
79
|
+
issues.push({ field: "entry", message: "\u5FC5\u987B\u662F\u5408\u6CD5\u8D44\u6E90\u5730\u5740\uFF08\u76F8\u5BF9\u57FA\u51C6 = \u6240\u5728\u76EE\u5F55\uFF09", got: m.entry });
|
|
80
|
+
}
|
|
81
|
+
for (const [key, v] of Object.entries(exposes)) {
|
|
82
|
+
if (!validExposeName(key)) {
|
|
83
|
+
issues.push({ field: `exposes["${key}"]`, message: '\u952E\u5FC5\u987B\u662F "./xxx" \u5F62\u6001\u7684 expose \u540D' });
|
|
84
|
+
}
|
|
85
|
+
if (!isPlainObject(v)) {
|
|
86
|
+
issues.push({ field: `exposes["${key}"]`, message: "\u503C\u5FC5\u987B\u662F\u5BF9\u8C61", got: typeof v });
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!validAssetRef(v.file)) {
|
|
90
|
+
issues.push({ field: `exposes["${key}"].file`, message: "\u5FC5\u987B\u662F\u8D44\u6E90\u8DEF\u5F84\uFF08\u76F8\u5BF9 entry \u76EE\u5F55\uFF09", got: v.file });
|
|
91
|
+
}
|
|
92
|
+
if (v.css !== void 0) {
|
|
93
|
+
if (!Array.isArray(v.css)) {
|
|
94
|
+
issues.push({ field: `exposes["${key}"].css`, message: "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4", got: typeof v.css });
|
|
95
|
+
} else {
|
|
96
|
+
v.css.forEach((css, i) => {
|
|
97
|
+
if (!validAssetRef(css)) {
|
|
98
|
+
issues.push({ field: `exposes["${key}"].css[${i}]`, message: "\u5FC5\u987B\u662F\u8D44\u6E90\u8DEF\u5F84", got: css });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
issues.push({ field: "exposes", message: "\u5FC5\u987B\u662F\u6570\u7EC4\uFF08dev\uFF09\u6216\u5BF9\u8C61\uFF08prod\uFF09", got: typeof exposes });
|
|
106
|
+
}
|
|
107
|
+
if (issues.length > 0) return { issues };
|
|
108
|
+
return { manifest: input, issues };
|
|
109
|
+
}
|
|
110
|
+
function validateShared(shared) {
|
|
111
|
+
if (shared === void 0) return [];
|
|
112
|
+
const issues = [];
|
|
113
|
+
if (!Array.isArray(shared)) {
|
|
114
|
+
return [{ field: "shared", message: "\u5FC5\u987B\u662F\u6570\u7EC4", got: typeof shared }];
|
|
115
|
+
}
|
|
116
|
+
for (const [i, s] of shared.entries()) {
|
|
117
|
+
if (!isPlainObject(s)) {
|
|
118
|
+
issues.push({ field: `shared[${i}]`, message: "\u5FC5\u987B\u662F\u5BF9\u8C61", got: typeof s });
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (typeof s.name !== "string" || s.name.length === 0) {
|
|
122
|
+
issues.push({ field: `shared[${i}].name`, message: "\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32", got: s.name });
|
|
123
|
+
}
|
|
124
|
+
if (typeof s.version !== "string" || s.version.length === 0) {
|
|
125
|
+
issues.push({ field: `shared[${i}].version`, message: "\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32", got: s.version });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return issues;
|
|
129
|
+
}
|
|
130
|
+
function isDevManifest(m) {
|
|
131
|
+
return Array.isArray(m.exposes);
|
|
132
|
+
}
|
|
133
|
+
function normalizeExposes(manifest) {
|
|
134
|
+
const out = /* @__PURE__ */ new Map();
|
|
135
|
+
if (isDevManifest(manifest)) {
|
|
136
|
+
for (const e of manifest.exposes) {
|
|
137
|
+
if (!validExposeName(e.name)) continue;
|
|
138
|
+
out.set(e.name, { name: e.name, file: e.file, css: [], src: e.src });
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
for (const [key, v] of Object.entries(manifest.exposes)) {
|
|
142
|
+
if (!validExposeName(key)) continue;
|
|
143
|
+
out.set(key, { name: key, file: v.file, css: Array.isArray(v.css) ? v.css.filter((c) => typeof c === "string") : [] });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
var MANIFEST_SCHEMA_VERSION, isPlainObject;
|
|
149
|
+
var init_manifest = __esm({
|
|
150
|
+
"src/manifest.ts"() {
|
|
151
|
+
"use strict";
|
|
152
|
+
MANIFEST_SCHEMA_VERSION = 1;
|
|
153
|
+
isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
12
157
|
// src/config.ts
|
|
13
158
|
async function loadRepoConfig(configPath) {
|
|
14
|
-
const { pathToFileURL, fileURLToPath } = await import("node:url");
|
|
15
|
-
const { createRequire } = await import("node:module");
|
|
16
|
-
const
|
|
159
|
+
const { pathToFileURL: pathToFileURL2, fileURLToPath } = await import("node:url");
|
|
160
|
+
const { createRequire: createRequire2 } = await import("node:module");
|
|
161
|
+
const fs3 = await import("node:fs");
|
|
17
162
|
const os = await import("node:os");
|
|
18
|
-
const
|
|
19
|
-
const selfDir =
|
|
20
|
-
|
|
21
|
-
|
|
163
|
+
const path3 = await import("node:path");
|
|
164
|
+
const selfDir = await (async () => {
|
|
165
|
+
try {
|
|
166
|
+
return path3.dirname(fileURLToPath(import.meta.url));
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
return __dirname;
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
return "";
|
|
174
|
+
})();
|
|
175
|
+
const selfConfigJs = selfDir ? path3.join(selfDir, "config.js") : "";
|
|
176
|
+
const SELF_SPEC = selfConfigJs && fs3.existsSync(selfConfigJs) ? pathToFileURL2(selfConfigJs).href : "@fulgurjs-federation-config-unavailable";
|
|
22
177
|
const rewriteSelfImports = (source) => source.replace(/(['"])@fulgurjs\/federation\/config\1/g, (_m, q) => `${q}${SELF_SPEC}${q}`);
|
|
178
|
+
let cacheBust = 0;
|
|
23
179
|
const importConfigs = async (cacheKey) => {
|
|
24
|
-
return await import(`${cacheKey}?t=${Date.now()}`);
|
|
180
|
+
return await import(`${cacheKey}?t=${Date.now()}-${++cacheBust}`);
|
|
25
181
|
};
|
|
26
182
|
let mod;
|
|
27
183
|
if (configPath.endsWith(".json")) {
|
|
28
|
-
mod = { default: JSON.parse(
|
|
184
|
+
mod = { default: JSON.parse(fs3.readFileSync(configPath, "utf8")) };
|
|
29
185
|
} else {
|
|
30
|
-
const source = rewriteSelfImports(
|
|
31
|
-
const tmpBase =
|
|
186
|
+
const source = rewriteSelfImports(fs3.readFileSync(configPath, "utf8"));
|
|
187
|
+
const tmpBase = path3.join(os.tmpdir(), `fulgurjs-config-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
32
188
|
if (/\.(ts|mts|cts)$/.test(configPath)) {
|
|
33
189
|
try {
|
|
34
190
|
const tmpTs = `${tmpBase}.ts`;
|
|
35
|
-
|
|
36
|
-
mod = await importConfigs(
|
|
191
|
+
fs3.writeFileSync(tmpTs, source);
|
|
192
|
+
mod = await importConfigs(pathToFileURL2(tmpTs).href);
|
|
37
193
|
} catch (e) {
|
|
38
194
|
if (!/Unexpected|SyntaxError|ERR_UNSUPPORTED_NODE_MODULES|ERR_UNKNOWN_FILE_EXTENSION|ERR_IMPORT_ATTRIBUTE/.test(String(e)) && process.version.startsWith("v2")) {
|
|
39
195
|
throw e;
|
|
40
196
|
}
|
|
41
|
-
const requireFromConfig =
|
|
197
|
+
const requireFromConfig = createRequire2(configPath);
|
|
42
198
|
let esbuild;
|
|
43
199
|
try {
|
|
44
200
|
esbuild = requireFromConfig("esbuild");
|
|
45
201
|
} catch {
|
|
46
|
-
const requireFromSelf =
|
|
202
|
+
const requireFromSelf = createRequire2(import.meta.url);
|
|
47
203
|
esbuild = requireFromSelf("esbuild");
|
|
48
204
|
}
|
|
49
205
|
const out = esbuild.buildSync({ entryPoints: [configPath], bundle: false, write: false, format: "esm", target: "node18" });
|
|
50
206
|
const jsFile = `${tmpBase}.mjs`;
|
|
51
|
-
|
|
52
|
-
mod = await importConfigs(
|
|
207
|
+
fs3.writeFileSync(jsFile, rewriteSelfImports(out.outputFiles[0].text));
|
|
208
|
+
mod = await importConfigs(pathToFileURL2(jsFile).href);
|
|
53
209
|
}
|
|
54
210
|
} else {
|
|
55
211
|
const jsFile = `${tmpBase}.mjs`;
|
|
56
|
-
|
|
57
|
-
mod = await importConfigs(
|
|
212
|
+
fs3.writeFileSync(jsFile, source);
|
|
213
|
+
mod = await importConfigs(pathToFileURL2(jsFile).href);
|
|
58
214
|
}
|
|
59
215
|
}
|
|
60
216
|
const cfg = mod.default;
|
|
@@ -73,18 +229,278 @@ async function loadRepoConfig(configPath) {
|
|
|
73
229
|
throw new Error(
|
|
74
230
|
`[fulgurjs:init] \u5E94\u7528 "${app.path}" \u65E2\u65E0 host \u4E5F\u65E0 remote \u89D2\u8272
|
|
75
231
|
\u6839\u56E0\uFF1A\u5E94\u7528\u5FC5\u987B\u81F3\u5C11\u58F0\u660E\u4E00\u4E2A\u89D2\u8272
|
|
76
|
-
\u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u8865 host: {
|
|
232
|
+
\u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u8865 host: { remotePrefixes, remotes }\uFF08pages \u53EF\u9009\uFF09\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
|
|
77
233
|
);
|
|
78
234
|
}
|
|
79
235
|
}
|
|
80
236
|
return cfg;
|
|
81
237
|
}
|
|
238
|
+
function federationOptionsForApp(config, appPathOrName) {
|
|
239
|
+
const app = config.apps.find(
|
|
240
|
+
(a) => a.path === appPathOrName || a.name === appPathOrName
|
|
241
|
+
);
|
|
242
|
+
if (!app) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`[fulgurjs] \u5E94\u7528 "${appPathOrName}" \u4E0D\u5728\u4ED3\u5E93\u914D\u7F6E\u4E2D
|
|
245
|
+
\u53EF\u7528\u5E94\u7528: ${config.apps.map((a) => `${a.path}(${a.name})`).join("\u3001")}
|
|
246
|
+
\u4FEE\u6CD5: \u4FEE\u6B63\u5E94\u7528\u76EE\u5F55\u540D\u6216\u5BB9\u5668\u540D\uFF08fulgurjs.config.ts apps \u6570\u7EC4\u5185\u4E8C\u9009\u4E00\u5339\u914D\uFF09`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (!app.host && !app.remote) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`[fulgurjs] \u5E94\u7528 "${app.path}" \u65E2\u65E0 host \u4E5F\u65E0 remote \u89D2\u8272
|
|
252
|
+
\u4FEE\u6CD5: \u5BBF\u4E3B\u8865 host: { remotePrefixes, remotes }\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
const remotes = {};
|
|
256
|
+
const remoteSource = /* @__PURE__ */ new Map();
|
|
257
|
+
const mergeRemotes = (from, role) => {
|
|
258
|
+
for (const [key, addr] of Object.entries(from ?? {})) {
|
|
259
|
+
const prev = remotes[key];
|
|
260
|
+
if (prev && (prev.dev !== addr.dev || prev.prod !== addr.prod)) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`[fulgurjs] \u5E94\u7528 "${app.path}" \u7684\u8FDC\u7A0B "${key}" \u5728 ${role} \u4E0E ${remoteSource.get(key)} \u4E24\u5904\u5730\u5740\u4E0D\u4E00\u81F4
|
|
263
|
+
${remoteSource.get(key)}: dev=${prev.dev} prod=${prev.prod}
|
|
264
|
+
${role}: dev=${addr.dev} prod=${addr.prod}
|
|
265
|
+
\u4FEE\u6CD5: \u4FEE\u6B63 fulgurjs.config.ts \u4F7F\u4E24\u5904\u4E00\u81F4\uFF08\u540C\u4E00\u8FDC\u7A0B\u5728\u4E00\u4E2A\u5E94\u7528\u5185\u53EA\u80FD\u6709\u4E00\u4E2A\u5730\u5740\uFF09`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
remotes[key] = addr;
|
|
269
|
+
remoteSource.set(key, role);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
mergeRemotes(app.host?.remotes, "host.remotes");
|
|
273
|
+
mergeRemotes(app.remote?.remotes, "remote.remotes");
|
|
274
|
+
const hasExposes = !!app.remote && Object.keys(app.remote.exposes).length > 0;
|
|
275
|
+
const hasRemotes = Object.keys(remotes).length > 0;
|
|
276
|
+
return {
|
|
277
|
+
name: app.name,
|
|
278
|
+
...hasRemotes ? { remotes } : {},
|
|
279
|
+
...hasExposes ? { exposes: app.remote.exposes } : {},
|
|
280
|
+
...app.remote?.setup ? { setup: app.remote.setup } : {},
|
|
281
|
+
...app.shared ? { shared: app.shared } : {},
|
|
282
|
+
devSharedSelf: app.devSharedSelf ?? (hasExposes || !!app.remote?.setup)
|
|
283
|
+
};
|
|
284
|
+
}
|
|
82
285
|
var init_config = __esm({
|
|
83
286
|
"src/config.ts"() {
|
|
84
287
|
"use strict";
|
|
85
288
|
}
|
|
86
289
|
});
|
|
87
290
|
|
|
291
|
+
// src/app-config.ts
|
|
292
|
+
import { createRequire } from "node:module";
|
|
293
|
+
import { pathToFileURL } from "node:url";
|
|
294
|
+
function appConfigError(what, cause, fix) {
|
|
295
|
+
throw new Error(`[fulgurjs:config] ${what}
|
|
296
|
+
\u6839\u56E0\uFF1A${cause}
|
|
297
|
+
\u4FEE\u6CD5\uFF1A${fix}`);
|
|
298
|
+
}
|
|
299
|
+
function isAggregateShape(value) {
|
|
300
|
+
return !!value && typeof value === "object" && Array.isArray(value.apps);
|
|
301
|
+
}
|
|
302
|
+
function fieldError(field, got, expect, example) {
|
|
303
|
+
appConfigError(
|
|
304
|
+
`fulgurjs.config.ts \u7684 ${field} \u5F62\u72B6\u4E0D\u5408\u6CD5\uFF08\u5B9E\u9645 ${JSON.stringify(got)}\uFF09`,
|
|
305
|
+
`CLI \u6309\u5355\u9879\u76EE\u5951\u7EA6\u8BFB\u53D6\uFF1A\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\u5BF9\u8C61`,
|
|
306
|
+
`\u671F\u671B ${expect}\u3002\u793A\u4F8B\uFF1A
|
|
307
|
+
${example}`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
function validateAppOptions(options) {
|
|
311
|
+
if (typeof options.name !== "string" || options.name.trim() === "") {
|
|
312
|
+
fieldError(
|
|
313
|
+
"\u9ED8\u8BA4\u5BFC\u51FA name",
|
|
314
|
+
options.name,
|
|
315
|
+
"\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u8054\u90A6\u5BB9\u5668\u540D\uFF09",
|
|
316
|
+
`export default { name: 'my-app', exposes: { './pages/home': './src/views/Home.vue' } } satisfies FederationOptions`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
for (const legacyField of ["root", "apps", "deploy", "host", "remote"]) {
|
|
320
|
+
if (options[legacyField] !== void 0) {
|
|
321
|
+
appConfigError(
|
|
322
|
+
`\u9ED8\u8BA4\u5BFC\u51FA\u5305\u542B\u65E7\u805A\u5408/\u89D2\u8272\u58F3\u5B57\u6BB5 "${legacyField}"`,
|
|
323
|
+
"\u5355\u9879\u76EE\u5951\u7EA6\u7684\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\uFF08name/exposes/setup/remotes/shared/\u2026\uFF09\uFF0C\u4E0D\u5305 host/remote \u89D2\u8272\u58F3\uFF0C\u4E5F\u6CA1\u6709 root/apps[]",
|
|
324
|
+
legacyField === "host" || legacyField === "remote" ? "\u628A host/remote \u5185\u7684\u5B57\u6BB5\u62CD\u5E73\u5230\u9876\u5C42\uFF1Ahost.remotes \u2192 remotes\uFF1Bremote.exposes \u2192 exposes\uFF1Bremote.setup \u2192 setup\uFF1Bhost.pages/remotePrefixes/deriveSpec \u2192 \u5177\u540D\u5BFC\u51FA hostPages" : `${legacyField} \u5C5E\u4E8E\u65E7\u805A\u5408\u914D\u7F6E\uFF08root + apps[]\uFF0C\u517C\u5BB9\u671F\u4FDD\u7559\uFF09\u3002\u5355\u9879\u76EE\u5F62\u6001\u8BF7\u5220\u9664\u8BE5\u5B57\u6BB5\uFF1B\u805A\u5408\u7528\u6CD5\u89C1 README\u300C\u5386\u53F2\u517C\u5BB9\u300D\u7AE0\u8282`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function validateHostPages(value) {
|
|
330
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
331
|
+
fieldError("\u5177\u540D\u5BFC\u51FA hostPages", value, "\u5BF9\u8C61 { pages, remotePrefixes, deriveSpec? }", `export const hostPages = { pages, remotePrefixes, deriveSpec }`);
|
|
332
|
+
}
|
|
333
|
+
const hp = value;
|
|
334
|
+
if (!Array.isArray(hp.pages)) {
|
|
335
|
+
fieldError("hostPages.pages", hp.pages, "PageEntry[]\uFF08\u8DEF\u7531\u8868\u6570\u7EC4\uFF09", `export const hostPages = { pages: [{ route: '/remote-a/home', name: 'Home', title: '\u9996\u9875' }], remotePrefixes: { '/remote-a/': 'remote-a' } }`);
|
|
336
|
+
}
|
|
337
|
+
if (!hp.remotePrefixes || typeof hp.remotePrefixes !== "object") {
|
|
338
|
+
fieldError("hostPages.remotePrefixes", hp.remotePrefixes, "Record<\u8DEF\u7531\u524D\u7F00, \u8FDC\u7A0B\u5BB9\u5668\u540D>", `remotePrefixes: { '/remote-a/': 'remote-a' }`);
|
|
339
|
+
}
|
|
340
|
+
if (hp.deriveSpec !== void 0 && typeof hp.deriveSpec !== "function") {
|
|
341
|
+
fieldError("hostPages.deriveSpec", hp.deriveSpec, "\u51FD\u6570 (route) => spec \u6216\u7701\u7565", `deriveSpec: (route) => 'pages/' + route.split('/').filter((s) => !s.startsWith(':')).join('/')`);
|
|
342
|
+
}
|
|
343
|
+
return hp;
|
|
344
|
+
}
|
|
345
|
+
function validatePathsInsideApp(options, appRoot, fs3, path3) {
|
|
346
|
+
const checkModule = (field, value) => {
|
|
347
|
+
if (typeof value !== "string" || value.trim() === "") return;
|
|
348
|
+
if (/^(https?:)?\/\//.test(value) || path3.isAbsolute(value)) {
|
|
349
|
+
appConfigError(
|
|
350
|
+
`${field} "${value}" \u4E0D\u662F\u672C\u9879\u76EE\u5185\u7684\u6A21\u5757\u8DEF\u5F84`,
|
|
351
|
+
"exposes/setup \u7684\u503C\u662F\u76F8\u5BF9\u672C\u5E94\u7528\u6839\u76EE\u5F55\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84\uFF08URL/\u7EDD\u5BF9\u8DEF\u5F84\u65E0\u6CD5\u5728\u72EC\u7ACB\u4ED3\u5E93\u95F4\u79FB\u690D\uFF09",
|
|
352
|
+
`\u6539\u4E3A\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u5982 './src/views/Home.vue'\uFF08\u914D\u7F6E\u6587\u4EF6\u76EE\u5F55 = \u5E94\u7528\u6839 ${appRoot}\uFF09`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
const abs = path3.resolve(appRoot, value);
|
|
356
|
+
if (!fs3.existsSync(abs)) {
|
|
357
|
+
appConfigError(
|
|
358
|
+
`${field} \u6307\u5411\u7684\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${value}`,
|
|
359
|
+
`\u6309\u5E94\u7528\u6839 ${appRoot} \u89E3\u6790\u540E\u672A\u627E\u5230\u8BE5\u6587\u4EF6`,
|
|
360
|
+
`\u6838\u5BF9\u76F8\u5BF9\u8DEF\u5F84\uFF08\u4EE5\u914D\u7F6E\u6587\u4EF6\u6240\u5728\u76EE\u5F55\u4E3A\u57FA\u51C6\uFF09\uFF1B\u6587\u4EF6\u88AB\u79FB\u52A8/\u5220\u9664\u65F6\u540C\u6B65\u66F4\u65B0 ${field}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const exposes = options.exposes;
|
|
365
|
+
if (exposes !== void 0 && (typeof exposes !== "object" || exposes === null || Array.isArray(exposes))) {
|
|
366
|
+
fieldError("exposes", exposes, 'Record<"./\u952E", "./\u6E90\u6587\u4EF6">', `exposes: { './pages/home': './src/views/Home.vue' }`);
|
|
367
|
+
}
|
|
368
|
+
for (const target of Object.values(exposes ?? {})) {
|
|
369
|
+
checkModule("exposes \u6761\u76EE", typeof target === "string" ? target : target?.import);
|
|
370
|
+
}
|
|
371
|
+
checkModule("setup", options.setup);
|
|
372
|
+
}
|
|
373
|
+
function resolveSelfEntry(configPath, spec) {
|
|
374
|
+
const tries = [() => createRequire(configPath).resolve(spec), () => createRequire(import.meta.url).resolve(spec)];
|
|
375
|
+
for (const t of tries) {
|
|
376
|
+
try {
|
|
377
|
+
return t();
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
function resolveSpec(spec, from) {
|
|
384
|
+
try {
|
|
385
|
+
return createRequire(from).resolve(spec);
|
|
386
|
+
} catch {
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function resolveEsbuild(configPath) {
|
|
391
|
+
const esbuildOf = (base) => {
|
|
392
|
+
const direct = resolveSpec("esbuild", base);
|
|
393
|
+
if (direct) return createRequire(direct)("esbuild");
|
|
394
|
+
const viteEntry = resolveSpec("vite", base);
|
|
395
|
+
if (viteEntry) {
|
|
396
|
+
const viaVite = resolveSpec("esbuild", viteEntry);
|
|
397
|
+
if (viaVite) return createRequire(viaVite)("esbuild");
|
|
398
|
+
}
|
|
399
|
+
return void 0;
|
|
400
|
+
};
|
|
401
|
+
for (const base of [configPath, import.meta.url]) {
|
|
402
|
+
const got = esbuildOf(base);
|
|
403
|
+
if (got) return got;
|
|
404
|
+
}
|
|
405
|
+
appConfigError(
|
|
406
|
+
"\u65E0\u6CD5\u52A0\u8F7D esbuild\uFF08\u5355\u9879\u76EE\u914D\u7F6E\u52A0\u8F7D\u9700\u8981\u5B83\u8F6C\u8BD1 TS \u4E0E\u76F8\u5BF9\u5BFC\u5165\uFF09",
|
|
407
|
+
"\u914D\u7F6E\u5DE5\u7A0B\uFF08\u542B\u5176 vite \u4F9D\u8D56\u6811\uFF09\u4E0E CLI \u4F9D\u8D56\u6811\u4E2D\u90FD\u89E3\u6790\u4E0D\u5230 esbuild",
|
|
408
|
+
"\u9879\u76EE\u5185\u6709 vite \u4F9D\u8D56\u5373\u53EF\uFF08\u7ECF\u5176\u4F9D\u8D56\u6811\u89E3\u6790\uFF09\uFF1B\u786E\u5B9E\u7F3A\u5931\u65F6 npm i -D esbuild \u540E\u91CD\u8BD5"
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
async function loadAppConfig(configPath) {
|
|
412
|
+
const fs3 = await import("node:fs");
|
|
413
|
+
const os = await import("node:os");
|
|
414
|
+
const path3 = await import("node:path");
|
|
415
|
+
const abs = path3.resolve(configPath);
|
|
416
|
+
if (!fs3.existsSync(abs)) {
|
|
417
|
+
appConfigError(
|
|
418
|
+
`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${abs}`,
|
|
419
|
+
"--config \u6307\u5411\u7684\u8DEF\u5F84\u5728\u672C\u673A\u4E0D\u5B58\u5728\uFF08\u76F8\u5BF9\u8DEF\u5F84\u4EE5 CLI \u8FDB\u7A0B cwd \u89E3\u6790\uFF09",
|
|
420
|
+
"\u6838\u5BF9\u8DEF\u5F84\uFF1B\u4ECE\u5E94\u7528\u6839\u76EE\u5F55\u8FD0\u884C\u65F6\u76F4\u63A5\u7528 fulgurjs.config.ts"
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (abs.endsWith(".json")) {
|
|
424
|
+
const parsed = JSON.parse(fs3.readFileSync(abs, "utf8"));
|
|
425
|
+
if (isAggregateShape(parsed)) {
|
|
426
|
+
const repo = await loadRepoConfig(abs);
|
|
427
|
+
return { kind: "repo", configPath: abs, appRoot: path3.dirname(abs), repo };
|
|
428
|
+
}
|
|
429
|
+
return finishAppShape(parsed, {}, abs, path3.dirname(abs), fs3, path3);
|
|
430
|
+
}
|
|
431
|
+
const esbuild = resolveEsbuild(abs);
|
|
432
|
+
const selfRedirect = {
|
|
433
|
+
name: "fulgurjs-self-redirect",
|
|
434
|
+
setup(build) {
|
|
435
|
+
build.onResolve({ filter: /^@fulgurjs\/federation(\/|$)/ }, (args) => {
|
|
436
|
+
const resolved = resolveSelfEntry(abs, args.path);
|
|
437
|
+
if (!resolved) {
|
|
438
|
+
return { errors: [{ text: `cannot resolve "${args.path}" from the config project or the CLI itself` }] };
|
|
439
|
+
}
|
|
440
|
+
return { path: resolved };
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const out = await esbuild.build({
|
|
445
|
+
entryPoints: [abs],
|
|
446
|
+
bundle: true,
|
|
447
|
+
write: false,
|
|
448
|
+
format: "esm",
|
|
449
|
+
target: "node18",
|
|
450
|
+
platform: "node",
|
|
451
|
+
plugins: [selfRedirect],
|
|
452
|
+
logLevel: "silent"
|
|
453
|
+
});
|
|
454
|
+
const tmpDir = fs3.mkdtempSync(path3.join(os.tmpdir(), "fulgurjs-appcfg-"));
|
|
455
|
+
try {
|
|
456
|
+
const bundleFile = path3.join(tmpDir, "fulgurjs.config.bundle.mjs");
|
|
457
|
+
fs3.writeFileSync(bundleFile, out.outputFiles[0].text);
|
|
458
|
+
const mod = await import(`${pathToFileURL(bundleFile).href}?t=${Date.now()}`);
|
|
459
|
+
if (isAggregateShape(mod.default)) {
|
|
460
|
+
const repo = await loadRepoConfig(abs);
|
|
461
|
+
return { kind: "repo", configPath: abs, appRoot: path3.dirname(abs), repo };
|
|
462
|
+
}
|
|
463
|
+
return finishAppShape(mod.default, { hostPages: mod.hostPages }, abs, path3.dirname(abs), fs3, path3);
|
|
464
|
+
} finally {
|
|
465
|
+
fs3.rmSync(tmpDir, { recursive: true, force: true });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function finishAppShape(def, named, configPath, appRoot, fs3, path3) {
|
|
469
|
+
if (def === void 0 || def === null) {
|
|
470
|
+
appConfigError(
|
|
471
|
+
"fulgurjs.config.ts \u6CA1\u6709\u53EF\u7528\u7684\u9ED8\u8BA4\u5BFC\u51FA",
|
|
472
|
+
"\u5355\u9879\u76EE\u5951\u7EA6\uFF1A\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\u5BF9\u8C61\uFF08satisfies FederationOptions\uFF09",
|
|
473
|
+
`export default { name: 'my-app', ... } satisfies FederationOptions`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
if (typeof def !== "object" || Array.isArray(def)) {
|
|
477
|
+
fieldError("\u9ED8\u8BA4\u5BFC\u51FA", def, "\u5BF9\u8C61\uFF08federation() \u9009\u9879\uFF09", `export default { name: 'my-app', exposes: { /* \u2026 */ } } satisfies FederationOptions`);
|
|
478
|
+
}
|
|
479
|
+
const options = def;
|
|
480
|
+
validateAppOptions(options);
|
|
481
|
+
validatePathsInsideApp(options, appRoot, fs3, path3);
|
|
482
|
+
const hostPages = named.hostPages !== void 0 ? validateHostPages(named.hostPages) : void 0;
|
|
483
|
+
return {
|
|
484
|
+
kind: "app",
|
|
485
|
+
configPath,
|
|
486
|
+
appRoot,
|
|
487
|
+
options,
|
|
488
|
+
hostPages
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function roleOfOptions(options) {
|
|
492
|
+
const hasRemotes = Object.keys(options.remotes ?? {}).length > 0;
|
|
493
|
+
const hasProvides = Object.keys(options.exposes ?? {}).length > 0 || !!options.setup;
|
|
494
|
+
if (hasRemotes && hasProvides) return "dual";
|
|
495
|
+
return hasRemotes ? "host" : "remote";
|
|
496
|
+
}
|
|
497
|
+
var init_app_config = __esm({
|
|
498
|
+
"src/app-config.ts"() {
|
|
499
|
+
"use strict";
|
|
500
|
+
init_config();
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
88
504
|
// src/init.ts
|
|
89
505
|
var init_exports = {};
|
|
90
506
|
__export(init_exports, {
|
|
@@ -118,6 +534,8 @@ ${hostRemotes || remoteRemotes}
|
|
|
118
534
|
const exposesBlock = app.remote?.exposes ? ` exposes: {
|
|
119
535
|
${exposesLiteral(app.remote.exposes)}
|
|
120
536
|
},
|
|
537
|
+
` : "";
|
|
538
|
+
const setupBlock = app.remote?.setup ? ` setup: '${app.remote.setup}',
|
|
121
539
|
` : "";
|
|
122
540
|
const sharedBlock = app.shared ? ` shared: {
|
|
123
541
|
${Object.entries(app.shared).map(([k, v]) => {
|
|
@@ -125,11 +543,22 @@ ${Object.entries(app.shared).map(([k, v]) => {
|
|
|
125
543
|
return ` '${k}': { ${opts.join(", ")} },`;
|
|
126
544
|
}).join("\n")}
|
|
127
545
|
},
|
|
128
|
-
` :
|
|
129
|
-
`;
|
|
546
|
+
` : "";
|
|
130
547
|
return `federation({
|
|
131
548
|
name: '${app.name}',
|
|
132
|
-
${remotesBlock}${exposesBlock}${sharedBlock}})`;
|
|
549
|
+
${remotesBlock}${exposesBlock}${setupBlock}${sharedBlock}})`;
|
|
550
|
+
}
|
|
551
|
+
function viteSnippetForAppMode(appRoot) {
|
|
552
|
+
return `// ${appRoot}/vite.config.ts\uFF08\u8054\u90A6\u76F8\u5173\u884C\uFF1B\u5176\u4F59 Vite \u914D\u7F6E\u539F\u6837\u4FDD\u7559\uFF09
|
|
553
|
+
import federation from '@fulgurjs/federation'
|
|
554
|
+
import fulgurjsConfig from './fulgurjs.config'
|
|
555
|
+
|
|
556
|
+
export default defineConfig({
|
|
557
|
+
plugins: [
|
|
558
|
+
// ...\u539F\u6709\u63D2\u4EF6,
|
|
559
|
+
federation(fulgurjsConfig),
|
|
560
|
+
],
|
|
561
|
+
})`;
|
|
133
562
|
}
|
|
134
563
|
function nginxSnippetFor(cfg) {
|
|
135
564
|
const listen = cfg.deploy?.listen ?? 8080;
|
|
@@ -156,7 +585,7 @@ function nginxSnippetFor(cfg) {
|
|
|
156
585
|
}`;
|
|
157
586
|
}
|
|
158
587
|
).join("\n\n");
|
|
159
|
-
return `# \u8054\u90A6\u7AD9\u70B9\
|
|
588
|
+
return `# \u8054\u90A6\u7AD9\u70B9\u6837\u677F\uFF08\u6253\u5370\u6837\u677F\uFF0C\u4E0D\u5199\u6587\u4EF6\u3002\u8981\u70B9\uFF1AremoteEntry/manifest/index.html \u5FC5\u987B no-cache\u2014\u2014
|
|
160
589
|
# \u6587\u4EF6\u540D\u56FA\u5B9A\u800C\u5185\u5BB9\u6BCF\u6B21\u6784\u5EFA\u53D8\u5316\uFF0Cimmutable \u957F\u7F13\u5B58\u4F1A\u8BA9\u6D4F\u89C8\u5668\u6301\u65E7\u5165\u53E3\u3001\u91CD\u90E8\u7F72\u540E\u5168 404\uFF1B
|
|
161
590
|
# \u5E26 hash \u7684 assets \u624D\u914D\u957F\u7F13\u5B58\uFF1B\u8DE8\u6E90\u90E8\u7F72\u65F6\u4FDD\u7559 Access-Control-Allow-Origin\uFF09
|
|
162
591
|
server {
|
|
@@ -174,235 +603,695 @@ function appSummary(app) {
|
|
|
174
603
|
for (const [k, v] of Object.entries(app.host.remotes)) {
|
|
175
604
|
lines.push(` \u6D88\u8D39\u8FDC\u7A0B ${k} \u2192 dev ${v.dev} / prod ${v.prod}`);
|
|
176
605
|
}
|
|
177
|
-
lines.push(` \u9875\u9762\u8DEF\u7531\u8868\uFF1A${app.host.pages
|
|
606
|
+
lines.push(` \u9875\u9762\u8DEF\u7531\u8868\uFF1A${app.host.pages?.length ?? 0} \u6761${app.host.pages ? "" : "\uFF08\u672A\u5728\u4ED3\u5E93\u914D\u7F6E\u4E2D\u63D0\u4F9B\u2014\u2014\u4EE5\u5E94\u7528\u4EE3\u7801\u9875\u9762\u8868\u4E3A\u51C6\uFF09"}`);
|
|
178
607
|
}
|
|
179
608
|
if (app.remote?.exposes) {
|
|
180
609
|
lines.push(` exposes\uFF1A${Object.keys(app.remote.exposes).join("\u3001")}`);
|
|
181
610
|
}
|
|
611
|
+
if (app.remote?.setup) {
|
|
612
|
+
lines.push(` setup\uFF08\u53EF\u9009\u8FDC\u7A0B\u521D\u59CB\u5316\uFF09\uFF1A${app.remote.setup}`);
|
|
613
|
+
}
|
|
182
614
|
if (app.remote?.remotes) {
|
|
183
615
|
for (const [k, v] of Object.entries(app.remote.remotes)) {
|
|
184
|
-
lines.push(` \u53CD\u5411\u6D88\u8D39 ${k} \u2192 dev ${v.dev} / prod ${v.prod}\uFF08\u53CC\u5411\u8054\u90A6\
|
|
616
|
+
lines.push(` \u53CD\u5411\u6D88\u8D39 ${k} \u2192 dev ${v.dev} / prod ${v.prod}\uFF08\u53CC\u5411\u8054\u90A6\uFF09`);
|
|
185
617
|
}
|
|
186
618
|
}
|
|
187
619
|
return lines.join("\n");
|
|
188
620
|
}
|
|
621
|
+
function appModeSummary(loaded) {
|
|
622
|
+
const options = loaded.options;
|
|
623
|
+
const roleText = { host: "\u5BBF\u4E3B", remote: "\u8FDC\u7A0B", dual: "\u5BBF\u4E3B+\u8FDC\u7A0B\uFF08\u53CC\u89D2\u8272\uFF09" }[Object.keys(options.remotes ?? {}).length > 0 && (Object.keys(options.exposes ?? {}).length > 0 || !!options.setup) ? "dual" : Object.keys(options.remotes ?? {}).length > 0 ? "host" : "remote"];
|
|
624
|
+
const lines = [];
|
|
625
|
+
lines.push(` ${options.name}\uFF08${roleText}\uFF0C\u5355\u9879\u76EE\u914D\u7F6E\uFF0C\u76EE\u5F55 ${loaded.appRoot}\uFF1Bbase/dev \u7AEF\u53E3\u7531 vite.config.ts \u7BA1\u7406\uFF09`);
|
|
626
|
+
for (const [k, v] of Object.entries(options.remotes ?? {})) {
|
|
627
|
+
const cfg = typeof v === "string" ? { dev: v, prod: v } : v;
|
|
628
|
+
lines.push(` \u6D88\u8D39\u8FDC\u7A0B ${k} \u2192 dev ${cfg.dev ?? ""} / prod ${cfg.prod ?? ""}`);
|
|
629
|
+
}
|
|
630
|
+
if (options.exposes) {
|
|
631
|
+
lines.push(` exposes\uFF08${Object.keys(options.exposes).length}\uFF09\uFF1A${Object.keys(options.exposes).join("\u3001")}`);
|
|
632
|
+
}
|
|
633
|
+
if (options.setup) {
|
|
634
|
+
lines.push(` setup\uFF08\u53EF\u9009\u8FDC\u7A0B\u521D\u59CB\u5316\uFF09\uFF1A${options.setup}`);
|
|
635
|
+
}
|
|
636
|
+
if (loaded.hostPages) {
|
|
637
|
+
lines.push(` \u9875\u9762\u8DEF\u7531\u8868\uFF08hostPages \u5177\u540D\u5BFC\u51FA\uFF09\uFF1A${loaded.hostPages.pages.length} \u6761`);
|
|
638
|
+
} else {
|
|
639
|
+
lines.push(" \u9875\u9762\u8DEF\u7531\u8868\uFF1A\uFF08\u65E0 hostPages \u5BFC\u51FA\u2014\u2014\u7EAF\u8FDC\u7A0B\u5E94\u7528\u65E0\u9700\uFF1B\u5BBF\u4E3B\u5EFA\u8BAE\u63D0\u4F9B\uFF09");
|
|
640
|
+
}
|
|
641
|
+
return lines;
|
|
642
|
+
}
|
|
189
643
|
async function inspectConfig(configPath) {
|
|
190
|
-
const
|
|
644
|
+
const loaded = await loadAppConfig(configPath);
|
|
191
645
|
const out = [];
|
|
192
|
-
|
|
646
|
+
if (loaded.kind === "app") {
|
|
647
|
+
out.push(`${MARK} \u914D\u7F6E\u6821\u9A8C\u901A\u8FC7\uFF1A\u5355\u9879\u76EE\u5F62\u6001\uFF08\u9ED8\u8BA4\u5BFC\u51FA = federation() \u9009\u9879\uFF09`);
|
|
648
|
+
out.push("\u5E94\u7528\u6458\u8981\uFF1A");
|
|
649
|
+
out.push(...appModeSummary(loaded));
|
|
650
|
+
out.push("\n\u2500\u2500 vite.config.ts plugins \u63A5\u5165\u5757\uFF08\u8FD9\u5C31\u662F\u5168\u90E8\u8054\u90A6\u63A5\u5165\u4EE3\u7801\uFF09 \u2500\u2500");
|
|
651
|
+
out.push(viteSnippetForAppMode(loaded.appRoot));
|
|
652
|
+
out.push("\n\u2500\u2500 \u63A5\u5165\u6838\u5BF9\u6E05\u5355\uFF08\u8054\u90A6\u901A\u7528\u9879\uFF0C\u4E0E\u5177\u4F53\u9879\u76EE\u65E0\u5173\uFF09 \u2500\u2500");
|
|
653
|
+
out.push(...COMMON_CHECKLIST);
|
|
654
|
+
return out.join("\n");
|
|
655
|
+
}
|
|
656
|
+
const cfg = loaded.repo ?? await loadRepoConfig(loaded.configPath);
|
|
657
|
+
out.push(`${MARK} \u914D\u7F6E\u6821\u9A8C\u901A\u8FC7\uFF1Aroot=${cfg.root}\uFF0Capps=${cfg.apps.length}\u3010\u65E7\u805A\u5408\u914D\u7F6E\u5F62\u6001\uFF08root+apps[]\uFF09\uFF0C\u517C\u5BB9\u671F\u4FDD\u7559\uFF1B\u65B0\u9879\u76EE\u63A8\u8350\u6BCF\u9879\u76EE\u4E00\u4EFD\u5355\u9879\u76EE\u914D\u7F6E\uFF0C\u89C1 README\u3011`);
|
|
193
658
|
out.push("\u5E94\u7528\u6458\u8981\uFF1A");
|
|
194
659
|
for (const app of cfg.apps) out.push(appSummary(app));
|
|
195
|
-
out.push("\n\u2500\u2500 \u5404\u5E94\u7528 vite.config.ts plugins \u7C98\u8D34\u5757 \u2500\u2500");
|
|
660
|
+
out.push("\n\u2500\u2500 \u5404\u5E94\u7528 vite.config.ts plugins \u7C98\u8D34\u5757\uFF08\u65E7\u805A\u5408\u5F62\u6001\u9700\u7ECF loadRepoConfig/federationOptionsForApp \u8F6C\u6362\uFF09 \u2500\u2500");
|
|
196
661
|
for (const app of cfg.apps) {
|
|
197
662
|
out.push(`
|
|
198
663
|
# ${app.path}\uFF08${app.name}\uFF09`);
|
|
199
664
|
out.push(viteSnippetFor(app));
|
|
200
665
|
}
|
|
201
|
-
out.push("\n\u2500\u2500 NGINX \u7AD9\u70B9\
|
|
666
|
+
out.push("\n\u2500\u2500 NGINX \u7AD9\u70B9\u6837\u677F\uFF08\u6253\u5370\u6837\u677F\uFF0C\u4E0D\u5199\u6587\u4EF6\uFF09 \u2500\u2500");
|
|
202
667
|
out.push(nginxSnippetFor(cfg));
|
|
203
668
|
out.push("\n\u2500\u2500 \u63A5\u5165\u6838\u5BF9\u6E05\u5355\uFF08\u8054\u90A6\u901A\u7528\u9879\uFF0C\u4E0E\u5177\u4F53\u9879\u76EE\u65E0\u5173\uFF09 \u2500\u2500");
|
|
204
|
-
out.push(
|
|
205
|
-
out.push("2. expose \u4E00\u5F8B\u6307\u5411\u72EC\u7ACB\u9875\uFF08\u9875\u9762\u4ECE\u8DEF\u7531\u53D6\u53C2\uFF09\uFF1B\u7EC4\u4EF6\u9700\u8981\u5FC5\u586B props \u65F6\u7ED9\u9ED8\u8BA4\u503C\uFF08BLD-003\uFF09");
|
|
206
|
-
out.push("3. shared \u91CC vue / vue-router / pinia \u5EFA\u8BAE singleton: true\u2014\u2014\u8DE8\u5E94\u7528\u5FC5\u987B\u540C\u5B9E\u4F8B\uFF08\u5168\u5C40\u54CD\u5E94\u6027\u3001getActivePinia\u3001\u8DEF\u7531\u6CE8\u5165\uFF09");
|
|
207
|
-
out.push("4. \u8FDC\u7A0B\u7684\u5168\u5C40\u526F\u4F5C\u7528\uFF08\u5168\u5C40\u7EC4\u4EF6/\u6307\u4EE4/\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF09\u5C01\u88C5\u4E3A\u542F\u52A8\u5668\u6A21\u5757\u5E76 expose\uFF0C\u5BBF\u4E3B\u5728 loadRemote \u9875\u9762\u524D\u8C03\u7528\uFF1B");
|
|
208
|
-
out.push(" \u8DE8\u5E94\u7528\u4F20\u503C\uFF08locale/store/\u4E8B\u4EF6\u7B49\uFF09\u7EDF\u4E00\u8D70 @fulgurjs/federation/runtime \u7684 context \u51FD\u6570\uFF1A\u5BBF\u4E3B provideAppContext \u4E00\u6B21\u5199\u5165\uFF0C\u8FDC\u7A0B boot \u7528 getAppContext / requireAppContext \u6D88\u8D39");
|
|
209
|
-
out.push("5. \u5E94\u7528\u4EE3\u7801\u552F\u4E00 API \u5165\u53E3\uFF1Aimport { loadRemote, provideAppContext, getAppContext, definePages, remoteSchema, remoteComponent } from '@fulgurjs/federation/runtime'\u2014\u20144.0.0 \u8D77 virtual:fulgurjs-api \u5DF2\u5220\u9664\uFF0C\u7C7B\u578B\u7531\u7269\u7406\u5B50\u8DEF\u5F84\u76F4\u63A5\u89E3\u6790");
|
|
210
|
-
out.push('6. dev \u51B7\u542F\u52A8\u9996\u8F6E 30~60s \u6709\u9884\u6784\u5EFA\u7A97\u53E3\uFF08\u77AC\u65F6 504/"ce"\uFF0CDEV-010\uFF09\uFF1A\u5148\u771F\u5B9E\u6253\u5F00\u9875\u9762\u9884\u70ED\u518D\u505A\u65AD\u8A00');
|
|
211
|
-
out.push("7. \u90E8\u7F72\u540E\u4F53\u68C0\uFF1Afulgurjs doctor --base <URL> --apps <\u5E94\u7528...>\uFF08\u7F13\u5B58\u5934/\u8D44\u6E90\u5F62\u6001/CORS/chunk \u53EF\u8FBE/\u7248\u672C skew\uFF09");
|
|
212
|
-
out.push("8. \u90E8\u7F72\u8BED\u4E49\uFF1AremoteEntry/manifest/index.html \u5FC5\u987B no-cache\uFF08\u4E25\u7981 immutable\uFF09\uFF1B\u5E26 hash \u7684 assets \u957F\u7F13\u5B58");
|
|
669
|
+
out.push(...COMMON_CHECKLIST);
|
|
213
670
|
return out.join("\n");
|
|
214
671
|
}
|
|
215
|
-
var MARK, STARTER_CONFIG,
|
|
672
|
+
var MARK, STARTER_CONFIG, COMMON_CHECKLIST;
|
|
216
673
|
var init_init = __esm({
|
|
217
674
|
"src/init.ts"() {
|
|
218
675
|
"use strict";
|
|
676
|
+
init_app_config();
|
|
219
677
|
init_config();
|
|
220
678
|
MARK = "[fulgurjs:init]";
|
|
221
|
-
STARTER_CONFIG = `// fulgurjs.config.ts \u2014\u2014 @fulgurjs/federation \u63A5\u5165\u914D\u7F6E\uFF08\
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
import
|
|
679
|
+
STARTER_CONFIG = `// fulgurjs.config.ts \u2014\u2014 @fulgurjs/federation \u5355\u9879\u76EE\u63A5\u5165\u914D\u7F6E\uFF08\u4E00\u9879\u76EE\u4E00\u4EFD\uFF0C\u53EF\u5165\u5E93\u3001\u53EF\u590D\u8DD1\uFF09
|
|
680
|
+
//
|
|
681
|
+
// Vite \u63A5\u5165\uFF08\u672C\u9879\u76EE vite.config.ts \u53EA\u9700\u4E24\u884C\u8054\u90A6\u76F8\u5173\u4EE3\u7801\uFF09\uFF1A
|
|
682
|
+
// import federation from '@fulgurjs/federation'
|
|
683
|
+
// import fulgurjsConfig from './fulgurjs.config'
|
|
684
|
+
// // plugins: [ ...\u539F\u6709\u63D2\u4EF6, federation(fulgurjsConfig) ]
|
|
685
|
+
//
|
|
686
|
+
// CLI\uFF08\u7EAF\u672C\u5730\uFF0C\u65E0\u7F51\u7EDC\uFF09\uFF1A
|
|
687
|
+
// npx fulgurjs explain # \u89E3\u91CA\u672C\u5E94\u7528\u6709\u6548\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE
|
|
688
|
+
// npx fulgurjs check-pages # \u5BBF\u4E3B\u9875\u9762\u8868 \u2194 \u8FDC\u7A0B manifest \u5951\u7EA6\u6838\u5BF9
|
|
689
|
+
// npx fulgurjs doctor --base http://<\u7AD9\u70B9> --apps <\u5BB9\u5668\u540D> # \u90E8\u7F72\u4F53\u68C0
|
|
690
|
+
import type { FederationOptions } from '@fulgurjs/federation'
|
|
225
691
|
|
|
226
|
-
export default
|
|
227
|
-
// \
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
base: '/remote-a',
|
|
257
|
-
remote: {
|
|
258
|
-
exposes: {
|
|
259
|
-
'./pages/remote-a/home': './src/views/Home.vue',
|
|
260
|
-
'./pages/remote-a/detail': './src/views/Detail.vue',
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
],
|
|
265
|
-
// \u90E8\u7F72\u4FE1\u606F\uFF08\u53EF\u9009\uFF09\uFF1A\u4EC5\u4F9B init \u8F93\u51FA NGINX \u6837\u677F\u65F6\u4F7F\u7528
|
|
266
|
-
deploy: { webRoot: '/var/www/your-site', listen: 8080 },
|
|
267
|
-
})
|
|
692
|
+
export default {
|
|
693
|
+
// \u8054\u90A6\u5BB9\u5668\u540D\uFF08\u8FDC\u7A0B\u5F15\u7528\u5B83\u65F6\u7528\u8FD9\u4E2A\u540D\uFF1B\u540C\u4E00\u9875\u9762\u5185\u552F\u4E00\uFF09
|
|
694
|
+
name: 'my-app',
|
|
695
|
+
// \u672C\u5E94\u7528\u5BF9\u5916\u66B4\u9732\u7684\u6A21\u5757\uFF1A'./\u952E' \u2192 \u76F8\u5BF9\u672C\u9879\u76EE\u6839\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84
|
|
696
|
+
exposes: {
|
|
697
|
+
'./pages/home': './src/views/Home.vue',
|
|
698
|
+
},
|
|
699
|
+
// \u672C\u5E94\u7528\u6D88\u8D39\u7684\u8FDC\u7A0B\uFF08\u952E = import \u524D\u7F00\uFF1Bdev/prod \u5730\u5740\u4E8C\u6BB5\u5F0F\uFF0C\u5355\u5730\u5740\u5B57\u7B26\u4E32\u4E5F\u884C\uFF09
|
|
700
|
+
remotes: {
|
|
701
|
+
'remote-a': { dev: 'http://localhost:5174/remote-a', prod: '/remote-a' },
|
|
702
|
+
},
|
|
703
|
+
// \u53EF\u9009\uFF1A\u8FDC\u7A0B\u9700\u8981\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF08\u5168\u5C40\u6837\u5F0F/\u7EC4\u4EF6/locale\uFF09\u65F6\u58F0\u660E\u5165\u53E3\u6587\u4EF6\u2014\u2014
|
|
704
|
+
// \u9ED8\u8BA4\u5BFC\u51FA setup(context) \u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF1B\u53EF\u9009\u5177\u540D\u5BFC\u51FA onSession(context)
|
|
705
|
+
// \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\u6267\u884C\u3002\u7F3A\u7701 = \u65E0\u521D\u59CB\u5316\u884C\u4E3A\uFF08\u666E\u901A expose \u8BED\u4E49\u4E0D\u53D8\uFF09
|
|
706
|
+
// setup: './src/fulgurjs/setup.ts',
|
|
707
|
+
shared: {
|
|
708
|
+
vue: { singleton: true, requiredVersion: '^3.4.0' },
|
|
709
|
+
'vue-router': { singleton: true, requiredVersion: '^4.4.5' },
|
|
710
|
+
pinia: { singleton: true, requiredVersion: '^2.1.7' },
|
|
711
|
+
},
|
|
712
|
+
} satisfies FederationOptions
|
|
713
|
+
|
|
714
|
+
// \u2500\u2500 \u4EE5\u4E0B\u4EC5\u4F9B CLI explain/check-pages \u8BFB\u53D6\uFF08\u4E0D\u662F federation() \u7684\u53C2\u6570\uFF09\u2500\u2500
|
|
715
|
+
// \u5BBF\u4E3B\u5E94\u7528\uFF1A\u628A\u9875\u9762\u8DEF\u7531\u8868\u4EE5\u5177\u540D\u5BFC\u51FA hostPages \u63D0\u4F9B\u7ED9 CLI\uFF0C\u4E0E\u8FD0\u884C\u65F6 createHostPages
|
|
716
|
+
// \u6D88\u8D39\u540C\u4E00\u4EFD\u6570\u636E\u6A21\u5757\uFF08\u552F\u4E00\u624B\u5DE5\u7EF4\u62A4\u4F4D\u7F6E\uFF1B\u7EAF\u8FDC\u7A0B\u5E94\u7528\u5220\u9664\u672C\u6BB5\u5373\u53EF\uFF09\u3002
|
|
717
|
+
// \u7EAF\u6570\u636E\u6A21\u5757\u793A\u4F8B src/fulgurjs/host/pages.data.ts\uFF1A
|
|
718
|
+
// export const remotePrefixes = { '/remote-a/': 'remote-a' }
|
|
719
|
+
// export const pages = [{ route: '/remote-a/home', name: 'Home', title: '\u9996\u9875' }]
|
|
720
|
+
// import { pages, remotePrefixes } from './src/fulgurjs/host/pages.data'
|
|
721
|
+
// export const hostPages = { pages, remotePrefixes }
|
|
268
722
|
`;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
723
|
+
COMMON_CHECKLIST = [
|
|
724
|
+
"1. \u5B89\u88C5\u4F9D\u8D56\uFF1Anpm/pnpm add @fulgurjs/federation",
|
|
725
|
+
"2. expose \u4E00\u5F8B\u6307\u5411\u72EC\u7ACB\u9875\uFF08\u9875\u9762\u4ECE\u8DEF\u7531\u53D6\u53C2\uFF09\uFF1B\u7EC4\u4EF6\u9700\u8981\u5FC5\u586B props \u65F6\u7ED9\u9ED8\u8BA4\u503C\uFF08BLD-003\uFF09",
|
|
726
|
+
"3. shared \u91CC vue / vue-router / pinia \u5EFA\u8BAE singleton: true\u2014\u2014\u8DE8\u5E94\u7528\u5FC5\u987B\u540C\u5B9E\u4F8B\uFF08\u5168\u5C40\u54CD\u5E94\u6027\u3001getActivePinia\u3001\u8DEF\u7531\u6CE8\u5165\uFF09",
|
|
727
|
+
"4. \u8FDC\u7A0B\u9700\u8981\u542F\u52A8\u671F\u521D\u59CB\u5316\uFF08\u5168\u5C40\u7EC4\u4EF6/\u6837\u5F0F/locale \u7B49\uFF09\u65F6\uFF0C\u5728 fulgurjs.config.ts \u7684 setup \u58F0\u660E\u5165\u53E3\u6587\u4EF6\uFF1A",
|
|
728
|
+
" \u9ED8\u8BA4\u5BFC\u51FA setup(context) \u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF08\u5BB9\u5668\u9996\u6B21\u88AB\u52A0\u8F7D\u4E1A\u52A1\u6A21\u5757\u524D\uFF09\uFF1B\u53EF\u9009\u5177\u540D\u5BFC\u51FA onSession(context)",
|
|
729
|
+
" \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\u6267\u884C\uFF08\u6362\u8D26\u53F7/\u91CD\u767B\u81EA\u52A8\u91CD\u8DD1\uFF09\u3002\u5BBF\u4E3B\u65E0\u9700\u518D\u624B\u5199\u300CloadRemote \u542F\u52A8\u5668\u5E76\u8C03\u7528\u300D\uFF1B",
|
|
730
|
+
" \u8DE8\u5E94\u7528\u4F20\u503C\u7EDF\u4E00\u8D70 @fulgurjs/federation/runtime \u7684 context \u51FD\u6570\uFF1A\u5BBF\u4E3B provideAppContext \u5199\u5165",
|
|
731
|
+
" \uFF08\u9000\u51FA\u65F6 clearAppContext \u6E05\u7406\uFF09\uFF0C\u8FDC\u7A0B setup/onSession \u7528 getAppContext / requireAppContext \u6D88\u8D39",
|
|
732
|
+
"5. \u5E94\u7528\u4EE3\u7801\u552F\u4E00 API \u5165\u53E3\uFF1Aimport { loadRemote, provideAppContext, getAppContext, clearAppContext, definePages, createHostPages, remoteSchema, remoteComponent } from '@fulgurjs/federation/runtime'",
|
|
733
|
+
'6. dev \u51B7\u542F\u52A8\u9996\u8F6E 30~60s \u6709\u9884\u6784\u5EFA\u7A97\u53E3\uFF08\u77AC\u65F6 504/"ce"\uFF0CDEV-010\uFF09\uFF1A\u5148\u771F\u5B9E\u6253\u5F00\u9875\u9762\u9884\u70ED\u518D\u505A\u65AD\u8A00',
|
|
734
|
+
"7. \u914D\u7F6E\u89E3\u91CA\uFF1Afulgurjs explain\uFF08\u5355\u9879\u76EE\u5F62\u6001\u514D --app\uFF09\uFF1B\u9875\u9762\u5951\u7EA6\u6838\u5BF9\uFF1Afulgurjs check-pages\uFF08\u5BBF\u4E3B\u9879\u76EE\u8FD0\u884C\uFF0C--manifest/--site \u6307\u5B9A\u8FDC\u7A0B manifest \u6765\u6E90\uFF09\uFF1B",
|
|
735
|
+
" \u90E8\u7F72\u4F53\u68C0\uFF1Afulgurjs doctor --base <URL> --apps <\u5BB9\u5668\u540D...>",
|
|
736
|
+
"8. \u90E8\u7F72\u8BED\u4E49\uFF1AremoteEntry/manifest/index.html \u5FC5\u987B no-cache\uFF08\u4E25\u7981 immutable\uFF09\uFF1B\u5E26 hash \u7684 assets \u957F\u7F13\u5B58"
|
|
737
|
+
];
|
|
274
738
|
}
|
|
275
739
|
});
|
|
276
740
|
|
|
277
|
-
// src/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
741
|
+
// src/pages.ts
|
|
742
|
+
function defaultDeriveSpec(route) {
|
|
743
|
+
return segs(route).slice(1).filter((seg) => !seg.startsWith(":")).join("/");
|
|
744
|
+
}
|
|
745
|
+
function shadows(earlier, later) {
|
|
746
|
+
const a = segs(earlier);
|
|
747
|
+
const b = segs(later);
|
|
748
|
+
if (a.length !== b.length) return null;
|
|
749
|
+
if (earlier === later) return "full";
|
|
750
|
+
let hasParam2 = false;
|
|
751
|
+
for (let i = 0; i < a.length; i++) {
|
|
752
|
+
if (a[i] === b[i]) continue;
|
|
753
|
+
if (a[i].startsWith(":")) {
|
|
754
|
+
hasParam2 = true;
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
return hasParam2 ? "param" : null;
|
|
760
|
+
}
|
|
761
|
+
function remoteOfRoute(route, remotes) {
|
|
762
|
+
if (!remotes) return null;
|
|
763
|
+
for (const [prefix, name] of Object.entries(remotes)) {
|
|
764
|
+
if (route === prefix.replace(/\/+$/, "") || route.startsWith(prefix)) return name;
|
|
765
|
+
}
|
|
766
|
+
return null;
|
|
767
|
+
}
|
|
768
|
+
function validatePages(pages, options = {}) {
|
|
769
|
+
const deriveSpec = options.deriveSpec ?? defaultDeriveSpec;
|
|
770
|
+
const violations = [];
|
|
771
|
+
const effective = pages.map((p) => normSpec(p.spec ?? deriveSpec(p.route)));
|
|
772
|
+
pages.forEach((p, i) => {
|
|
773
|
+
if (p.spec || !hasParam(p.route)) return;
|
|
774
|
+
pages.forEach((q, j) => {
|
|
775
|
+
if (i === j) return;
|
|
776
|
+
if (effective[j] !== effective[i]) return;
|
|
777
|
+
violations.push({
|
|
778
|
+
rule: "R1",
|
|
779
|
+
level: "error",
|
|
780
|
+
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
|
|
781
|
+
\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
|
|
782
|
+
\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
|
|
783
|
+
\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`
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
const seenSpec = /* @__PURE__ */ new Map();
|
|
788
|
+
pages.forEach((p, i) => {
|
|
789
|
+
const key = effective[i];
|
|
790
|
+
if (seenSpec.has(key)) {
|
|
791
|
+
const first = pages[seenSpec.get(key)];
|
|
792
|
+
const bothExplicit = !!p.spec && !!first.spec;
|
|
793
|
+
violations.push({
|
|
794
|
+
rule: "R2",
|
|
795
|
+
level: "warn",
|
|
796
|
+
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`
|
|
797
|
+
});
|
|
798
|
+
} else {
|
|
799
|
+
seenSpec.set(key, i);
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
if (options.schema && options.remotes) {
|
|
803
|
+
pages.forEach((p, i) => {
|
|
804
|
+
const remote = remoteOfRoute(p.route, options.remotes);
|
|
805
|
+
if (!remote) return;
|
|
806
|
+
const entry = options.schema[remote];
|
|
807
|
+
if (!entry || entry.exists === false) return;
|
|
808
|
+
const exposes = new Set((entry.exposes ?? []).map(normSpec));
|
|
809
|
+
if (!exposes.has(effective[i])) {
|
|
810
|
+
violations.push({
|
|
811
|
+
rule: "R3",
|
|
812
|
+
level: "error",
|
|
813
|
+
message: `[R3] \u8DEF\u7531 "${p.route}" \u7684 spec "${effective[i]}" \u4E0D\u5728\u8FDC\u7A0B "${remote}" \u7684 exposes \u6E05\u5355\u4E2D\u3002
|
|
814
|
+
\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
|
|
815
|
+
\u4FEE\u6CD5\uFF1A\u6838\u5BF9\u8FDC\u7A0B vite.config exposes \u952E\u540D\uFF0C\u6216\u4FEE\u6B63\u672C\u8868 spec\u3002
|
|
816
|
+
\u8FDC\u7A0B\u5B9E\u9645 exposes\uFF08\u524D 10\uFF09\uFF1A${[...exposes].slice(0, 10).join("\u3001") || "\uFF08\u7A7A\uFF09"}`
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
pages.forEach((p, i) => {
|
|
822
|
+
pages.forEach((q, j) => {
|
|
823
|
+
if (j <= i) return;
|
|
824
|
+
const kind = shadows(p.route, q.route);
|
|
825
|
+
if (!kind) return;
|
|
826
|
+
violations.push({
|
|
827
|
+
rule: "R4",
|
|
828
|
+
level: "error",
|
|
829
|
+
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
|
|
830
|
+
\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`
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
const seenName = /* @__PURE__ */ new Map();
|
|
835
|
+
pages.forEach((p, i) => {
|
|
836
|
+
const name = typeof p.name === "string" ? p.name : "";
|
|
837
|
+
if (!name) return;
|
|
838
|
+
if (seenName.has(name)) {
|
|
839
|
+
violations.push({
|
|
840
|
+
rule: "R5",
|
|
841
|
+
level: "warn",
|
|
842
|
+
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`
|
|
843
|
+
});
|
|
844
|
+
} else {
|
|
845
|
+
seenName.set(name, i);
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
return violations;
|
|
849
|
+
}
|
|
850
|
+
var normSpec, hasParam, segs;
|
|
851
|
+
var init_pages = __esm({
|
|
852
|
+
"src/pages.ts"() {
|
|
853
|
+
"use strict";
|
|
854
|
+
normSpec = (s) => s.replace(/^[./]+/, "").replace(/\/+$/, "");
|
|
855
|
+
hasParam = (route) => /(^|\/):[\w]+/.test(route);
|
|
856
|
+
segs = (route) => route.split("/").filter(Boolean);
|
|
857
|
+
}
|
|
858
|
+
});
|
|
283
859
|
|
|
284
|
-
// src/
|
|
285
|
-
var
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
860
|
+
// src/commands.ts
|
|
861
|
+
var commands_exports = {};
|
|
862
|
+
__export(commands_exports, {
|
|
863
|
+
checkPages: () => checkPages,
|
|
864
|
+
explainApp: () => explainApp,
|
|
865
|
+
formatCheckPages: () => formatCheckPages,
|
|
866
|
+
formatExplain: () => formatExplain
|
|
867
|
+
});
|
|
868
|
+
import fs2 from "node:fs";
|
|
869
|
+
import path2 from "node:path";
|
|
870
|
+
function roleOf(app) {
|
|
871
|
+
if (app.host && app.remote) return "dual";
|
|
872
|
+
return app.host ? "host" : "remote";
|
|
292
873
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
874
|
+
function pageMappings(app) {
|
|
875
|
+
const pages = app.host?.pages ?? [];
|
|
876
|
+
const prefixes = Object.entries(app.host?.remotePrefixes ?? {}).sort((a, b) => b[0].length - a[0].length);
|
|
877
|
+
const remoteOf = (route) => {
|
|
878
|
+
for (const [prefix, name] of prefixes) {
|
|
879
|
+
const p = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
880
|
+
if (route.startsWith(p) || `${route}/` === p) return name;
|
|
881
|
+
}
|
|
882
|
+
return null;
|
|
883
|
+
};
|
|
884
|
+
const derive = app.host?.deriveSpec ?? ((route) => route.split("/").filter(Boolean).slice(1).filter((s) => !s.startsWith(":")).join("/"));
|
|
885
|
+
return mappingsOf({ pages, remotePrefixes: app.host?.remotePrefixes ?? {}, deriveSpec: app.host?.deriveSpec }, derive);
|
|
295
886
|
}
|
|
296
|
-
function
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
887
|
+
function mappingsOf(hp, deriveFallback) {
|
|
888
|
+
const pages = hp.pages ?? [];
|
|
889
|
+
const prefixes = Object.entries(hp.remotePrefixes ?? {}).sort((a, b) => b[0].length - a[0].length);
|
|
890
|
+
const remoteOf = (route) => {
|
|
891
|
+
for (const [prefix, name] of prefixes) {
|
|
892
|
+
const p = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
893
|
+
if (route.startsWith(p) || `${route}/` === p) return name;
|
|
894
|
+
}
|
|
895
|
+
return null;
|
|
896
|
+
};
|
|
897
|
+
const derive = hp.deriveSpec ?? deriveFallback;
|
|
898
|
+
const out = [];
|
|
899
|
+
for (const page of pages) {
|
|
900
|
+
const remote = remoteOf(page.route);
|
|
901
|
+
if (!remote) continue;
|
|
902
|
+
const spec = `${remote}/${String(page.spec ?? derive(page.route)).replace(/^[./]+/, "")}`;
|
|
903
|
+
out.push({ route: page.route, remote, spec, title: typeof page.title === "string" ? page.title : void 0 });
|
|
300
904
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
function remotesOfOptions(options) {
|
|
908
|
+
return Object.entries(options.remotes ?? {}).map(([key, v]) => {
|
|
909
|
+
const cfg = typeof v === "string" ? { external: v } : v;
|
|
910
|
+
return { key, dev: String(cfg.dev ?? cfg.external ?? ""), prod: String(cfg.prod ?? cfg.external ?? "") };
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
function sharedOfOptions(options) {
|
|
914
|
+
const shared = options.shared;
|
|
915
|
+
if (!shared) return [];
|
|
916
|
+
if (Array.isArray(shared)) {
|
|
917
|
+
return shared.map((name) => ({ name, singleton: false, requiredVersion: false }));
|
|
918
|
+
}
|
|
919
|
+
return Object.entries(shared).map(([name, v]) => {
|
|
920
|
+
const hint = typeof v === "string" ? { requiredVersion: v } : v ?? {};
|
|
921
|
+
return {
|
|
922
|
+
name,
|
|
923
|
+
singleton: !!hint.singleton,
|
|
924
|
+
requiredVersion: hint.requiredVersion ?? false
|
|
925
|
+
};
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
function chainLines(options, role) {
|
|
929
|
+
const chain = [];
|
|
930
|
+
chain.push(role === "remote" ? "\u88AB\u5BBF\u4E3B\u6D88\u8D39\uFF1AremoteEntry/\u5171\u4EAB\u4F9D\u8D56\u52A0\u8F7D \u2192 \u5BBF\u4E3B\u9996\u6B21 loadRemote \u672C\u5E94\u7528\u6A21\u5757\u65F6\u6267\u884C\u53EF\u9009 setup/onSession \u2192 \u9875\u9762\u6A21\u5757\u5728\u5BBF\u4E3B\u5E03\u5C40\u5185\u6E32\u67D3" : "\u5BBF\u4E3B\u63D0\u4F9B AppContext\uFF08provideAppContext\uFF0C\u542B sessionKey\uFF09\u2192 \u52A0\u8F7D remoteEntry/\u5171\u4EAB\u4F9D\u8D56 \u2192 \u9996\u6B21 loadRemote \u6267\u884C\u8BE5\u8FDC\u7A0B\u53EF\u9009 setup/onSession \u2192 \u53D6\u5F97\u9875\u9762\u6A21\u5757 \u2192 \u5BBF\u4E3B\u5E03\u5C40\u6E32\u67D3");
|
|
931
|
+
if (options.setup) {
|
|
932
|
+
chain.push(`setup \u5165\u53E3 ${options.setup}\uFF1A\u9ED8\u8BA4\u5BFC\u51FA\u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF1BonSession \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\uFF08\u7F3A sessionKey \u65F6\u62A5 MFU-013\uFF09`);
|
|
933
|
+
} else {
|
|
934
|
+
chain.push("\u672C\u5E94\u7528\u672A\u914D\u7F6E setup\uFF1A\u65E0\u521D\u59CB\u5316\u751F\u547D\u5468\u671F\uFF0CloadRemote \u76F4\u63A5\u8FD4\u56DE\u6A21\u5757\uFF08\u666E\u901A expose \u8BED\u4E49\uFF09");
|
|
935
|
+
}
|
|
936
|
+
return chain;
|
|
937
|
+
}
|
|
938
|
+
function explainAppMode(loaded, appName) {
|
|
939
|
+
const options = loaded.options;
|
|
940
|
+
if (appName && appName !== options.name) {
|
|
941
|
+
throw new Error(
|
|
942
|
+
`[fulgurjs:explain] \u5355\u9879\u76EE\u914D\u7F6E\u53EA\u63CF\u8FF0\u672C\u5E94\u7528\uFF08name: "${options.name}"\uFF09\uFF0C\u4E0E --app "${appName}" \u4E0D\u4E00\u81F4
|
|
943
|
+
\u4FEE\u6CD5: \u53BB\u6389 --app\uFF0C\u6216\u6539\u4E3A --app ${options.name}`
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
const role = roleOfOptions(options);
|
|
947
|
+
const devSharedSelfExplicit = options.devSharedSelf !== void 0;
|
|
948
|
+
const devSharedSelfValue = devSharedSelfExplicit ? options.devSharedSelf === true : role !== "host";
|
|
949
|
+
const hp = loaded.hostPages;
|
|
950
|
+
return {
|
|
951
|
+
app: options.name,
|
|
952
|
+
role,
|
|
953
|
+
mode: "app",
|
|
954
|
+
path: loaded.appRoot,
|
|
955
|
+
remotes: remotesOfOptions(options),
|
|
956
|
+
exposes: Object.keys(options.exposes ?? {}),
|
|
957
|
+
setup: options.setup,
|
|
958
|
+
shared: sharedOfOptions(options),
|
|
959
|
+
devSharedSelf: { value: devSharedSelfValue, source: devSharedSelfExplicit ? "explicit" : "inferred" },
|
|
960
|
+
pages: hp ? mappingsOf(hp, DEFAULT_DERIVE) : [],
|
|
961
|
+
pagesSource: hp ? `fulgurjs.config.ts \u7684 hostPages \u5177\u540D\u5BFC\u51FA\uFF08${hp.pages.length} \u6761\uFF1B\u8FD0\u884C\u65F6\u771F\u6E90\u4E3A\u5E94\u7528\u5185\u540C\u4E00\u6570\u636E\u6A21\u5757\uFF09` : "\u672A\u627E\u5230 hostPages \u5177\u540D\u5BFC\u51FA\uFF08\u7EAF\u8FDC\u7A0B\u5E94\u7528\u65E0\u9700\u9875\u9762\u8868\uFF1B\u5BBF\u4E3B\u82E5\u8981\u8DD1 check-pages \u8BF7\u5728 fulgurjs.config.ts \u8865\u5177\u540D\u5BFC\u51FA hostPages\uFF09",
|
|
962
|
+
chain: chainLines(options, role)
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
function explainRepoMode(cfg, appPathOrName) {
|
|
966
|
+
const app = cfg.apps.find((a) => a.path === appPathOrName || a.name === appPathOrName);
|
|
967
|
+
if (!app) {
|
|
968
|
+
throw new Error(
|
|
969
|
+
`[fulgurjs:explain] \u5E94\u7528 "${appPathOrName}" \u4E0D\u5728\u914D\u7F6E\u4E2D\uFF08\u53EF\u7528\uFF1A${cfg.apps.map((a) => a.path).join("\u3001")}\uFF09`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
const opts = federationOptionsForApp(cfg, appPathOrName);
|
|
973
|
+
const remotes = Object.entries(opts.remotes ?? {}).map(
|
|
974
|
+
([key, v]) => ({ key, dev: String(v.dev ?? v.external ?? ""), prod: String(v.prod ?? v.external ?? "") })
|
|
975
|
+
);
|
|
976
|
+
const shared = Object.entries(opts.shared ?? {}).map(
|
|
977
|
+
([name, v]) => ({ name, singleton: !!v.singleton, requiredVersion: v.requiredVersion ?? false })
|
|
978
|
+
);
|
|
979
|
+
const chain = [];
|
|
980
|
+
chain.push(app.host ? "\u5BBF\u4E3B\u63D0\u4F9B AppContext\uFF08provideAppContext\uFF0C\u542B sessionKey\uFF09\u2192 \u52A0\u8F7D remoteEntry/\u5171\u4EAB\u4F9D\u8D56 \u2192 \u9996\u6B21 loadRemote \u6267\u884C\u8BE5\u8FDC\u7A0B\u53EF\u9009 setup/onSession \u2192 \u53D6\u5F97\u9875\u9762\u6A21\u5757 \u2192 \u5BBF\u4E3B\u5E03\u5C40\u6E32\u67D3" : "\u88AB\u5BBF\u4E3B\u6D88\u8D39\uFF1AremoteEntry/\u5171\u4EAB\u4F9D\u8D56\u52A0\u8F7D \u2192 \u5BBF\u4E3B\u9996\u6B21 loadRemote \u672C\u5E94\u7528\u6A21\u5757\u65F6\u6267\u884C\u53EF\u9009 setup/onSession \u2192 \u9875\u9762\u6A21\u5757\u5728\u5BBF\u4E3B\u5E03\u5C40\u5185\u6E32\u67D3");
|
|
981
|
+
if (app.remote?.setup) {
|
|
982
|
+
chain.push(`setup \u5165\u53E3 ${app.remote.setup}\uFF1A\u9ED8\u8BA4\u5BFC\u51FA\u5E94\u7528\u7EA7\u6267\u884C\u4E00\u6B21\uFF1B${"onSession"} \u6309\u5BBF\u4E3B sessionKey \u53BB\u91CD\uFF08\u7F3A sessionKey \u65F6\u62A5 MFU-013\uFF09`);
|
|
983
|
+
} else {
|
|
984
|
+
chain.push("\u672C\u5E94\u7528\u672A\u914D\u7F6E setup\uFF1A\u65E0\u521D\u59CB\u5316\u751F\u547D\u5468\u671F\uFF0CloadRemote \u76F4\u63A5\u8FD4\u56DE\u6A21\u5757\uFF08\u666E\u901A expose \u8BED\u4E49\uFF09");
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
app: app.name,
|
|
988
|
+
role: roleOf(app),
|
|
989
|
+
mode: "repo",
|
|
990
|
+
path: app.path,
|
|
991
|
+
base: app.base,
|
|
992
|
+
port: app.port,
|
|
993
|
+
remotes,
|
|
994
|
+
exposes: Object.keys(app.remote?.exposes ?? {}),
|
|
995
|
+
setup: app.remote?.setup,
|
|
996
|
+
shared,
|
|
997
|
+
devSharedSelf: {
|
|
998
|
+
value: opts.devSharedSelf === true,
|
|
999
|
+
source: app.devSharedSelf === void 0 ? "inferred" : "explicit"
|
|
1000
|
+
},
|
|
1001
|
+
pages: pageMappings(app),
|
|
1002
|
+
pagesSource: app.host?.pages ? "\u4ED3\u5E93\u914D\u7F6E host.pages \u526F\u672C\uFF08\u517C\u5BB9\u5F62\u6001\uFF1B\u5355\u9879\u76EE\u5F62\u6001\u8BF7\u6539\u7528 hostPages \u5177\u540D\u5BFC\u51FA\uFF09" : "\u4ED3\u5E93\u914D\u7F6E\u672A\u63D0\u4F9B\u9875\u9762\u8868\u2014\u2014\u5E94\u7528\u4EE3\u7801\u9875\u9762\u8868\u4E3A\u8FD0\u884C\u65F6\u771F\u6E90",
|
|
1003
|
+
chain
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
async function explainApp(configPath, appName) {
|
|
1007
|
+
const loaded = await loadAppConfig(configPath);
|
|
1008
|
+
if (loaded.kind === "repo") {
|
|
1009
|
+
if (!appName) {
|
|
1010
|
+
throw new Error("[fulgurjs:explain] \u805A\u5408\u914D\u7F6E\u9700 --app <\u5E94\u7528\u76EE\u5F55\u540D\u6216\u5BB9\u5668\u540D>\uFF08\u5355\u9879\u76EE\u914D\u7F6E\u53EF\u7701\u7565\uFF09");
|
|
317
1011
|
}
|
|
1012
|
+
return explainRepoMode(loaded.repo, appName);
|
|
318
1013
|
}
|
|
319
|
-
|
|
320
|
-
|
|
1014
|
+
return explainAppMode(loaded, appName);
|
|
1015
|
+
}
|
|
1016
|
+
function formatExplain(r) {
|
|
1017
|
+
const roleText = r.role === "dual" ? "\u5BBF\u4E3B+\u8FDC\u7A0B\uFF08\u53CC\u89D2\u8272\uFF09" : r.role === "host" ? "\u5BBF\u4E3B" : "\u8FDC\u7A0B";
|
|
1018
|
+
const L = [];
|
|
1019
|
+
if (r.mode === "app") {
|
|
1020
|
+
L.push(`\u5E94\u7528 ${r.app}\uFF08${roleText}\uFF0C\u5355\u9879\u76EE\u914D\u7F6E\uFF0C\u76EE\u5F55 ${r.path}\uFF1Bbase/dev \u7AEF\u53E3\u7531 vite.config.ts \u7BA1\u7406\uFF09`);
|
|
1021
|
+
} else {
|
|
1022
|
+
L.push(`\u5E94\u7528 ${r.app}\uFF08${roleText}\uFF0C\u76EE\u5F55 ${r.path}\uFF0Cdev \u7AEF\u53E3 ${r.port}\uFF0Cbase ${r.base}\uFF09\u3010\u65E7\u805A\u5408\u914D\u7F6E\u5F62\u6001\uFF0C\u517C\u5BB9\u671F\u3011`);
|
|
321
1023
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
1024
|
+
if (r.remotes.length) {
|
|
1025
|
+
L.push("\u6D88\u8D39\u8FDC\u7A0B\uFF1A");
|
|
1026
|
+
for (const x of r.remotes) L.push(` ${x.key} \u2192 dev ${x.dev} / prod ${x.prod}`);
|
|
1027
|
+
} else {
|
|
1028
|
+
L.push("\u6D88\u8D39\u8FDC\u7A0B\uFF1A\uFF08\u65E0\uFF09");
|
|
1029
|
+
}
|
|
1030
|
+
L.push(`\u516C\u5F00 exposes\uFF08${r.exposes.length}\uFF09\uFF1A${r.exposes.join("\u3001") || "\uFF08\u7A7A\uFF09"}`);
|
|
1031
|
+
L.push(`\u5185\u90E8 setup\uFF1A${r.setup ?? "\uFF08\u672A\u914D\u7F6E\u2014\u2014\u65E0\u521D\u59CB\u5316\u751F\u547D\u5468\u671F\uFF09"}`);
|
|
1032
|
+
if (r.shared.length) {
|
|
1033
|
+
L.push("shared\uFF1A");
|
|
1034
|
+
for (const s of r.shared) {
|
|
1035
|
+
L.push(` ${s.name}${s.singleton ? " [singleton]" : ""} requiredVersion=${String(s.requiredVersion)}`);
|
|
329
1036
|
}
|
|
330
|
-
|
|
331
|
-
|
|
1037
|
+
}
|
|
1038
|
+
L.push(`devSharedSelf\uFF1A${r.devSharedSelf.value}\uFF08\u6765\u6E90\uFF1A${r.devSharedSelf.source === "explicit" ? "\u663E\u5F0F\u914D\u7F6E" : "\u6309\u89D2\u8272\u63A8\u65AD\u2014\u2014\u63D0\u4F9B exposes/setup \u7684\u5E94\u7528\u4E3A true\uFF0C\u7EAF\u5BBF\u4E3B\u4E3A false"}\uFF09`);
|
|
1039
|
+
if (r.pages.length) {
|
|
1040
|
+
L.push(`\u9875\u9762 spec \u6620\u5C04\uFF08${r.pages.length}\uFF09\u2014\u2014\u6765\u6E90\uFF1A${r.pagesSource}\uFF1A`);
|
|
1041
|
+
for (const p of r.pages) L.push(` ${p.route} \u2192 ${p.spec}${p.title ? `\uFF08${p.title}\uFF09` : ""}`);
|
|
1042
|
+
} else {
|
|
1043
|
+
L.push(`\u9875\u9762 spec \u6620\u5C04\uFF1A\uFF08\u65E0\uFF09\u2014\u2014\u6765\u6E90\uFF1A${r.pagesSource}`);
|
|
1044
|
+
}
|
|
1045
|
+
L.push("\u52A0\u8F7D\u94FE\uFF1A");
|
|
1046
|
+
for (const c of r.chain) L.push(` \xB7 ${c}`);
|
|
1047
|
+
return L.join("\n");
|
|
1048
|
+
}
|
|
1049
|
+
function manifestFromFile(p) {
|
|
1050
|
+
try {
|
|
1051
|
+
const parsed = parseManifest(JSON.parse(fs2.readFileSync(p, "utf8")));
|
|
1052
|
+
if (parsed.manifest && !parsed.unsupportedVersion) return parsed.manifest;
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
return void 0;
|
|
1056
|
+
}
|
|
1057
|
+
async function manifestFromUrl(url) {
|
|
1058
|
+
try {
|
|
1059
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(5e3) });
|
|
1060
|
+
if (res.ok) {
|
|
1061
|
+
const parsed = parseManifest(await res.json());
|
|
1062
|
+
if (parsed.manifest && !parsed.unsupportedVersion) return parsed.manifest;
|
|
332
1063
|
}
|
|
333
|
-
|
|
334
|
-
|
|
1064
|
+
} catch {
|
|
1065
|
+
}
|
|
1066
|
+
return void 0;
|
|
1067
|
+
}
|
|
1068
|
+
async function fetchRemoteManifestRepo(cfg, app, opts) {
|
|
1069
|
+
const explicit = opts.manifests?.[app.name] ?? opts.manifests?.[app.path];
|
|
1070
|
+
if (explicit) {
|
|
1071
|
+
const fromUrl = /^https?:\/\//.test(explicit);
|
|
1072
|
+
const got = fromUrl ? await manifestFromUrl(explicit) : manifestFromFile(explicit);
|
|
1073
|
+
if (got) return { manifest: got, from: explicit };
|
|
1074
|
+
return void 0;
|
|
1075
|
+
}
|
|
1076
|
+
const candidates = [app.deployDir || app.base.replace(/^\/|\/$/g, ""), "dist"];
|
|
1077
|
+
for (const dir of candidates) {
|
|
1078
|
+
const p = path2.join(cfg.root, app.path, dir, "fulgurjs-manifest.json");
|
|
1079
|
+
const got = manifestFromFile(p);
|
|
1080
|
+
if (got) return { manifest: got, from: p };
|
|
1081
|
+
}
|
|
1082
|
+
if (opts.site) {
|
|
1083
|
+
const base = app.base === "/" ? "" : app.base.replace(/\/$/, "");
|
|
1084
|
+
const url = `${opts.site.replace(/\/$/, "")}${base}/fulgurjs-manifest.json`;
|
|
1085
|
+
const got = await manifestFromUrl(url);
|
|
1086
|
+
if (got) return { manifest: got, from: url };
|
|
1087
|
+
}
|
|
1088
|
+
return void 0;
|
|
1089
|
+
}
|
|
1090
|
+
async function fetchRemoteManifestApp(loaded, options, remoteName, opts) {
|
|
1091
|
+
const explicit = opts.manifests?.[remoteName];
|
|
1092
|
+
if (explicit) {
|
|
1093
|
+
const fromUrl = /^https?:\/\//.test(explicit);
|
|
1094
|
+
const got = fromUrl ? await manifestFromUrl(explicit) : manifestFromFile(explicit);
|
|
1095
|
+
if (got) return { manifest: got, from: explicit };
|
|
1096
|
+
return void 0;
|
|
1097
|
+
}
|
|
1098
|
+
const remoteCfg = (options.remotes ?? {})[remoteName];
|
|
1099
|
+
if (!remoteCfg) return void 0;
|
|
1100
|
+
const cfg = typeof remoteCfg === "string" ? { external: remoteCfg } : remoteCfg;
|
|
1101
|
+
const prodBase = cfg.prod ?? cfg.external ?? "";
|
|
1102
|
+
if (!prodBase) return void 0;
|
|
1103
|
+
if (/^https?:\/\//.test(prodBase)) {
|
|
1104
|
+
const url = `${prodBase.replace(/\/$/, "")}/fulgurjs-manifest.json`;
|
|
1105
|
+
const got = await manifestFromUrl(url);
|
|
1106
|
+
if (got) return { manifest: got, from: url };
|
|
1107
|
+
return void 0;
|
|
1108
|
+
}
|
|
1109
|
+
if (opts.site) {
|
|
1110
|
+
const url = `${opts.site.replace(/\/$/, "")}${prodBase.startsWith("/") ? prodBase : `/${prodBase}`}/fulgurjs-manifest.json`;
|
|
1111
|
+
const got = await manifestFromUrl(url);
|
|
1112
|
+
if (got) return { manifest: got, from: url };
|
|
1113
|
+
}
|
|
1114
|
+
return void 0;
|
|
1115
|
+
}
|
|
1116
|
+
async function checkPagesApp(loaded, opts) {
|
|
1117
|
+
const options = loaded.options;
|
|
1118
|
+
const issues = [];
|
|
1119
|
+
const hp = loaded.hostPages;
|
|
1120
|
+
const pages = hp?.pages ?? [];
|
|
1121
|
+
const prefixes = hp?.remotePrefixes ?? {};
|
|
1122
|
+
if (pages.length === 0) {
|
|
1123
|
+
issues.push({
|
|
1124
|
+
level: "unverified",
|
|
1125
|
+
message: "fulgurjs.config.ts \u672A\u63D0\u4F9B hostPages \u5177\u540D\u5BFC\u51FA\u2014\u2014\u65E0\u4ECE\u6838\u5BF9\u9875\u9762\u5951\u7EA6\uFF08\u7EAF\u8FDC\u7A0B\u5E94\u7528\u5C5E\u6B63\u5E38\uFF0C\u65E0\u9700\u9875\u9762\u8868\uFF09\u3002\n \u4FEE\u6CD5: \u5BBF\u4E3B\u5E94\u7528\u5728 fulgurjs.config.ts \u52A0\u5177\u540D\u5BFC\u51FA hostPages = { pages, remotePrefixes, deriveSpec? }\uFF08\u4E0E\u8FD0\u884C\u65F6 createHostPages \u6D88\u8D39\u540C\u4E00\u4EFD\u6570\u636E\u6A21\u5757\uFF09"
|
|
1126
|
+
});
|
|
1127
|
+
return { app: options.name, checked: 0, issues, failed: false, unverifiedFailed: !!opts.requireVerified, manifestSources: [] };
|
|
1128
|
+
}
|
|
1129
|
+
const violations = validatePages(pages, { remotes: prefixes });
|
|
1130
|
+
for (const v of violations) {
|
|
1131
|
+
issues.push({ level: v.level === "error" ? "error" : "warn", message: v.message });
|
|
1132
|
+
}
|
|
1133
|
+
const remoteNames = [...new Set(Object.values(prefixes))];
|
|
1134
|
+
const manifests = /* @__PURE__ */ new Map();
|
|
1135
|
+
const sources = [];
|
|
1136
|
+
for (const name of remoteNames) {
|
|
1137
|
+
if (!options.remotes || !(name in options.remotes)) {
|
|
1138
|
+
issues.push({ level: "error", message: `\u8DEF\u7531\u524D\u7F00\u6620\u5C04\u5230\u8FDC\u7A0B "${name}"\uFF0C\u4F46\u672C\u5E94\u7528 federation({ remotes }) \u672A\u6D88\u8D39\u5B83\u2014\u2014\u5BBF\u4E3B\u53EA\u6838\u5BF9\u5B83\u5B9E\u9645\u6D88\u8D39\u7684\u8FDC\u7A0B` });
|
|
1139
|
+
continue;
|
|
335
1140
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
issues.push({ field: `exposes[${i}].src`, message: "dev \u5F62\u6001\u5FC5\u987B\u63D0\u4F9B\u6E90\u7801\u76F8\u5BF9\u8DEF\u5F84", got: e.src });
|
|
346
|
-
}
|
|
347
|
-
if (!validAssetRef(e.file)) {
|
|
348
|
-
issues.push({ field: `exposes[${i}].file`, message: "\u5FC5\u987B\u662F\u53EF\u8BF7\u6C42\u7684\u6A21\u5757\u5730\u5740", got: e.file });
|
|
349
|
-
}
|
|
1141
|
+
const got = await fetchRemoteManifestApp(loaded, options, name, opts);
|
|
1142
|
+
if (!got) {
|
|
1143
|
+
const how = opts.manifests?.[name] ? `--manifest ${name}=${opts.manifests[name]} \u4E0D\u53EF\u8FBE\u6216\u975E\u6709\u6548 manifest` : opts.site ? `\u6309 remotes prod \u5730\u5740\u4E0E --site ${opts.site} \u63A8\u5BFC\u7684 manifest URL \u4E0D\u53EF\u8FBE` : "\u672A\u63D0\u4F9B manifest \u6765\u6E90";
|
|
1144
|
+
issues.push({
|
|
1145
|
+
level: "unverified",
|
|
1146
|
+
message: `\u8FDC\u7A0B "${name}" \u7684 manifest \u4E0D\u53EF\u5F97\uFF08${how}\uFF09\u2014\u2014\u8BE5\u8FDC\u7A0B\u7684 spec \u5B58\u5728\u6027\u65E0\u6CD5\u6838\u5BF9\u3002
|
|
1147
|
+
\u4FEE\u6CD5: --manifest ${name}=<\u5DF2\u6784\u5EFA\u4EA7\u7269\u8DEF\u5F84\u6216 https://\u2026/fulgurjs-manifest.json>\uFF0C\u6216 --site <\u90E8\u7F72\u7AD9\u70B9>\uFF08\u6309 prod \u5730\u5740\u63A8\u5BFC\uFF09`
|
|
1148
|
+
});
|
|
1149
|
+
continue;
|
|
350
1150
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
1151
|
+
manifests.set(name, { exposes: new Set([...normalizeExposes(got.manifest).keys()].map(normSpecKey)), from: got.from });
|
|
1152
|
+
sources.push({ remote: name, from: got.from });
|
|
1153
|
+
}
|
|
1154
|
+
const derive = hp?.deriveSpec ?? DEFAULT_DERIVE;
|
|
1155
|
+
let checked = 0;
|
|
1156
|
+
for (const page of pages) {
|
|
1157
|
+
const prefixHit = Object.entries(prefixes).sort((a, b) => b[0].length - a[0].length).find(
|
|
1158
|
+
([p]) => page.route.startsWith(p.endsWith("/") ? p : `${p}/`) || `${page.route}/` === (p.endsWith("/") ? p : `${p}/`)
|
|
1159
|
+
);
|
|
1160
|
+
if (!prefixHit) {
|
|
1161
|
+
issues.push({ level: "error", message: `\u8DEF\u7531 "${page.route}" \u4E0D\u5728\u4EFB\u4F55 remotePrefixes \u524D\u7F00\u4E0B` });
|
|
1162
|
+
continue;
|
|
354
1163
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
if (v.css !== void 0) {
|
|
367
|
-
if (!Array.isArray(v.css)) {
|
|
368
|
-
issues.push({ field: `exposes["${key}"].css`, message: "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4", got: typeof v.css });
|
|
369
|
-
} else {
|
|
370
|
-
v.css.forEach((css, i) => {
|
|
371
|
-
if (!validAssetRef(css)) {
|
|
372
|
-
issues.push({ field: `exposes["${key}"].css[${i}]`, message: "\u5FC5\u987B\u662F\u8D44\u6E90\u8DEF\u5F84", got: css });
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
}
|
|
1164
|
+
checked++;
|
|
1165
|
+
const m = manifests.get(prefixHit[1]);
|
|
1166
|
+
if (!m) continue;
|
|
1167
|
+
const spec = normSpecKey(String(page.spec ?? derive(page.route)));
|
|
1168
|
+
if (!m.exposes.has(spec)) {
|
|
1169
|
+
issues.push({
|
|
1170
|
+
level: "error",
|
|
1171
|
+
message: `\u8DEF\u7531 "${page.route}" \u7684 spec "${spec}" \u4E0D\u5728\u8FDC\u7A0B "${prefixHit[1]}" \u7684 exposes \u6E05\u5355\u4E2D\uFF08\u6765\u6E90 ${m.from}\uFF09\u3002
|
|
1172
|
+
\u8FDC\u7A0B\u5B9E\u9645 exposes\uFF08\u524D 10\uFF09\uFF1A${[...m.exposes].slice(0, 10).join("\u3001") || "\uFF08\u7A7A\uFF09"}
|
|
1173
|
+
\u4FEE\u6CD5: \u6838\u5BF9\u8FDC\u7A0B federation({ exposes }) \u952E\u540D\u6216\u4FEE\u6B63\u9875\u9762\u8868 spec`
|
|
1174
|
+
});
|
|
377
1175
|
}
|
|
378
|
-
} else {
|
|
379
|
-
issues.push({ field: "exposes", message: "\u5FC5\u987B\u662F\u6570\u7EC4\uFF08dev\uFF09\u6216\u5BF9\u8C61\uFF08prod\uFF09", got: typeof exposes });
|
|
380
1176
|
}
|
|
381
|
-
|
|
382
|
-
return {
|
|
1177
|
+
const failed = issues.some((i) => i.level === "error");
|
|
1178
|
+
return { app: options.name, checked, issues, failed, unverifiedFailed: !!opts.requireVerified, manifestSources: sources };
|
|
383
1179
|
}
|
|
384
|
-
function
|
|
385
|
-
|
|
1180
|
+
async function checkPagesRepo(cfg, appPathOrName, opts) {
|
|
1181
|
+
const app = cfg.apps.find((a) => a.path === appPathOrName || a.name === appPathOrName);
|
|
1182
|
+
if (!app) {
|
|
1183
|
+
throw new Error(`[fulgurjs:check-pages] \u5E94\u7528 "${appPathOrName}" \u4E0D\u5728\u914D\u7F6E\u4E2D\uFF08\u53EF\u7528\uFF1A${cfg.apps.map((a) => a.path).join("\u3001")}\uFF09`);
|
|
1184
|
+
}
|
|
386
1185
|
const issues = [];
|
|
387
|
-
|
|
388
|
-
|
|
1186
|
+
const pages = app.host?.pages ?? [];
|
|
1187
|
+
const prefixes = app.host?.remotePrefixes ?? {};
|
|
1188
|
+
if (pages.length === 0) {
|
|
1189
|
+
issues.push({
|
|
1190
|
+
level: "unverified",
|
|
1191
|
+
message: "\u4ED3\u5E93\u914D\u7F6E\u672A\u63D0\u4F9B\u9875\u9762\u8868\uFF08pages \u53EF\u9009\uFF0C\u5E94\u7528\u4EE3\u7801\u9875\u9762\u8868\u4E3A\u8FD0\u884C\u65F6\u771F\u6E90\uFF09\u2014\u2014\u672C\u547D\u4EE4\u65E0\u4ECE\u6838\u5BF9\u3002\n \u4FEE\u6CD5: \u5728 fulgurjs.config.ts \u5BBF\u4E3B host.pages \u63D0\u4F9B\u9875\u9762\u8868\u526F\u672C\uFF0C\u6216\u4F9D\u8D56 dev \u671F remoteSchema \u63A2\u9488\u6821\u9A8C"
|
|
1192
|
+
});
|
|
1193
|
+
return { app: app.name, checked: 0, issues, failed: false, unverifiedFailed: !!opts.requireVerified, manifestSources: [] };
|
|
389
1194
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
1195
|
+
const violations = validatePages(pages, { remotes: prefixes });
|
|
1196
|
+
for (const v of violations) {
|
|
1197
|
+
issues.push({ level: v.level === "error" ? "error" : "warn", message: v.message });
|
|
1198
|
+
}
|
|
1199
|
+
const remoteNames = [...new Set(Object.values(prefixes))];
|
|
1200
|
+
const manifests = /* @__PURE__ */ new Map();
|
|
1201
|
+
const sources = [];
|
|
1202
|
+
for (const name of remoteNames) {
|
|
1203
|
+
const remoteApp = cfg.apps.find((a) => a.name === name);
|
|
1204
|
+
if (!remoteApp) {
|
|
1205
|
+
issues.push({ level: "error", message: `\u8DEF\u7531\u524D\u7F00\u6620\u5C04\u5230\u672A\u77E5\u8FDC\u7A0B "${name}"\u2014\u2014\u914D\u7F6E apps \u4E2D\u4E0D\u5B58\u5728\u8BE5\u5BB9\u5668\u540D` });
|
|
393
1206
|
continue;
|
|
394
1207
|
}
|
|
395
|
-
|
|
396
|
-
|
|
1208
|
+
const got = await fetchRemoteManifestRepo(cfg, remoteApp, opts);
|
|
1209
|
+
if (!got) {
|
|
1210
|
+
issues.push({
|
|
1211
|
+
level: "unverified",
|
|
1212
|
+
message: `\u8FDC\u7A0B "${name}" \u7684 manifest \u4E0D\u53EF\u5F97\uFF08\u672C\u5730 dist \u4E0E${opts.site ? `\u7AD9\u70B9 ${opts.site}` : "\u9ED8\u8BA4 dist \u8DEF\u5F84"}\u5747\u672A\u547D\u4E2D\uFF09\u2014\u2014\u8BE5\u8FDC\u7A0B\u7684 spec \u5B58\u5728\u6027\u65E0\u6CD5\u6838\u5BF9\u3002
|
|
1213
|
+
\u4FEE\u6CD5: \u5148\u6784\u5EFA\u8BE5\u8FDC\u7A0B\uFF08\u4EA7\u7269\u542B fulgurjs-manifest.json\uFF09\uFF0C\u6216\u7528 --site <URL> \u6307\u5411\u5DF2\u90E8\u7F72\u7AD9\u70B9`
|
|
1214
|
+
});
|
|
1215
|
+
continue;
|
|
397
1216
|
}
|
|
398
|
-
|
|
399
|
-
|
|
1217
|
+
manifests.set(name, { exposes: new Set([...normalizeExposes(got.manifest).keys()].map(normSpecKey)), from: got.from });
|
|
1218
|
+
sources.push({ remote: name, from: got.from });
|
|
1219
|
+
}
|
|
1220
|
+
const derive = app.host?.deriveSpec ?? ((r) => r.split("/").filter(Boolean).slice(1).filter((s) => !s.startsWith(":")).join("/"));
|
|
1221
|
+
let checked = 0;
|
|
1222
|
+
for (const page of pages) {
|
|
1223
|
+
const prefixHit = Object.entries(prefixes).sort((a, b) => b[0].length - a[0].length).find(
|
|
1224
|
+
([p]) => page.route.startsWith(p.endsWith("/") ? p : `${p}/`) || `${page.route}/` === (p.endsWith("/") ? p : `${p}/`)
|
|
1225
|
+
);
|
|
1226
|
+
if (!prefixHit) {
|
|
1227
|
+
issues.push({ level: "error", message: `\u8DEF\u7531 "${page.route}" \u4E0D\u5728\u4EFB\u4F55 remotePrefixes \u524D\u7F00\u4E0B` });
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
checked++;
|
|
1231
|
+
const m = manifests.get(prefixHit[1]);
|
|
1232
|
+
if (!m) continue;
|
|
1233
|
+
const spec = normSpecKey(String(page.spec ?? derive(page.route)));
|
|
1234
|
+
if (!m.exposes.has(spec)) {
|
|
1235
|
+
issues.push({
|
|
1236
|
+
level: "error",
|
|
1237
|
+
message: `\u8DEF\u7531 "${page.route}" \u7684 spec "${spec}" \u4E0D\u5728\u8FDC\u7A0B "${prefixHit[1]}" \u7684 exposes \u6E05\u5355\u4E2D\uFF08\u6765\u6E90 ${m.from}\uFF09\u3002
|
|
1238
|
+
\u8FDC\u7A0B\u5B9E\u9645 exposes\uFF08\u524D 10\uFF09\uFF1A${[...m.exposes].slice(0, 10).join("\u3001") || "\uFF08\u7A7A\uFF09"}
|
|
1239
|
+
\u4FEE\u6CD5: \u6838\u5BF9\u8FDC\u7A0B federation({ exposes }) \u952E\u540D\u6216\u4FEE\u6B63\u9875\u9762\u8868 spec`
|
|
1240
|
+
});
|
|
400
1241
|
}
|
|
401
1242
|
}
|
|
402
|
-
|
|
1243
|
+
const failed = issues.some((i) => i.level === "error");
|
|
1244
|
+
return { app: app.name, checked, issues, failed, unverifiedFailed: !!opts.requireVerified, manifestSources: sources };
|
|
1245
|
+
}
|
|
1246
|
+
async function checkPages(configPath, appPathOrName, opts = {}) {
|
|
1247
|
+
const loaded = await loadAppConfig(configPath);
|
|
1248
|
+
if (loaded.kind === "repo") {
|
|
1249
|
+
if (!appPathOrName) {
|
|
1250
|
+
throw new Error("[fulgurjs:check-pages] \u805A\u5408\u914D\u7F6E\u9700 --app <\u5E94\u7528\u76EE\u5F55\u540D\u6216\u5BB9\u5668\u540D>\uFF08\u5355\u9879\u76EE\u914D\u7F6E\u53EF\u7701\u7565\uFF09");
|
|
1251
|
+
}
|
|
1252
|
+
return checkPagesRepo(loaded.repo, appPathOrName, opts);
|
|
1253
|
+
}
|
|
1254
|
+
return checkPagesApp(loaded, opts);
|
|
403
1255
|
}
|
|
1256
|
+
function formatCheckPages(r) {
|
|
1257
|
+
const L = [];
|
|
1258
|
+
const errors = r.issues.filter((i) => i.level === "error");
|
|
1259
|
+
const unverified = r.issues.filter((i) => i.level === "unverified");
|
|
1260
|
+
const warns = r.issues.filter((i) => i.level === "warn");
|
|
1261
|
+
L.push(`[fulgurjs:check-pages] \u5E94\u7528 ${r.app}\uFF1A\u6838\u5BF9 ${r.checked} \u6761\u9875\u9762\uFF0Cerror ${errors.length} / warn ${warns.length} / \u65E0\u6CD5\u9A8C\u8BC1 ${unverified.length}`);
|
|
1262
|
+
if (r.manifestSources?.length) {
|
|
1263
|
+
L.push("manifest \u6765\u6E90\uFF08\u4F18\u5148\u7EA7\uFF1A--manifest > --site/prod \u63A8\u5BFC > \u672C\u5730 dist\uFF1B\u4EE5\u5B9E\u9645\u547D\u4E2D\u7684\u6765\u6E90\u4E3A\u51C6\uFF09\uFF1A");
|
|
1264
|
+
for (const s of r.manifestSources) L.push(` ${s.remote} \u2190 ${s.from}`);
|
|
1265
|
+
}
|
|
1266
|
+
for (const i of [...errors, ...warns, ...unverified]) L.push(`
|
|
1267
|
+
[${i.level.toUpperCase()}] ${i.message}`);
|
|
1268
|
+
if (r.issues.length === 0) L.push("\u5168\u90E8\u9875\u9762 spec \u4E0E\u8FDC\u7A0B exposes \u4E00\u81F4 \u2713");
|
|
1269
|
+
if (r.unverifiedFailed && unverified.length > 0 && errors.length === 0) {
|
|
1270
|
+
L.push(`
|
|
1271
|
+
--require-verified\uFF1A\u5B58\u5728 ${unverified.length} \u9879\u65E0\u6CD5\u9A8C\u8BC1\u2014\u2014\u6309 CI \u4E25\u683C\u6A21\u5F0F\u5224\u5B9A\u4E3A\u672A\u901A\u8FC7\uFF08\u9000\u51FA\u7801\u975E\u96F6\uFF09`);
|
|
1272
|
+
}
|
|
1273
|
+
return L.join("\n");
|
|
1274
|
+
}
|
|
1275
|
+
var DEFAULT_DERIVE, normSpecKey;
|
|
1276
|
+
var init_commands = __esm({
|
|
1277
|
+
"src/commands.ts"() {
|
|
1278
|
+
"use strict";
|
|
1279
|
+
init_app_config();
|
|
1280
|
+
init_config();
|
|
1281
|
+
init_pages();
|
|
1282
|
+
init_manifest();
|
|
1283
|
+
DEFAULT_DERIVE = (route) => route.split("/").filter(Boolean).slice(1).filter((s) => !s.startsWith(":")).join("/");
|
|
1284
|
+
normSpecKey = (s) => s.replace(/^[./]+/, "").replace(/\/+$/, "");
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
// src/cli.ts
|
|
1289
|
+
import { resolve } from "node:path";
|
|
404
1290
|
|
|
405
1291
|
// src/doctor.ts
|
|
1292
|
+
init_manifest();
|
|
1293
|
+
import http from "node:http";
|
|
1294
|
+
import https from "node:https";
|
|
406
1295
|
function fetchHeadOrGet(url, method = "GET") {
|
|
407
1296
|
return new Promise((resolve2, reject) => {
|
|
408
1297
|
const mod = url.startsWith("https:") ? https : http;
|
|
@@ -709,18 +1598,31 @@ function formatDoctorReport(checks) {
|
|
|
709
1598
|
// src/cli.ts
|
|
710
1599
|
var HELP = `fulgurjs \u2014 Vite Module Federation CLI (@fulgurjs/federation)
|
|
711
1600
|
|
|
712
|
-
\u7528\u6CD5\uFF1A
|
|
713
|
-
fulgurjs init [--template <path>] [--force] \u751F\u6210\
|
|
714
|
-
|
|
715
|
-
|
|
1601
|
+
\u7528\u6CD5\uFF08\u5355\u9879\u76EE fulgurjs.config.ts \u5728\u5E94\u7528\u6839\u76EE\u5F55\uFF1B\u547D\u4EE4\u9ED8\u8BA4\u8BFB ./fulgurjs.config.ts\uFF09\uFF1A
|
|
1602
|
+
fulgurjs init [--template <path>] [--force] \u751F\u6210\u5355\u9879\u76EE fulgurjs.config.ts \u8D77\u6B65\u6A21\u677F
|
|
1603
|
+
\uFF08\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\uFF09
|
|
1604
|
+
fulgurjs init --config <path> \u6821\u9A8C\u914D\u7F6E\uFF1B\u8F93\u51FA federation(fulgurjsConfig) \u63A5\u5165\u5757
|
|
1605
|
+
\u4E0E\u63A5\u5165\u6838\u5BF9\u6E05\u5355\uFF08NGINX \u6837\u677F\u4EC5\u968F\u65E7\u805A\u5408\u914D\u7F6E\u6253\u5370\uFF09
|
|
1606
|
+
fulgurjs explain [--config <path>] [--app <name>] [--json]
|
|
1607
|
+
\u89E3\u91CA\u672C\u5E94\u7528\u6709\u6548\u8054\u90A6\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE\uFF08\u7EAF\u672C\u5730\uFF0C\u65E0\u7F51\u7EDC\uFF1B
|
|
1608
|
+
\u5355\u9879\u76EE\u5F62\u6001\u514D --app\uFF0C\u65E7\u805A\u5408\u914D\u7F6E\u5FC5\u586B\uFF09
|
|
1609
|
+
fulgurjs check-pages [--config <path>] [--app <name>] [--site <URL>]
|
|
1610
|
+
[--manifest <remote>=<\u8DEF\u5F84|URL>]... [--require-verified] [--json]
|
|
1611
|
+
\u6838\u5BF9\u5BBF\u4E3B\u9875\u9762\u8868\u4E0E\u8FDC\u7A0B exposes\uFF08\u5BBF\u4E3B\u9879\u76EE\u8FD0\u884C\uFF1B
|
|
1612
|
+
manifest \u6765\u6E90\u4F18\u5148\u7EA7 --manifest > --site/prod \u63A8\u5BFC\uFF1B
|
|
1613
|
+
\u786E\u5B9A\u6027\u9519\u8BEF\u975E\u96F6\u9000\u51FA\uFF1B--require-verified \u65F6\u65E0\u6CD5\u9A8C\u8BC1\u4E5F\u975E\u96F6\uFF09
|
|
716
1614
|
fulgurjs doctor --base <URL> --apps <a,b,c> [--dev] [--json] [--chunk-sample N]
|
|
717
1615
|
fulgurjs --help
|
|
718
1616
|
|
|
719
|
-
\u793A\u4F8B\uFF1A
|
|
1617
|
+
\u793A\u4F8B\uFF08\u5355\u9879\u76EE\u5F62\u6001\uFF0C\u5E94\u7528\u6839\u76EE\u5F55\u5185\u8FD0\u884C\uFF09\uFF1A
|
|
720
1618
|
fulgurjs init # \u5F53\u524D\u76EE\u5F55\u5199 fulgurjs.config.ts\uFF08\u5DF2\u5B58\u5728\u5219\u62D2\u7EDD\uFF0C--force \u8986\u76D6\uFF09
|
|
721
|
-
fulgurjs
|
|
722
|
-
fulgurjs
|
|
723
|
-
fulgurjs
|
|
1619
|
+
fulgurjs explain # \u89E3\u91CA\u672C\u5E94\u7528\uFF08--config \u6307\u5411\u5176\u4ED6\u8DEF\u5F84\u65F6\u663E\u5F0F\u4F20\uFF09
|
|
1620
|
+
fulgurjs check-pages --site http://your-site # \u6309 remotes prod \u5730\u5740\u63A8\u5BFC\u8FDC\u7A0B manifest \u6838\u5BF9
|
|
1621
|
+
fulgurjs check-pages --manifest remote-a=https://cdn.example.com/remote-a/fulgurjs-manifest.json
|
|
1622
|
+
fulgurjs doctor --base http://your-site --apps my-app,remote-a
|
|
1623
|
+
|
|
1624
|
+
\u65E7\u805A\u5408\u914D\u7F6E\uFF08root + apps[]\uFF0C\u517C\u5BB9\u671F\uFF09\uFF1A\u6240\u6709\u5B50\u547D\u4EE4\u52A0 --config \u6307\u5411\u805A\u5408\u6587\u4EF6\uFF0C
|
|
1625
|
+
explain/check-pages \u9700 --app <\u5E94\u7528\u76EE\u5F55\u540D\u6216\u5BB9\u5668\u540D>\u3002
|
|
724
1626
|
`;
|
|
725
1627
|
async function main() {
|
|
726
1628
|
const argv = process.argv.slice(2);
|
|
@@ -734,6 +1636,17 @@ async function main() {
|
|
|
734
1636
|
return i > -1 ? argv[i + 1] : void 0;
|
|
735
1637
|
};
|
|
736
1638
|
const has = (k) => argv.includes(k);
|
|
1639
|
+
const kvAllOf = (k) => {
|
|
1640
|
+
const out = {};
|
|
1641
|
+
argv.forEach((a, i) => {
|
|
1642
|
+
if (a !== k) return;
|
|
1643
|
+
const v = argv[i + 1];
|
|
1644
|
+
const eq = v?.indexOf("=");
|
|
1645
|
+
if (!v || !eq || eq < 1) return;
|
|
1646
|
+
out[v.slice(0, eq)] = v.slice(eq + 1);
|
|
1647
|
+
});
|
|
1648
|
+
return out;
|
|
1649
|
+
};
|
|
737
1650
|
if (cmd === "init") {
|
|
738
1651
|
const { writeConfigTemplate: writeConfigTemplate2, inspectConfig: inspectConfig2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
739
1652
|
const configPath = argOf("--config");
|
|
@@ -752,14 +1665,42 @@ async function main() {
|
|
|
752
1665
|
console.error(`[fulgurjs:init] ${target} \u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u8986\u76D6\uFF08--force \u5F3A\u5236\u8986\u76D6\uFF09`);
|
|
753
1666
|
return 2;
|
|
754
1667
|
}
|
|
755
|
-
console.log(`[fulgurjs:init] \u5DF2\u751F\u6210\u8D77\u6B65\u6A21\u677F ${target}
|
|
1668
|
+
console.log(`[fulgurjs:init] \u5DF2\u751F\u6210\u5355\u9879\u76EE\u8D77\u6B65\u6A21\u677F ${target}
|
|
756
1669
|
\u540E\u7EED\u6B65\u9AA4\uFF1A
|
|
757
|
-
1. \u7F16\u8F91 fulgurjs.config.ts\uFF1A\u586B\u5165\
|
|
758
|
-
2.
|
|
759
|
-
|
|
760
|
-
|
|
1670
|
+
1. \u7F16\u8F91 fulgurjs.config.ts\uFF1A\u586B\u5165\u5BB9\u5668\u540D/exposes/remotes/shared\uFF08\u9ED8\u8BA4\u5BFC\u51FA\u76F4\u63A5\u662F federation() \u9009\u9879\uFF09
|
|
1671
|
+
2. vite.config.ts \u63A5\u5165\uFF08\u4EC5\u4E24\u884C\u8054\u90A6\u76F8\u5173\u4EE3\u7801\uFF09\uFF1A
|
|
1672
|
+
import federation from '@fulgurjs/federation'
|
|
1673
|
+
import fulgurjsConfig from './fulgurjs.config'
|
|
1674
|
+
// plugins: [ ...\u539F\u6709\u63D2\u4EF6, federation(fulgurjsConfig) ]
|
|
1675
|
+
3. npx fulgurjs explain # \u6838\u5BF9\u6709\u6548\u5F62\u6001\u4E0E\u52A0\u8F7D\u94FE
|
|
1676
|
+
4. \u5BBF\u4E3B\u5E94\u7528\u53E6\u5728 fulgurjs.config.ts \u5177\u540D\u5BFC\u51FA hostPages\uFF08\u4E0E\u8FD0\u884C\u65F6\u9875\u9762\u6570\u636E\u6A21\u5757\u540C\u6E90\uFF09\uFF0C
|
|
1677
|
+
\u8FD0\u884C npx fulgurjs check-pages --site <\u7AD9\u70B9> \u6838\u5BF9\u9875\u9762\u5951\u7EA6
|
|
1678
|
+
5. \u90E8\u7F72\u540E\uFF1Anpx fulgurjs doctor --base <URL> --apps <\u5BB9\u5668\u540D>`);
|
|
761
1679
|
return 0;
|
|
762
1680
|
}
|
|
1681
|
+
if (cmd === "explain" || cmd === "check-pages") {
|
|
1682
|
+
const app = argOf("--app");
|
|
1683
|
+
try {
|
|
1684
|
+
if (cmd === "explain") {
|
|
1685
|
+
const { explainApp: explainApp2, formatExplain: formatExplain2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
1686
|
+
const r2 = await explainApp2(resolve(argOf("--config") ?? "fulgurjs.config.ts"), app);
|
|
1687
|
+
console.log(has("--json") ? JSON.stringify(r2, null, 2) : formatExplain2(r2));
|
|
1688
|
+
return 0;
|
|
1689
|
+
}
|
|
1690
|
+
const { checkPages: checkPages2, formatCheckPages: formatCheckPages2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
1691
|
+
const opts = {
|
|
1692
|
+
...argOf("--site") ? { site: argOf("--site") } : {},
|
|
1693
|
+
...Object.keys(kvAllOf("--manifest")).length ? { manifests: kvAllOf("--manifest") } : {},
|
|
1694
|
+
...has("--require-verified") ? { requireVerified: true } : {}
|
|
1695
|
+
};
|
|
1696
|
+
const r = await checkPages2(resolve(argOf("--config") ?? "fulgurjs.config.ts"), app, opts);
|
|
1697
|
+
console.log(has("--json") ? JSON.stringify(r, null, 2) : formatCheckPages2(r));
|
|
1698
|
+
return r.failed || r.unverifiedFailed ? 1 : 0;
|
|
1699
|
+
} catch (e) {
|
|
1700
|
+
console.error(String(e.message ?? e));
|
|
1701
|
+
return 2;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
763
1704
|
if (cmd === "doctor") {
|
|
764
1705
|
const base = argOf("--base");
|
|
765
1706
|
const appsRaw = argOf("--apps") ?? "main";
|