@fulgurjs/federation 0.4.2
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 +56 -0
- package/DESIGN.md +190 -0
- package/LICENSE +21 -0
- package/README.md +513 -0
- package/dist/cli.js +650 -0
- package/dist/config.cjs +114 -0
- package/dist/config.d.cts +79 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +77 -0
- package/dist/index.cjs +1698 -0
- package/dist/index.d.cts +82 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +1663 -0
- package/dist/pages.cjs +169 -0
- package/dist/pages.d.cts +66 -0
- package/dist/pages.d.ts +66 -0
- package/dist/pages.js +143 -0
- package/dist/runtime.js +3 -0
- package/docs/P5-vite7-8/345/205/274/345/256/271/347/237/251/351/230/265.md +36 -0
- package/docs/manual.html +467 -0
- package/docs/vite-upstream-issue-irregexp.md +91 -0
- package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +37 -0
- package/docs//346/262/231/347/256/261/350/276/271/347/225/214/345/256/241/350/256/241.md +45 -0
- package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +80 -0
- package/examples/fulgur.config.example.ts +41 -0
- package/package.json +90 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1698 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
default: () => index_default,
|
|
34
|
+
federation: () => federation
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
38
|
+
var import_node_module2 = require("module");
|
|
39
|
+
|
|
40
|
+
// src/options.ts
|
|
41
|
+
var import_node_module = require("module");
|
|
42
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
43
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
44
|
+
|
|
45
|
+
// src/version.ts
|
|
46
|
+
var RUNTIME_VERSION = "0.4.2";
|
|
47
|
+
|
|
48
|
+
// src/options.ts
|
|
49
|
+
var DEFAULT_FILENAME = "fulgur-remoteEntry.js";
|
|
50
|
+
var RUNTIME_VIRTUAL_ID = "virtual:fulgur-runtime";
|
|
51
|
+
var RUNTIME_PROXY_VIRTUAL_ID = "virtual:fulgur-runtime-proxy";
|
|
52
|
+
var INIT_VIRTUAL_ID = "virtual:fulgur-init";
|
|
53
|
+
var SHARED_FACADE_PREFIX = "virtual:fulgur-shared:";
|
|
54
|
+
var SHARED_NS_FACADE_PREFIX = "virtual:fulgur-shared-ns:";
|
|
55
|
+
var RESOLVED = {
|
|
56
|
+
runtime: "virtual:fulgur-runtime",
|
|
57
|
+
runtimeProxy: "virtual:fulgur-runtime-proxy",
|
|
58
|
+
init: "virtual:fulgur-init",
|
|
59
|
+
provides: "virtual:fulgur-provides",
|
|
60
|
+
remoteEntry: "virtual:fulgur-remote-entry",
|
|
61
|
+
sharedFacade: (name) => `virtual:fulgur-shared:${name}`,
|
|
62
|
+
sharedNsFacade: (name) => `virtual:fulgur-shared-ns:${name}`,
|
|
63
|
+
/** CJS require(<shared>) 垫片:与 sharedNsFacade 同体,仅 id 形态不同(保持 require 调用语义) */
|
|
64
|
+
cjsNsFacade: (name) => `virtual:fulgur-cjs-ns:${name}`
|
|
65
|
+
};
|
|
66
|
+
function joinUrl(base, file) {
|
|
67
|
+
const b = base.endsWith("/") ? base : `${base}/`;
|
|
68
|
+
return `${b}${file.replace(/^\//, "")}`;
|
|
69
|
+
}
|
|
70
|
+
function normalizeRemoteValue(key, value, filename, warnings) {
|
|
71
|
+
if (typeof value === "function") {
|
|
72
|
+
warnings.push(
|
|
73
|
+
`remotes["${key}"] is a function (promise-based remote): it cannot be serialized into the bundle; register it at runtime via registerRemote() (same semantics as webpack "promise new Promise"). The key stays in config for import-syntax rewriting.`
|
|
74
|
+
);
|
|
75
|
+
return {
|
|
76
|
+
key,
|
|
77
|
+
name: key,
|
|
78
|
+
shareScope: "default",
|
|
79
|
+
devEntry: "",
|
|
80
|
+
prodEntry: "",
|
|
81
|
+
promise: true
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const cfg = typeof value === "string" ? { external: value } : value;
|
|
85
|
+
let raw = cfg.external ?? "";
|
|
86
|
+
let selfName = key;
|
|
87
|
+
if (raw) {
|
|
88
|
+
const at = raw.indexOf("@");
|
|
89
|
+
if (at > 0 && at < raw.length - 1 && !/^https?:$/.test(raw.slice(0, at))) {
|
|
90
|
+
selfName = raw.slice(0, at);
|
|
91
|
+
raw = raw.slice(at + 1);
|
|
92
|
+
} else if (at === 0 || at === raw.length - 1) {
|
|
93
|
+
throw new Error(`Invalid remote request "${raw}" for "${key}": misplaced "@".`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const resolveEntry = (rawUrl, mode) => {
|
|
97
|
+
if (!rawUrl) return "";
|
|
98
|
+
if (/\.js(\?.*)?$/.test(rawUrl)) {
|
|
99
|
+
if (mode === "prod") return rawUrl;
|
|
100
|
+
return rawUrl;
|
|
101
|
+
}
|
|
102
|
+
return joinUrl(rawUrl, mode === "dev" ? "@fulgur-entry.js" : filename);
|
|
103
|
+
};
|
|
104
|
+
const explicitDev = cfg.dev;
|
|
105
|
+
const explicitProd = cfg.prod;
|
|
106
|
+
return {
|
|
107
|
+
key,
|
|
108
|
+
name: selfName,
|
|
109
|
+
shareScope: cfg.shareScope || "default",
|
|
110
|
+
devEntry: resolveEntry(explicitDev || raw, "dev"),
|
|
111
|
+
prodEntry: resolveEntry(explicitProd || raw, "prod"),
|
|
112
|
+
timeout: cfg.timeout,
|
|
113
|
+
retries: cfg.retries,
|
|
114
|
+
fallback: cfg.fallback,
|
|
115
|
+
breaker: cfg.breaker
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function readPkgDependencies(root) {
|
|
119
|
+
try {
|
|
120
|
+
const pkgPath = import_node_path.default.join(root, "package.json");
|
|
121
|
+
const pkg = JSON.parse(import_node_fs.default.readFileSync(pkgPath, "utf8"));
|
|
122
|
+
return {
|
|
123
|
+
...pkg.dependencies ?? {},
|
|
124
|
+
...pkg.optionalDependencies ?? {},
|
|
125
|
+
...pkg.peerDependencies ?? {},
|
|
126
|
+
...pkg.devDependencies ?? {}
|
|
127
|
+
};
|
|
128
|
+
} catch {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function readInstalledVersion(root, specifier) {
|
|
133
|
+
try {
|
|
134
|
+
const req = (0, import_node_module.createRequire)(import_node_path.default.join(root, "package.json"));
|
|
135
|
+
const pkgJsonPath = req.resolve(`${specifier}/package.json`, { paths: [root] });
|
|
136
|
+
const pkg = JSON.parse(import_node_fs.default.readFileSync(pkgJsonPath, "utf8"));
|
|
137
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function normalizeShared(shared, shareScopeDefault, pkgDependencies, root, warnings) {
|
|
143
|
+
if (!shared) return [];
|
|
144
|
+
const entries = [];
|
|
145
|
+
if (Array.isArray(shared)) {
|
|
146
|
+
for (const item of shared) entries.push([item, {}]);
|
|
147
|
+
} else {
|
|
148
|
+
for (const [key, val] of Object.entries(shared)) {
|
|
149
|
+
entries.push([key, typeof val === "string" ? { requiredVersion: val } : val ?? {}]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const out = [];
|
|
153
|
+
for (const [configKey, hint] of entries) {
|
|
154
|
+
if (hint.eager && hint.import === false) {
|
|
155
|
+
configError(
|
|
156
|
+
`CFG-008: shared["${configKey}"] combines eager with import:false`,
|
|
157
|
+
hint,
|
|
158
|
+
"eager requires a local copy to bundle into the initial chunk",
|
|
159
|
+
`shared: { '${configKey}': { eager: true } } // or drop eager: { '${configKey}': { import: false } }`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const canonical = configKey.endsWith("/") ? configKey.slice(0, -1) : configKey;
|
|
163
|
+
const importSpec = hint.import === void 0 ? canonical : hint.import;
|
|
164
|
+
const lookupName = hint.packageName || canonical;
|
|
165
|
+
const shareScopeOfHint = hint.shareScope || shareScopeDefault;
|
|
166
|
+
const shareKeyOfHint = hint.shareKey || canonical;
|
|
167
|
+
const dup = out.find((s) => s.shareKey === shareKeyOfHint && s.shareScope === shareScopeOfHint);
|
|
168
|
+
if (dup) {
|
|
169
|
+
configError(
|
|
170
|
+
`CFG-008: shared key "${shareKeyOfHint}" is declared twice in share scope "${shareScopeOfHint}" (keys "${dup.configKey}" and "${configKey}")`,
|
|
171
|
+
configKey,
|
|
172
|
+
"one declaration per shareKey per share scope",
|
|
173
|
+
`merge hints: { '${shareKeyOfHint}': { singleton: true } } // or use distinct shareKey values`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
let requiredVersion;
|
|
177
|
+
if (hint.requiredVersion !== void 0) {
|
|
178
|
+
requiredVersion = hint.requiredVersion;
|
|
179
|
+
} else if (typeof hint.import === "string" && hint.import !== canonical) {
|
|
180
|
+
requiredVersion = false;
|
|
181
|
+
} else {
|
|
182
|
+
const depRange = pkgDependencies[lookupName];
|
|
183
|
+
if (depRange) {
|
|
184
|
+
requiredVersion = depRange;
|
|
185
|
+
} else {
|
|
186
|
+
requiredVersion = false;
|
|
187
|
+
warnings.push(
|
|
188
|
+
`shared["${configKey}"]: requiredVersion could not be inferred from package.json; accepting any version.`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const singleton = hint.singleton ?? false;
|
|
193
|
+
const strictVersion = hint.strictVersion ?? (importSpec !== false && !singleton);
|
|
194
|
+
let version = hint.version ?? "";
|
|
195
|
+
if (!version) {
|
|
196
|
+
const installed = readInstalledVersion(root, importSpec === false ? canonical : importSpec);
|
|
197
|
+
if (installed) {
|
|
198
|
+
version = installed;
|
|
199
|
+
} else {
|
|
200
|
+
version = "0.0.0";
|
|
201
|
+
warnings.push(
|
|
202
|
+
`shared["${configKey}"]: installed version not found; registering as 0.0.0 (it will still be consumable but ranked lowest).`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const extraAliases = canonical === "vue" || hint.shareKey === "vue" || hint.packageName === "vue" ? ["vue-demi"] : [];
|
|
207
|
+
out.push({
|
|
208
|
+
configKey,
|
|
209
|
+
shareKey: hint.shareKey || canonical,
|
|
210
|
+
import: importSpec,
|
|
211
|
+
requiredVersion,
|
|
212
|
+
singleton,
|
|
213
|
+
strictVersion,
|
|
214
|
+
shareScope: hint.shareScope || shareScopeDefault,
|
|
215
|
+
eager: hint.eager ?? false,
|
|
216
|
+
version,
|
|
217
|
+
aliases: [
|
|
218
|
+
.../* @__PURE__ */ new Set([
|
|
219
|
+
canonical,
|
|
220
|
+
...typeof importSpec === "string" ? [importSpec] : [],
|
|
221
|
+
...hint.packageName ? [hint.packageName] : [],
|
|
222
|
+
...extraAliases
|
|
223
|
+
])
|
|
224
|
+
]
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
function configError(what, got, expect, example) {
|
|
230
|
+
const gotText = typeof got === "string" ? `"${got}"` : JSON.stringify(got);
|
|
231
|
+
throw new Error(
|
|
232
|
+
[
|
|
233
|
+
`[fulgur] Invalid federation() config \u2014 ${what}`,
|
|
234
|
+
` got: ${gotText}`,
|
|
235
|
+
` expected: ${expect}`,
|
|
236
|
+
` example: ${example}`
|
|
237
|
+
].join("\n")
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function validateOptions(options) {
|
|
241
|
+
if (options.name === void 0 || options.name === null || options.name === "") {
|
|
242
|
+
configError(
|
|
243
|
+
"`name` is required (container name, also used as uniqueName)",
|
|
244
|
+
options.name,
|
|
245
|
+
"a non-empty string unique among host/remotes in the same page",
|
|
246
|
+
`federation({ name: 'my-app', ... })`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (typeof options.name !== "string" || !/^[a-zA-Z][\w.-]*$/.test(options.name)) {
|
|
250
|
+
configError(
|
|
251
|
+
"`name` must match /^[a-zA-Z][\\w.-]*$/ (letters first, no spaces/slashes)",
|
|
252
|
+
options.name,
|
|
253
|
+
'e.g. "my-app", "host-app"',
|
|
254
|
+
`federation({ name: 'my-app', ... })`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
if (options.exposes !== void 0 && typeof options.exposes !== "object") {
|
|
258
|
+
configError("`exposes` must be an object", options.exposes, 'an object of { "./Module": "./src/path" }', `exposes: { './Button': './src/Button.vue' }`);
|
|
259
|
+
}
|
|
260
|
+
if (options.remotes !== void 0 && typeof options.remotes !== "object") {
|
|
261
|
+
configError("`remotes` must be an object", options.remotes, "an object of { name: url | { dev, prod } | () => Promise<container> }", `remotes: { 'remote-a': 'http://localhost:5101' }`);
|
|
262
|
+
}
|
|
263
|
+
if (options.shared !== void 0 && typeof options.shared !== "object") {
|
|
264
|
+
configError("`shared` must be an array or an object", options.shared, '["vue"] or { vue: { singleton: true } }', `shared: { vue: { singleton: true } }`);
|
|
265
|
+
}
|
|
266
|
+
for (const [key, val] of Object.entries(options.exposes ?? {})) {
|
|
267
|
+
const importPath = typeof val === "string" ? val : val?.import;
|
|
268
|
+
if (!importPath || typeof importPath !== "string") {
|
|
269
|
+
configError(
|
|
270
|
+
`exposes["${key}"].import is missing`,
|
|
271
|
+
val,
|
|
272
|
+
'a source path string, or { import: "./src/path" }',
|
|
273
|
+
`exposes: { '${key.startsWith("./") ? key : "./" + key}': './src/views/Home.vue' }`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for (const [key, val] of Object.entries(options.remotes ?? {})) {
|
|
278
|
+
if (/[/@\s]/.test(key)) {
|
|
279
|
+
configError(
|
|
280
|
+
`remotes key "${key}" contains invalid characters (@, / or whitespace)`,
|
|
281
|
+
key,
|
|
282
|
+
'a bare module-style name used as import prefix, e.g. "remote-a"',
|
|
283
|
+
`remotes: { 'remote-a': '...' } // then: import X from 'remote-a/Button'`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (typeof val === "function") continue;
|
|
287
|
+
const cfg = typeof val === "string" ? { external: val } : val;
|
|
288
|
+
if (typeof val === "string" && val.trim() === "") {
|
|
289
|
+
configError(`remotes["${key}"] is an empty string`, val, "a remote base URL or full entry URL", `remotes: { '${key}': 'http://localhost:5101' }`);
|
|
290
|
+
}
|
|
291
|
+
if (typeof val !== "string" && typeof cfg !== "object") {
|
|
292
|
+
configError(`remotes["${key}"] has unsupported type`, val, "string | { dev?, prod?, external? } | () => Promise<container>", `remotes: { '${key}': { dev: 'http://localhost:5101', prod: '/remote-a' } }`);
|
|
293
|
+
}
|
|
294
|
+
if (!cfg?.external && !cfg?.dev && !cfg?.prod) {
|
|
295
|
+
configError(
|
|
296
|
+
`remotes["${key}"] has no address (need one of external / dev / prod)`,
|
|
297
|
+
val,
|
|
298
|
+
"at least one address; with only one URL it is used for both dev and prod",
|
|
299
|
+
`remotes: { '${key}': 'http://localhost:5101' }
|
|
300
|
+
// or split: { '${key}': { dev: 'http://localhost:5101', prod: '/${key}' } }`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
for (const slot of ["dev", "prod", "external"]) {
|
|
304
|
+
const v = cfg[slot];
|
|
305
|
+
if (v !== void 0 && typeof v !== "string") {
|
|
306
|
+
configError(`remotes["${key}"].${slot} must be a string`, v, "a URL string", `remotes: { '${key}': { ${slot}: 'http://localhost:5101' } }`);
|
|
307
|
+
}
|
|
308
|
+
if (slot !== "external") {
|
|
309
|
+
const slotUrl = cfg[slot];
|
|
310
|
+
if (typeof slotUrl === "string" && /^[A-Za-z][\w.-]*@/.test(slotUrl)) {
|
|
311
|
+
configError(
|
|
312
|
+
`CFG-007: remotes["${key}"].${slot} uses the "name@url" prefix, which the object form does not support (it is concatenated verbatim into a broken URL)`,
|
|
313
|
+
slotUrl,
|
|
314
|
+
"a bare URL (the remote self-name defaults to the key), or the plain string form if renaming is needed",
|
|
315
|
+
`remotes: { '${key}': 'http://localhost:5101' } // or: remotes: { '${key}': { dev: 'http://localhost:5101', prod: '/${key}' } }`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function normalizeOptions(options, root, command) {
|
|
323
|
+
const warnings = [];
|
|
324
|
+
validateOptions(options);
|
|
325
|
+
if (!options.name) throw new Error("[fulgur] option `name` is required.");
|
|
326
|
+
if (options.remoteType && options.remoteType !== "module") {
|
|
327
|
+
warnings.push(
|
|
328
|
+
`remoteType "${options.remoteType}" requires webpack-host interop (P3 milestone); falling back to "module".`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
if (options.library?.type && options.library.type !== "module" && options.library.type !== "esm") {
|
|
332
|
+
warnings.push(
|
|
333
|
+
`library.type "${options.library.type}" requires webpack-host interop (P3 milestone); falling back to "module".`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
if (options.automaticAsyncBoundary === false) {
|
|
337
|
+
warnings.push(
|
|
338
|
+
"automaticAsyncBoundary=false has no effect: fulgur uses TLA-based automatic async boundaries (better than webpack manual bootstrap)."
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
const exposes = [];
|
|
342
|
+
for (const [rawName, val] of Object.entries(options.exposes ?? {})) {
|
|
343
|
+
const name = rawName.startsWith("./") ? rawName : `./${rawName}`;
|
|
344
|
+
if (rawName !== name) warnings.push(`exposes key "${rawName}" normalized to "${name}".`);
|
|
345
|
+
const hint = typeof val === "string" ? { import: val } : val;
|
|
346
|
+
exposes.push({ name, import: hint.import, chunkName: hint.name });
|
|
347
|
+
}
|
|
348
|
+
const shareScopeDefault = options.shareScope || "default";
|
|
349
|
+
const filename = options.filename || DEFAULT_FILENAME;
|
|
350
|
+
const pkgDependencies = readPkgDependencies(root);
|
|
351
|
+
const shared = normalizeShared(options.shared, shareScopeDefault, pkgDependencies, root, warnings);
|
|
352
|
+
const remotes = [];
|
|
353
|
+
for (const [key, value] of Object.entries(options.remotes ?? {})) {
|
|
354
|
+
remotes.push(normalizeRemoteValue(key, value, filename, warnings));
|
|
355
|
+
}
|
|
356
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
357
|
+
for (const r of remotes) {
|
|
358
|
+
if (seenNames.has(r.name) && seenNames.get(r.name) !== r.key) {
|
|
359
|
+
warnings.push(`remote name "${r.name}" is declared for multiple keys; names must be unique.`);
|
|
360
|
+
}
|
|
361
|
+
seenNames.set(r.name, r.key);
|
|
362
|
+
}
|
|
363
|
+
if ((options.exposes === void 0 || Object.keys(options.exposes).length === 0) && (options.remotes === void 0 || Object.keys(options.remotes).length === 0)) {
|
|
364
|
+
warnings.push(
|
|
365
|
+
'federation() has neither `exposes` nor `remotes` \u2014 it only sets up shared modules. If you intended a remote, add `exposes: { "./Module": "./src/path" }`; if a host, add `remotes: { ... }`.'
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
if (Object.keys(options.exposes ?? {}).length === 0 && Object.keys(options.remotes ?? {}).length > 0) {
|
|
369
|
+
warnings.push(
|
|
370
|
+
`federation({ name: "${options.name}" }) consumes remotes but exposes nothing \u2014 this app is a pure host. Files will not be available to other apps; add \`exposes\` if that is unintended.`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
name: options.name,
|
|
375
|
+
uniqueName: options.name,
|
|
376
|
+
filename,
|
|
377
|
+
exposes,
|
|
378
|
+
remotes,
|
|
379
|
+
shared,
|
|
380
|
+
shareScope: shareScopeDefault,
|
|
381
|
+
remoteType: "module",
|
|
382
|
+
runtime: options.runtime,
|
|
383
|
+
runtimeChunk: options.runtimeChunk,
|
|
384
|
+
manifest: options.manifest === void 0 ? true : !!options.manifest,
|
|
385
|
+
runtimePlugins: options.runtimePlugins ?? [],
|
|
386
|
+
dts: options.dts === void 0 ? true : options.dts,
|
|
387
|
+
root,
|
|
388
|
+
pluginVersion: RUNTIME_VERSION,
|
|
389
|
+
pkgDependencies,
|
|
390
|
+
warnings,
|
|
391
|
+
// dev 改写开关:默认纯 remote 参与 shared 协商,宿主(有 remotes)不参与;可显式覆盖
|
|
392
|
+
devSharedSelf: options.devSharedSelf ?? remotes.length === 0
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/transform.ts
|
|
397
|
+
var import_es_module_lexer = require("es-module-lexer");
|
|
398
|
+
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
399
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
400
|
+
var lexerReady = null;
|
|
401
|
+
function ensureLexer() {
|
|
402
|
+
lexerReady ??= import_es_module_lexer.init;
|
|
403
|
+
return lexerReady;
|
|
404
|
+
}
|
|
405
|
+
function isExposeTargetFile(cleanPath, root, exposes) {
|
|
406
|
+
for (const e of exposes) {
|
|
407
|
+
const abs = import_node_path2.default.resolve(root, e.import.replace(/^\.\//, ""));
|
|
408
|
+
if (cleanPath === abs) return true;
|
|
409
|
+
for (const ext of [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".vue"]) {
|
|
410
|
+
if (cleanPath === abs + ext) return true;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
function buildSharedMatcher(shared) {
|
|
416
|
+
const exact = /* @__PURE__ */ new Map();
|
|
417
|
+
const prefixes = [];
|
|
418
|
+
for (const s of shared) {
|
|
419
|
+
for (const alias of s.aliases) {
|
|
420
|
+
if (!exact.has(alias)) exact.set(alias, s);
|
|
421
|
+
}
|
|
422
|
+
if (s.configKey.endsWith("/")) prefixes.push({ prefix: s.configKey, item: s });
|
|
423
|
+
}
|
|
424
|
+
return { exact, prefixes };
|
|
425
|
+
}
|
|
426
|
+
function matchShared(spec, matcher) {
|
|
427
|
+
const exact = matcher.exact.get(spec);
|
|
428
|
+
if (exact) return exact;
|
|
429
|
+
for (const p of matcher.prefixes) {
|
|
430
|
+
if (spec.startsWith(p.prefix)) return p.item;
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
function serializeShareCallForFacade(item, fallbackUrl) {
|
|
435
|
+
const opts = [];
|
|
436
|
+
opts.push(`shareScope: ${JSON.stringify(item.shareScope)}`);
|
|
437
|
+
opts.push(`shareKey: ${JSON.stringify(item.shareKey)}`);
|
|
438
|
+
if (item.requiredVersion !== false) opts.push(`requiredVersion: ${JSON.stringify(item.requiredVersion)}`);
|
|
439
|
+
if (item.singleton) opts.push("singleton: true");
|
|
440
|
+
if (item.strictVersion) opts.push("strictVersion: true");
|
|
441
|
+
if (item.import !== false) {
|
|
442
|
+
const facadeUrl = fallbackUrl ?? SHARED_FACADE_PREFIX + item.shareKey;
|
|
443
|
+
opts.push(`fallback: () => import(${JSON.stringify(facadeUrl)})`);
|
|
444
|
+
}
|
|
445
|
+
return `__fulgur_loadShare(${JSON.stringify(item.shareKey)}, { ${opts.join(", ")} })`;
|
|
446
|
+
}
|
|
447
|
+
function serializeShareCall(item, devUrls) {
|
|
448
|
+
const opts = [];
|
|
449
|
+
opts.push(`shareScope: ${JSON.stringify(item.shareScope)}`);
|
|
450
|
+
opts.push(`shareKey: ${JSON.stringify(item.shareKey)}`);
|
|
451
|
+
if (item.requiredVersion !== false) opts.push(`requiredVersion: ${JSON.stringify(item.requiredVersion)}`);
|
|
452
|
+
if (item.singleton) opts.push("singleton: true");
|
|
453
|
+
if (item.strictVersion) opts.push("strictVersion: true");
|
|
454
|
+
if (item.import !== false) {
|
|
455
|
+
const facadeUrl = devUrls ? devUrls.namespaceFacade(item.shareKey) : SHARED_FACADE_PREFIX + item.shareKey;
|
|
456
|
+
opts.push(`fallback: () => import(${JSON.stringify(facadeUrl)})`);
|
|
457
|
+
}
|
|
458
|
+
return `__fulgur_loadShare(${JSON.stringify(item.shareKey)}, { ${opts.join(", ")} })`;
|
|
459
|
+
}
|
|
460
|
+
function stripComments(s) {
|
|
461
|
+
return s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
|
|
462
|
+
}
|
|
463
|
+
function parseBindings(inner) {
|
|
464
|
+
const out = [];
|
|
465
|
+
for (const piece of stripComments(inner).split(",")) {
|
|
466
|
+
const raw = piece.trim();
|
|
467
|
+
if (!raw) continue;
|
|
468
|
+
if (/^type\s/.test(raw)) continue;
|
|
469
|
+
const asMatch = raw.match(/^(.*?)\s+as\s+(.+)$/);
|
|
470
|
+
const imported = asMatch ? asMatch[1].trim() : raw;
|
|
471
|
+
const local = asMatch ? asMatch[2].trim() : raw;
|
|
472
|
+
out.push({ imported, local, isDefault: imported === "default" });
|
|
473
|
+
}
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
function parseImportClause(clause) {
|
|
477
|
+
const nsMatch = clause.match(/\*\s+as\s+(\w+)/);
|
|
478
|
+
const braceStart = clause.indexOf("{");
|
|
479
|
+
const bindings = braceStart !== -1 ? parseBindings(clause.slice(braceStart + 1, clause.lastIndexOf("}"))) : null;
|
|
480
|
+
let head = clause.replace(/^\s*import\s*/, "");
|
|
481
|
+
if (braceStart !== -1) head = head.slice(0, head.indexOf("{"));
|
|
482
|
+
if (nsMatch) head = head.slice(0, head.indexOf("*"));
|
|
483
|
+
const defaultLocal = head.replace(/,\s*$/, "").trim() || null;
|
|
484
|
+
return { ns: nsMatch?.[1] ?? null, bindings, defaultLocal };
|
|
485
|
+
}
|
|
486
|
+
var JS_EXT_RE = /\.(m|c)?[jt]sx?$/;
|
|
487
|
+
function isTransformableId(id, allowNodeModules = false) {
|
|
488
|
+
const clean = id.split("?")[0];
|
|
489
|
+
if (!JS_EXT_RE.test(clean) && !clean.endsWith(".vue")) return false;
|
|
490
|
+
if (clean.includes("node_modules") && !allowNodeModules) return false;
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
var facadeRegistry = /* @__PURE__ */ new Map();
|
|
494
|
+
function shortSig(kind, key, bindings) {
|
|
495
|
+
const full = kind + "|" + key + "|" + bindings.join("|");
|
|
496
|
+
let h = 5381;
|
|
497
|
+
for (let i = 0; i < full.length; i++) h = (h << 5) + h + full.charCodeAt(i) | 0;
|
|
498
|
+
const id = (h >>> 0).toString(36) + bindings.length.toString(36);
|
|
499
|
+
facadeRegistry.set(id, kind === "shared" ? { bindings, shareKey: key } : { bindings, remoteName: key });
|
|
500
|
+
return id;
|
|
501
|
+
}
|
|
502
|
+
function getFacadeEntry(id) {
|
|
503
|
+
return facadeRegistry.get(id);
|
|
504
|
+
}
|
|
505
|
+
function canonicalBindings(bindings) {
|
|
506
|
+
const names = /* @__PURE__ */ new Set();
|
|
507
|
+
for (const bRaw of bindings) {
|
|
508
|
+
if (bRaw === "default") continue;
|
|
509
|
+
const asMatch = bRaw.match(/^(.*?)\s+as\s+(.+)$/);
|
|
510
|
+
names.add((asMatch ? asMatch[1] : bRaw).trim());
|
|
511
|
+
}
|
|
512
|
+
return [...names].sort();
|
|
513
|
+
}
|
|
514
|
+
function bindingFacadeId(item, bindings) {
|
|
515
|
+
const canonical = canonicalBindings(bindings);
|
|
516
|
+
const sig = canonical.length ? `?f=${shortSig("shared", item.shareKey, canonical)}` : "";
|
|
517
|
+
return `${SHARED_FACADE_PREFIX}${item.shareKey}${sig}`;
|
|
518
|
+
}
|
|
519
|
+
function facadeSpecFor(ctx, item, bindings) {
|
|
520
|
+
const id = bindingFacadeId(item, bindings);
|
|
521
|
+
return ctx.devUrls ? ctx.devUrls.bindingFacade(id) : id;
|
|
522
|
+
}
|
|
523
|
+
function remoteBindingFacadeId(remoteName, exposeName, bindings) {
|
|
524
|
+
const canonical = canonicalBindings(bindings);
|
|
525
|
+
const sig = canonical.length ? `?f=${shortSig("remote", `${remoteName}/${exposeName}`, canonical)}` : "";
|
|
526
|
+
return `${SHARED_FACADE_PREFIX}__remote__${remoteName}/${exposeName}${sig}`;
|
|
527
|
+
}
|
|
528
|
+
function remoteFacadeSpecFor(ctx, remoteName, exposeName, bindings) {
|
|
529
|
+
const id = remoteBindingFacadeId(remoteName, exposeName, bindings);
|
|
530
|
+
return ctx.devUrls ? ctx.devUrls.bindingFacade(id) : id;
|
|
531
|
+
}
|
|
532
|
+
async function transformModule(code, id, ctx) {
|
|
533
|
+
const { options } = ctx;
|
|
534
|
+
if (options.shared.length === 0 && options.remotes.length === 0) return null;
|
|
535
|
+
const candidates = [];
|
|
536
|
+
for (const s2 of options.shared) candidates.push(s2.configKey.replace(/\/$/, ""));
|
|
537
|
+
for (const r of options.remotes) candidates.push(r.key);
|
|
538
|
+
if (!candidates.some((c) => code.includes(c))) return null;
|
|
539
|
+
if (ctx.cjsRequireRewrite && /require\s*\(\s*["']/.test(code)) {
|
|
540
|
+
const sharedByAlias = /* @__PURE__ */ new Map();
|
|
541
|
+
for (const s2 of options.shared) {
|
|
542
|
+
if (s2.import === false) continue;
|
|
543
|
+
for (const a of s2.aliases) if (!a.includes("/")) sharedByAlias.set(a, s2);
|
|
544
|
+
}
|
|
545
|
+
let cjsEdited = false;
|
|
546
|
+
for (const [alias, item] of sharedByAlias) {
|
|
547
|
+
const re = new RegExp(`require\\((["'])${alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\1\\)`, "g");
|
|
548
|
+
if (!re.test(code)) continue;
|
|
549
|
+
code = code.replace(re, `require(${JSON.stringify(`virtual:fulgur-cjs-ns:${item.shareKey}`)})`);
|
|
550
|
+
cjsEdited = true;
|
|
551
|
+
}
|
|
552
|
+
if (!cjsEdited) return null;
|
|
553
|
+
return { code, map: null };
|
|
554
|
+
}
|
|
555
|
+
await ensureLexer();
|
|
556
|
+
let imports;
|
|
557
|
+
try {
|
|
558
|
+
;
|
|
559
|
+
[imports] = (0, import_es_module_lexer.parse)(code);
|
|
560
|
+
} catch {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
const matcher = ctx.rewriteShared ? buildSharedMatcher(options.shared) : null;
|
|
564
|
+
const remotesByKey = new Map(options.remotes.map((r) => [r.key, r]));
|
|
565
|
+
const s = new import_magic_string.default(code);
|
|
566
|
+
let edited = false;
|
|
567
|
+
let usesRuntimeHelpers = false;
|
|
568
|
+
const tlaLines = [];
|
|
569
|
+
let tempIdx = 0;
|
|
570
|
+
const genTemp = () => `__fulgur_m${tempIdx++}`;
|
|
571
|
+
const remap = (spec) => ctx.remapSpecifier?.(spec) ?? spec;
|
|
572
|
+
const remoteExposeName = (spec, remoteKey) => {
|
|
573
|
+
if (!spec.startsWith(`${remoteKey}/`)) return null;
|
|
574
|
+
const exposeRaw = spec.slice(remoteKey.length + 1);
|
|
575
|
+
if (!exposeRaw) return null;
|
|
576
|
+
return exposeRaw.startsWith(".") ? exposeRaw : `./${exposeRaw}`;
|
|
577
|
+
};
|
|
578
|
+
const bindingsOf = (clause) => [
|
|
579
|
+
...clause.defaultLocal ? ["default"] : [],
|
|
580
|
+
...(clause.bindings ?? []).map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`)
|
|
581
|
+
];
|
|
582
|
+
const genTlaOverwrite = (stmtStart, stmtEnd, shared, remoteName, exposeName, clauseRaw, clause) => {
|
|
583
|
+
usesRuntimeHelpers = true;
|
|
584
|
+
const call = shared ? serializeShareCall(shared, ctx?.devUrls) : `__fulgur_loadRemote(${JSON.stringify(`${remoteName}/${exposeName}`)})`;
|
|
585
|
+
const nsAs = clauseRaw.match(/export\s+\*\s+as\s+(\w+)\s+from/);
|
|
586
|
+
if (nsAs) {
|
|
587
|
+
s.overwrite(stmtStart, stmtEnd, `const ${nsAs[1]} = await ${call};
|
|
588
|
+
export { ${nsAs[1]} };`);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (!clause.ns && !clause.bindings && !clause.defaultLocal) {
|
|
592
|
+
s.overwrite(stmtStart, stmtEnd, `await ${call};`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const tmp = genTemp();
|
|
596
|
+
const parts = [`${tmp} = await ${call}`];
|
|
597
|
+
if (clause.ns) parts.push(`${clause.ns} = ${tmp}`);
|
|
598
|
+
if (clause.defaultLocal) parts.push(`${clause.defaultLocal} = __fulgurU(${tmp})`);
|
|
599
|
+
const named = clause.bindings?.filter((b) => !b.isDefault) ?? [];
|
|
600
|
+
if (named.length) {
|
|
601
|
+
const destructure = named.map((b) => b.imported === b.local ? b.local : `${b.imported}: ${b.local}`);
|
|
602
|
+
parts.push(`{ ${destructure.join(", ")} } = ${tmp}`);
|
|
603
|
+
}
|
|
604
|
+
for (const b of clause.bindings?.filter((x) => x.isDefault) ?? []) {
|
|
605
|
+
parts.push(`${b.local} = __fulgurU(${tmp})`);
|
|
606
|
+
}
|
|
607
|
+
s.overwrite(stmtStart, stmtEnd, `const ${parts.join(", ")}`);
|
|
608
|
+
};
|
|
609
|
+
for (const imp of imports) {
|
|
610
|
+
const { d } = imp;
|
|
611
|
+
if (d === -2) continue;
|
|
612
|
+
if (d === -1) {
|
|
613
|
+
const rawSpec = code.slice(imp.s, imp.e).replace(/['"]/g, "");
|
|
614
|
+
const spec2 = remap(rawSpec);
|
|
615
|
+
const stmtStart = imp.ss;
|
|
616
|
+
let stmtEnd = imp.se;
|
|
617
|
+
while (code[stmtEnd] === ";") stmtEnd++;
|
|
618
|
+
const head = code.slice(stmtStart, stmtStart + 24);
|
|
619
|
+
if (/^\s*(import|export)\s+type[\s{]/.test(head)) continue;
|
|
620
|
+
const shared2 = matcher ? matchShared(spec2, matcher) : null;
|
|
621
|
+
const firstSeg2 = spec2.split("/")[0];
|
|
622
|
+
const remote2 = remotesByKey.get(firstSeg2);
|
|
623
|
+
const exposeName2 = remote2 ? remoteExposeName(spec2, remote2.key) : null;
|
|
624
|
+
if (!shared2 && !(remote2 && exposeName2)) continue;
|
|
625
|
+
edited = true;
|
|
626
|
+
const clauseRaw = code.slice(stmtStart, imp.s).replace(/['"]\s*$/, "");
|
|
627
|
+
const isImport = clauseRaw.trimStart().startsWith("import");
|
|
628
|
+
const clause = parseImportClause(clauseRaw.replace(/\bfrom\s*$/, ""));
|
|
629
|
+
if (clause.ns || /export\s+\*\s+as\s+\w+\s+from/.test(clauseRaw)) {
|
|
630
|
+
genTlaOverwrite(stmtStart, stmtEnd, shared2, remote2?.name ?? null, exposeName2, clauseRaw, clause);
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (!isImport) {
|
|
634
|
+
const inner = clauseRaw.slice(clauseRaw.indexOf("{") + 1, clauseRaw.lastIndexOf("}"));
|
|
635
|
+
if (/export\s+\*/.test(clauseRaw) && !clauseRaw.includes("{")) {
|
|
636
|
+
throw new Error(
|
|
637
|
+
`[fulgur] "export * from '${spec2}'" on a shared module is not supported (ESM cannot create dynamic export bindings). Use named re-exports: "export { a, b } from '${spec2}'".`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
const bindings = parseBindings(inner).map((b) => b.imported === "default" ? "default" : b.imported);
|
|
641
|
+
const facadeSpec2 = shared2 ? facadeSpecFor(ctx, shared2, bindings) : remoteFacadeSpecFor(ctx, remote2.name, exposeName2, bindings);
|
|
642
|
+
s.overwrite(imp.s, imp.e, facadeSpec2);
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
const facadeSpec = shared2 ? facadeSpecFor(ctx, shared2, bindingsOf(clause)) : remoteFacadeSpecFor(ctx, remote2.name, exposeName2, bindingsOf(clause));
|
|
646
|
+
s.overwrite(imp.s, imp.e, facadeSpec);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const specExpr = code.slice(imp.s, imp.e).trim();
|
|
650
|
+
const literal = specExpr.match(/^['"](.*)['"]$/s);
|
|
651
|
+
if (!literal) continue;
|
|
652
|
+
const spec = remap(literal[1]);
|
|
653
|
+
const shared = matcher ? matchShared(spec, matcher) : null;
|
|
654
|
+
const firstSeg = spec.split("/")[0];
|
|
655
|
+
const remote = remotesByKey.get(firstSeg);
|
|
656
|
+
const exposeName = remote ? remoteExposeName(spec, remote.key) : null;
|
|
657
|
+
if (!shared && !(remote && exposeName)) continue;
|
|
658
|
+
edited = true;
|
|
659
|
+
usesRuntimeHelpers = true;
|
|
660
|
+
const callStart = code.lastIndexOf("import", d);
|
|
661
|
+
const closeParen = code.indexOf(")", imp.e);
|
|
662
|
+
if (shared) {
|
|
663
|
+
s.overwrite(callStart, closeParen + 1, serializeShareCall(shared, ctx?.devUrls));
|
|
664
|
+
} else if (remote && exposeName) {
|
|
665
|
+
s.overwrite(
|
|
666
|
+
callStart,
|
|
667
|
+
closeParen + 1,
|
|
668
|
+
`__fulgur_loadRemote(${JSON.stringify(`${remote.name}/${exposeName}`)})`
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (!edited) return null;
|
|
673
|
+
if (usesRuntimeHelpers) {
|
|
674
|
+
const runtimeSpec = JSON.stringify(ctx.devUrls?.runtime ?? "virtual:fulgur-runtime");
|
|
675
|
+
s.prepend(
|
|
676
|
+
`import { loadShare as __fulgur_loadShare, loadRemote as __fulgur_loadRemote, unwrapDefault as __fulgurU } from ${runtimeSpec};
|
|
677
|
+
`
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
return { code: s.toString(), map: null };
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// src/virtual.ts
|
|
684
|
+
function jsonReplacer(_k, v) {
|
|
685
|
+
return v;
|
|
686
|
+
}
|
|
687
|
+
function genSharedFacade(specifier, exportNames) {
|
|
688
|
+
const names = (exportNames ?? []).filter(
|
|
689
|
+
(n) => n !== "default" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(n)
|
|
690
|
+
);
|
|
691
|
+
if (names.length === 0) {
|
|
692
|
+
return [
|
|
693
|
+
`import * as __fulgur_facade from ${JSON.stringify(specifier)};`,
|
|
694
|
+
`export * from ${JSON.stringify(specifier)};`,
|
|
695
|
+
`export default __fulgur_facade.default ?? __fulgur_facade;`,
|
|
696
|
+
""
|
|
697
|
+
].join("\n");
|
|
698
|
+
}
|
|
699
|
+
return [
|
|
700
|
+
`import * as __fulgur_facade from ${JSON.stringify(specifier)};`,
|
|
701
|
+
...names.map((n) => `export const ${n} = __fulgur_facade[${JSON.stringify(n)}];`),
|
|
702
|
+
`export default __fulgur_facade.default ?? __fulgur_facade;`,
|
|
703
|
+
""
|
|
704
|
+
].join("\n");
|
|
705
|
+
}
|
|
706
|
+
function genSharedNsFacade(item, loadShareCall, exportNames) {
|
|
707
|
+
const lines = [
|
|
708
|
+
`import { loadShare as __fulgur_loadShare, unwrapDefault as __fulgurU } from "virtual:fulgur-runtime";`,
|
|
709
|
+
`const __fulgur_m = await ${loadShareCall};`,
|
|
710
|
+
`const __fulgur_d = __fulgurU(__fulgur_m);`,
|
|
711
|
+
`export default __fulgur_d;`
|
|
712
|
+
];
|
|
713
|
+
const seen = /* @__PURE__ */ new Set(["default"]);
|
|
714
|
+
for (const name of exportNames) {
|
|
715
|
+
if (seen.has(name) || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) continue;
|
|
716
|
+
seen.add(name);
|
|
717
|
+
lines.push(`export const ${name} = __fulgur_d[${JSON.stringify(name)}];`);
|
|
718
|
+
}
|
|
719
|
+
lines.push("");
|
|
720
|
+
return lines.join("\n");
|
|
721
|
+
}
|
|
722
|
+
function genBindingFacade(item, bindings, loadShareCall) {
|
|
723
|
+
const lines = [
|
|
724
|
+
`import { loadShare as __fulgur_loadShare, unwrapDefault as __fulgurU } from "virtual:fulgur-runtime";`,
|
|
725
|
+
`const __fulgur_m = await ${loadShareCall};`
|
|
726
|
+
];
|
|
727
|
+
const seen = /* @__PURE__ */ new Set();
|
|
728
|
+
for (const bRaw of bindings) {
|
|
729
|
+
if (bRaw === "default") continue;
|
|
730
|
+
const asMatch = bRaw.match(/^(.*?)\s+as\s+(.+)$/);
|
|
731
|
+
const imported = (asMatch ? asMatch[1] : bRaw).trim();
|
|
732
|
+
if (seen.has(imported)) continue;
|
|
733
|
+
seen.add(imported);
|
|
734
|
+
lines.push(`export const ${imported} = __fulgur_m.${imported};`);
|
|
735
|
+
}
|
|
736
|
+
lines.push(`export default __fulgurU(__fulgur_m);`);
|
|
737
|
+
lines.push("");
|
|
738
|
+
return lines.join("\n");
|
|
739
|
+
}
|
|
740
|
+
function genRemoteBindingFacade(remoteSpec, bindings) {
|
|
741
|
+
const lines = [
|
|
742
|
+
`import { loadRemote as __fulgur_loadRemote, unwrapDefault as __fulgurU } from "virtual:fulgur-runtime";`,
|
|
743
|
+
`const __fulgur_m = await __fulgur_loadRemote(${JSON.stringify(remoteSpec)});`
|
|
744
|
+
];
|
|
745
|
+
const seen = /* @__PURE__ */ new Set();
|
|
746
|
+
for (const bRaw of bindings) {
|
|
747
|
+
if (bRaw === "default") continue;
|
|
748
|
+
const asMatch = bRaw.match(/^(.*?)\s+as\s+(.+)$/);
|
|
749
|
+
const imported = (asMatch ? asMatch[1] : bRaw).trim();
|
|
750
|
+
if (seen.has(imported)) continue;
|
|
751
|
+
seen.add(imported);
|
|
752
|
+
lines.push(`export const ${imported} = __fulgur_m.${imported};`);
|
|
753
|
+
}
|
|
754
|
+
lines.push(`export default __fulgurU(__fulgur_m);`);
|
|
755
|
+
lines.push("");
|
|
756
|
+
return lines.join("\n");
|
|
757
|
+
}
|
|
758
|
+
function providesRecords(options) {
|
|
759
|
+
return options.shared.filter((s) => s.import !== false).map((s) => ({
|
|
760
|
+
shareScope: s.shareScope,
|
|
761
|
+
name: s.shareKey,
|
|
762
|
+
version: s.version,
|
|
763
|
+
eager: s.eager,
|
|
764
|
+
from: options.name
|
|
765
|
+
}));
|
|
766
|
+
}
|
|
767
|
+
function genInitModule(options, command) {
|
|
768
|
+
const lines = [];
|
|
769
|
+
lines.push(`import { initSharing, registerShare, registerRemotes, registerPlugins } from "virtual:fulgur-runtime";`);
|
|
770
|
+
const provides = providesRecords(options);
|
|
771
|
+
const eagerShared = options.shared.filter((s) => s.eager && s.import !== false);
|
|
772
|
+
eagerShared.forEach((s, i) => {
|
|
773
|
+
lines.push(`import * as __fulgur_eager_${i} from "virtual:fulgur-shared:${s.shareKey}";`);
|
|
774
|
+
});
|
|
775
|
+
const isDevHost = command === "serve" && options.remotes.length > 0;
|
|
776
|
+
const loadedFlag = isDevHost ? "true" : "false";
|
|
777
|
+
lines.push(`initSharing(${JSON.stringify(options.shareScope)});`);
|
|
778
|
+
if (options.runtimePlugins.length > 0) {
|
|
779
|
+
options.runtimePlugins.forEach((p, i) => {
|
|
780
|
+
lines.push(`import * as __fulgur_rp_${i} from ${JSON.stringify(p)};`);
|
|
781
|
+
});
|
|
782
|
+
lines.push(
|
|
783
|
+
`registerPlugins([${options.runtimePlugins.map((_, i) => `(__fulgur_rp_${i}.default ?? __fulgur_rp_${i})`).join(", ")}]);`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
if (options.remotes.length > 0) {
|
|
787
|
+
lines.push(...registerRemotesLines(options, command));
|
|
788
|
+
}
|
|
789
|
+
provides.forEach((p) => {
|
|
790
|
+
const eagerIdx = eagerShared.findIndex((s) => s.shareKey === p.name && s.shareScope === p.shareScope);
|
|
791
|
+
if (p.eager && eagerIdx !== -1) {
|
|
792
|
+
lines.push(
|
|
793
|
+
`registerShare(${JSON.stringify(p.shareScope)}, ${JSON.stringify(p.name)}, ${JSON.stringify(p.version)}, () => Promise.resolve(__fulgur_eager_${eagerIdx}), { from: ${JSON.stringify(p.from)}, eager: true, loaded: ${loadedFlag} });`
|
|
794
|
+
);
|
|
795
|
+
} else {
|
|
796
|
+
lines.push(
|
|
797
|
+
`registerShare(${JSON.stringify(p.shareScope)}, ${JSON.stringify(p.name)}, ${JSON.stringify(p.version)}, () => import("virtual:fulgur-shared:${p.name}"), { from: ${JSON.stringify(p.from)}, eager: false, loaded: ${loadedFlag} });`
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
return `${lines.join("\n")}
|
|
802
|
+
`;
|
|
803
|
+
}
|
|
804
|
+
function manifestUrlFor(r, command) {
|
|
805
|
+
const entry = command === "serve" ? r.devEntry : r.prodEntry;
|
|
806
|
+
if (!entry) return null;
|
|
807
|
+
try {
|
|
808
|
+
const u = new URL(entry);
|
|
809
|
+
if (u.pathname.includes("@fulgur-entry.js")) {
|
|
810
|
+
u.pathname = u.pathname.replace("@fulgur-entry.js", "@fulgur-manifest.json");
|
|
811
|
+
} else {
|
|
812
|
+
u.pathname = u.pathname.replace(/[^/]*$/, "") + "fulgur-manifest.json";
|
|
813
|
+
}
|
|
814
|
+
return u.href;
|
|
815
|
+
} catch {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
function registerRemotesLines(options, command) {
|
|
820
|
+
if (options.remotes.length === 0) return [];
|
|
821
|
+
const remotesJson = options.remotes.filter((r) => !r.promise).map((r) => ({
|
|
822
|
+
name: r.name,
|
|
823
|
+
entry: command === "serve" ? r.devEntry : r.prodEntry,
|
|
824
|
+
shareScope: r.shareScope,
|
|
825
|
+
timeout: r.timeout,
|
|
826
|
+
retries: r.retries,
|
|
827
|
+
fallback: r.fallback,
|
|
828
|
+
breaker: r.breaker,
|
|
829
|
+
manifestUrl: manifestUrlFor(r, command)
|
|
830
|
+
}));
|
|
831
|
+
if (remotesJson.length === 0) return [];
|
|
832
|
+
return [
|
|
833
|
+
`registerRemotes(${JSON.stringify(remotesJson, jsonReplacer).replace(/"manifestUrl":null,?/g, "")});`
|
|
834
|
+
];
|
|
835
|
+
}
|
|
836
|
+
function genRuntimeProxyModule() {
|
|
837
|
+
const promiseApis = [
|
|
838
|
+
"loadRemote",
|
|
839
|
+
"loadShare",
|
|
840
|
+
"preloadRemote",
|
|
841
|
+
"getContainer",
|
|
842
|
+
"registerRemote",
|
|
843
|
+
"registerRemotes",
|
|
844
|
+
"registerShare",
|
|
845
|
+
"initSharing",
|
|
846
|
+
"registerPlugins",
|
|
847
|
+
"parseSpec"
|
|
848
|
+
];
|
|
849
|
+
const lines = [
|
|
850
|
+
`let __fulgur_mod_p;`,
|
|
851
|
+
`const __fulgur_rt = async () => {`,
|
|
852
|
+
` __fulgur_mod_p ??= import('virtual:fulgur-runtime');`,
|
|
853
|
+
` return await __fulgur_mod_p;`,
|
|
854
|
+
`};`,
|
|
855
|
+
...promiseApis.map((m) => `export const ${m} = (...a) => __fulgur_rt().then((m2) => m2.${m}(...a));`),
|
|
856
|
+
`export const getRuntime = () => (globalThis).__FULGUR_RUNTIME__;`,
|
|
857
|
+
`export const getFulgurAppConfig = () =>`,
|
|
858
|
+
` (globalThis).__FULGUR_RUNTIME__?.getFulgurAppConfig?.() ?? (globalThis).__FULGUR_APP_CONFIG__ ?? {};`,
|
|
859
|
+
`export const provideFulgurAppConfig = (c) => {`,
|
|
860
|
+
` const g = (globalThis);`,
|
|
861
|
+
` g.__FULGUR_APP_CONFIG__ = { ...(g.__FULGUR_APP_CONFIG__ ?? {}), ...c };`,
|
|
862
|
+
` g.__FULGUR_RUNTIME__?.provideFulgurAppConfig?.(c);`,
|
|
863
|
+
`};`,
|
|
864
|
+
`export const unwrapDefault = (ns) =>`,
|
|
865
|
+
` ns && typeof ns === 'object' && 'default' in ns ? (ns.default !== undefined ? ns.default : ns) : ns;`,
|
|
866
|
+
`export const version = (globalThis).__FULGUR_RUNTIME__?.version;`,
|
|
867
|
+
`export default { get runtime() { return (globalThis).__FULGUR_RUNTIME__; } };`,
|
|
868
|
+
``
|
|
869
|
+
];
|
|
870
|
+
return lines.join("\n");
|
|
871
|
+
}
|
|
872
|
+
function genDevRemoteEntry(options, base) {
|
|
873
|
+
const b = base.endsWith("/") ? base : `${base}/`;
|
|
874
|
+
const remoteLines = registerRemotesLines(options, "serve");
|
|
875
|
+
return `import ${JSON.stringify(`${b}@vite/client`)};
|
|
876
|
+
import { name as _fulgur_name, exposes, provides } from ${JSON.stringify(`${b}@id/__x00__virtual:fulgur-provides`)};
|
|
877
|
+
${remoteLines.length > 0 ? `import { registerRemotes } from ${JSON.stringify(`${b}@id/virtual:fulgur-runtime`)};
|
|
878
|
+
${remoteLines.join("\n")}` : ""}
|
|
879
|
+
|
|
880
|
+
export const name = _fulgur_name;
|
|
881
|
+
|
|
882
|
+
export async function init(shareScopeMap) {
|
|
883
|
+
for (const p of provides) {
|
|
884
|
+
const scope = shareScopeMap[p.shareScope] || (shareScopeMap[p.shareScope] = {});
|
|
885
|
+
const byName = scope[p.name] || (scope[p.name] = {});
|
|
886
|
+
if (byName[p.version]) continue; // \u5DF2\u6CE8\u518C\u7248\u672C\u6C38\u4E0D\u66FF\u6362
|
|
887
|
+
byName[p.version] = { version: p.version, get: p.get, from: name, eager: p.eager, loaded: false };
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export async function get(moduleName) {
|
|
892
|
+
const loader = exposes[moduleName];
|
|
893
|
+
if (!loader) {
|
|
894
|
+
const err = new Error('MFU-006: module "' + moduleName + '" is not exposed by remote "' + name + '"');
|
|
895
|
+
err.code = 'MFU-006';
|
|
896
|
+
throw err;
|
|
897
|
+
}
|
|
898
|
+
return loader();
|
|
899
|
+
}
|
|
900
|
+
`;
|
|
901
|
+
}
|
|
902
|
+
function genDevProvides(options) {
|
|
903
|
+
const exposes = [];
|
|
904
|
+
for (const e of options.exposes) {
|
|
905
|
+
const devUrl = `/${e.import.replace(/^\.?\//, "")}`;
|
|
906
|
+
exposes.push(
|
|
907
|
+
` ${JSON.stringify(e.name)}: () => import(${JSON.stringify(devUrl)}),`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
const provides = providesRecords(options).map(
|
|
911
|
+
(p) => ` { shareScope: ${JSON.stringify(p.shareScope)}, name: ${JSON.stringify(p.name)}, version: ${JSON.stringify(p.version)}, eager: ${p.eager}, get: () => import("virtual:fulgur-shared:${p.name}") },`
|
|
912
|
+
);
|
|
913
|
+
return [
|
|
914
|
+
`export const name = ${JSON.stringify(options.name)};`,
|
|
915
|
+
`export const exposes = {`,
|
|
916
|
+
...exposes,
|
|
917
|
+
`};`,
|
|
918
|
+
`export const provides = [`,
|
|
919
|
+
...provides,
|
|
920
|
+
`];`,
|
|
921
|
+
""
|
|
922
|
+
].join("\n");
|
|
923
|
+
}
|
|
924
|
+
function genBuildRemoteEntry(options, exposeAbsPaths) {
|
|
925
|
+
const exposes = [];
|
|
926
|
+
for (const e of options.exposes) {
|
|
927
|
+
const abs = exposeAbsPaths[e.import];
|
|
928
|
+
if (!abs) continue;
|
|
929
|
+
exposes.push(` ${JSON.stringify(e.name)}: () => import(${JSON.stringify(abs)}),`);
|
|
930
|
+
}
|
|
931
|
+
const provides = providesRecords(options).map(
|
|
932
|
+
(p) => ` { shareScope: ${JSON.stringify(p.shareScope)}, name: ${JSON.stringify(p.name)}, version: ${JSON.stringify(p.version)}, eager: ${p.eager}, get: () => import("virtual:fulgur-shared:${p.name}") },`
|
|
933
|
+
);
|
|
934
|
+
const remoteLines = registerRemotesLines(options, "build");
|
|
935
|
+
return [
|
|
936
|
+
...remoteLines.length > 0 ? [`import { registerRemotes } from "virtual:fulgur-runtime";`, ...remoteLines] : [],
|
|
937
|
+
`const exposes = {`,
|
|
938
|
+
...exposes,
|
|
939
|
+
`};`,
|
|
940
|
+
`const provides = [`,
|
|
941
|
+
...provides,
|
|
942
|
+
`];`,
|
|
943
|
+
`export const name = ${JSON.stringify(options.name)};`,
|
|
944
|
+
``,
|
|
945
|
+
`export async function init(shareScopeMap) {`,
|
|
946
|
+
` for (const p of provides) {`,
|
|
947
|
+
` const scope = shareScopeMap[p.shareScope] || (shareScopeMap[p.shareScope] = {});`,
|
|
948
|
+
` const byName = scope[p.name] || (scope[p.name] = {});`,
|
|
949
|
+
` if (byName[p.version]) continue; // \u5DF2\u6CE8\u518C\u7248\u672C\u6C38\u4E0D\u66FF\u6362`,
|
|
950
|
+
` byName[p.version] = { version: p.version, get: p.get, from: name, eager: p.eager, loaded: false };`,
|
|
951
|
+
` }`,
|
|
952
|
+
`}`,
|
|
953
|
+
``,
|
|
954
|
+
`export async function get(moduleName) {`,
|
|
955
|
+
` const loader = exposes[moduleName];`,
|
|
956
|
+
` if (!loader) {`,
|
|
957
|
+
` const err = new Error('MFU-006: module "' + moduleName + '" is not exposed by remote "' + name + '"');`,
|
|
958
|
+
` err.code = 'MFU-006';`,
|
|
959
|
+
` throw err;`,
|
|
960
|
+
` }`,
|
|
961
|
+
` return loader();`,
|
|
962
|
+
`}`,
|
|
963
|
+
""
|
|
964
|
+
].join("\n");
|
|
965
|
+
}
|
|
966
|
+
function genDevManifest(options, base) {
|
|
967
|
+
const b = base.endsWith("/") ? base : `${base}/`;
|
|
968
|
+
return {
|
|
969
|
+
id: options.name,
|
|
970
|
+
name: options.name,
|
|
971
|
+
version: options.pkgDependencies?.["fulgur"] ?? "0.0.0",
|
|
972
|
+
devServer: true,
|
|
973
|
+
base: b,
|
|
974
|
+
entry: `${b}@fulgur-entry.js`,
|
|
975
|
+
/** 本地联调时供宿主端 dts 类型直连(见 dts.ts);远程不在本机时宿主回退 any 桩 */
|
|
976
|
+
fsRoot: options.root,
|
|
977
|
+
exposes: options.exposes.map((e) => ({
|
|
978
|
+
name: e.name,
|
|
979
|
+
src: e.import,
|
|
980
|
+
file: `${b}@fulgur-src/${e.import.replace(/^\.?\//, "")}`
|
|
981
|
+
})),
|
|
982
|
+
shared: options.shared.map((s) => ({
|
|
983
|
+
name: s.shareKey,
|
|
984
|
+
version: s.version,
|
|
985
|
+
singleton: s.singleton,
|
|
986
|
+
requiredVersion: s.requiredVersion,
|
|
987
|
+
shareScope: s.shareScope,
|
|
988
|
+
eager: s.eager
|
|
989
|
+
}))
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function genProdManifest(options, exposeFiles, entryFile) {
|
|
993
|
+
return {
|
|
994
|
+
id: options.name,
|
|
995
|
+
name: options.name,
|
|
996
|
+
entry: entryFile,
|
|
997
|
+
exposes: exposeFiles,
|
|
998
|
+
shared: options.shared.map((s) => ({
|
|
999
|
+
name: s.shareKey,
|
|
1000
|
+
version: s.version,
|
|
1001
|
+
singleton: s.singleton,
|
|
1002
|
+
requiredVersion: s.requiredVersion,
|
|
1003
|
+
shareScope: s.shareScope,
|
|
1004
|
+
eager: s.eager
|
|
1005
|
+
})),
|
|
1006
|
+
buildInfo: {
|
|
1007
|
+
builtBy: "fulgur-federation",
|
|
1008
|
+
timestamp: Date.now()
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/dts.ts
|
|
1014
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
1015
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
1016
|
+
async function fetchManifest(devEntry, attempts = 30, delayMs = 2e3) {
|
|
1017
|
+
let manifestUrl;
|
|
1018
|
+
try {
|
|
1019
|
+
const u = new URL(devEntry);
|
|
1020
|
+
u.pathname = u.pathname.replace("@fulgur-entry.js", "@fulgur-manifest.json");
|
|
1021
|
+
manifestUrl = u;
|
|
1022
|
+
} catch {
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
for (let i = 0; i < attempts; i++) {
|
|
1026
|
+
try {
|
|
1027
|
+
const res = await fetch(manifestUrl, { signal: AbortSignal.timeout(3e3) });
|
|
1028
|
+
if (res.ok) return await res.json();
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1032
|
+
}
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
function sourceHasDefaultExport(file) {
|
|
1036
|
+
try {
|
|
1037
|
+
const text = import_node_fs2.default.readFileSync(file, "utf8");
|
|
1038
|
+
return /export\s+default\b/.test(text) || /export\s*\{[^}]*\bdefault\b[^}]*\}/.test(text);
|
|
1039
|
+
} catch {
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
async function generateDevTypes(options, _server) {
|
|
1044
|
+
const dtsOpt = options.dts === void 0 ? true : options.dts;
|
|
1045
|
+
if (dtsOpt === false) return;
|
|
1046
|
+
const defaultDir = import_node_fs2.default.existsSync(import_node_path3.default.join(options.root, "src")) ? "src/fulgur-types" : "fulgur-types";
|
|
1047
|
+
const dir = typeof dtsOpt === "object" ? dtsOpt.dir ?? defaultDir : defaultDir;
|
|
1048
|
+
const outDir = import_node_path3.default.join(options.root, dir);
|
|
1049
|
+
import_node_fs2.default.mkdirSync(outDir, { recursive: true });
|
|
1050
|
+
for (const remote of options.remotes) {
|
|
1051
|
+
if (!remote.devEntry || remote.promise) continue;
|
|
1052
|
+
const manifest = await fetchManifest(remote.devEntry);
|
|
1053
|
+
if (!manifest || !manifest.exposes) {
|
|
1054
|
+
console.warn(`[fulgur] dts: remote "${remote.key}" dev manifest unavailable; type mapping skipped.`);
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
const remoteRoot = manifest.fsRoot;
|
|
1058
|
+
if (!remoteRoot || !import_node_fs2.default.existsSync(remoteRoot)) {
|
|
1059
|
+
console.warn(
|
|
1060
|
+
`[fulgur] dts: remote "${remote.key}" is not on this machine; type mapping skipped (module types fall back to any).`
|
|
1061
|
+
);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
const lines = [
|
|
1065
|
+
`// \u81EA\u52A8\u751F\u6210\uFF1Afulgur-federation dev \u7C7B\u578B\u76F4\u8FDE\uFF08remote: ${remote.name}\uFF09`,
|
|
1066
|
+
`// \u91CD\u65B0\u751F\u6210\uFF1A\u91CD\u542F host dev server`
|
|
1067
|
+
];
|
|
1068
|
+
for (const expose of manifest.exposes ?? []) {
|
|
1069
|
+
const abs = import_node_path3.default.join(remoteRoot, expose.src.replace(/^\//, ""));
|
|
1070
|
+
if (!import_node_fs2.default.existsSync(abs)) continue;
|
|
1071
|
+
const rel = import_node_path3.default.relative(outDir, abs).split(import_node_path3.default.sep).join("/");
|
|
1072
|
+
const importPath = rel.startsWith(".") ? rel : `./${rel}`;
|
|
1073
|
+
const moduleSpecifier = `${remote.key}/${expose.name.replace(/^\.\//, "")}`;
|
|
1074
|
+
lines.push("");
|
|
1075
|
+
if (abs.endsWith(".vue")) {
|
|
1076
|
+
lines.push(`declare module '${moduleSpecifier}' {`);
|
|
1077
|
+
lines.push(` import type { DefineComponent } from 'vue'`);
|
|
1078
|
+
lines.push(` const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>`);
|
|
1079
|
+
lines.push(` export default component`);
|
|
1080
|
+
lines.push(` export * from '${importPath}'`);
|
|
1081
|
+
lines.push(`}`);
|
|
1082
|
+
} else {
|
|
1083
|
+
lines.push(`declare module '${moduleSpecifier}' {`);
|
|
1084
|
+
lines.push(` export * from '${importPath}'`);
|
|
1085
|
+
if (sourceHasDefaultExport(abs)) lines.push(` export { default } from '${importPath}'`);
|
|
1086
|
+
lines.push(`}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
import_node_fs2.default.writeFileSync(import_node_path3.default.join(outDir, `${remote.key}.d.ts`), `${lines.join("\n")}
|
|
1090
|
+
`);
|
|
1091
|
+
console.log(
|
|
1092
|
+
`[fulgur] dts: generated ${dir}/${remote.key}.d.ts (${manifest.exposes?.length ?? 0} exposes). \u786E\u4FDD tsconfig include \u5305\u542B "${dir}" \u4EE5\u83B7\u5F97\u6E90\u7801\u7EA7\u7C7B\u578B\u8865\u5168\u3002`
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// src/diagnostics.ts
|
|
1098
|
+
function formatFulgurDiagnostic(input) {
|
|
1099
|
+
const ctx = input.details ? `
|
|
1100
|
+
\u4E0A\u4E0B\u6587\uFF1A${JSON.stringify(input.details)}` : "";
|
|
1101
|
+
return `[fulgur:${input.code}] ${input.symptom}
|
|
1102
|
+
\u6839\u56E0\uFF1A${input.cause}
|
|
1103
|
+
\u4FEE\u6CD5\uFF1A${input.fix}` + ctx;
|
|
1104
|
+
}
|
|
1105
|
+
async function isPortReachable(host, port, timeoutMs = 800) {
|
|
1106
|
+
const net = await import("net");
|
|
1107
|
+
return new Promise((resolve) => {
|
|
1108
|
+
const socket = new net.Socket();
|
|
1109
|
+
const done = (ok) => {
|
|
1110
|
+
socket.destroy();
|
|
1111
|
+
resolve(ok);
|
|
1112
|
+
};
|
|
1113
|
+
socket.setTimeout(timeoutMs);
|
|
1114
|
+
socket.once("connect", () => done(true));
|
|
1115
|
+
socket.once("timeout", () => done(false));
|
|
1116
|
+
socket.once("error", () => done(false));
|
|
1117
|
+
socket.connect(port, host);
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/remote-schema.ts
|
|
1122
|
+
function manifestUrlOf(devEntry) {
|
|
1123
|
+
try {
|
|
1124
|
+
const u = new URL(devEntry);
|
|
1125
|
+
u.pathname = u.pathname.replace("@fulgur-entry.js", "@fulgur-manifest.json");
|
|
1126
|
+
return u;
|
|
1127
|
+
} catch {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
async function probeRemotesAndBuildSchema(options) {
|
|
1132
|
+
const schema = {};
|
|
1133
|
+
for (const remote of options.remotes) {
|
|
1134
|
+
if (!remote.devEntry || remote.promise) {
|
|
1135
|
+
schema[remote.key] = { exposes: [], exists: false };
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
const u = manifestUrlOf(remote.devEntry);
|
|
1139
|
+
if (!u) {
|
|
1140
|
+
schema[remote.key] = { exposes: [], exists: false };
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
try {
|
|
1144
|
+
const res = await fetch(u, { signal: AbortSignal.timeout(3e3) });
|
|
1145
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1146
|
+
const manifest = await res.json();
|
|
1147
|
+
const exposes = (manifest?.exposes ?? []).map((e) => e.name);
|
|
1148
|
+
schema[remote.key] = { exposes, exists: true };
|
|
1149
|
+
if (exposes.length === 0) {
|
|
1150
|
+
console.warn(
|
|
1151
|
+
formatFulgurDiagnostic({
|
|
1152
|
+
code: "DEV-002",
|
|
1153
|
+
symptom: `remote "${remote.key}" \u7684 dev manifest \u53EF\u8FBE\u4F46 exposes \u4E3A\u7A7A\uFF08${u.href}\uFF09`,
|
|
1154
|
+
cause: "remote \u4FA7 federation({ exposes }) \u4E3A\u7A7A\uFF0C\u6216\u5176\u63D2\u4EF6\u7248\u672C\u8FC7\u65E7\u5BFC\u81F4 manifest \u7F3A\u5B57\u6BB5",
|
|
1155
|
+
fix: "\u6838\u5BF9 remote \u7684 federation \u914D\u7F6E exposes\uFF0C\u5E76\u786E\u8BA4 @fulgurjs/federation \u7248\u672C\u4E0E\u5BBF\u4E3B\u4E00\u81F4",
|
|
1156
|
+
details: { remote: remote.key, url: u.href, pluginVersion: manifest?.buildInfo?.version }
|
|
1157
|
+
})
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
const remoteVersion = manifest?.buildInfo?.version;
|
|
1161
|
+
if (remoteVersion && remoteVersion !== options.pluginVersion) {
|
|
1162
|
+
console.warn(
|
|
1163
|
+
formatFulgurDiagnostic({
|
|
1164
|
+
code: "DEV-006",
|
|
1165
|
+
symptom: `\u5BBF\u4E3B\u4E0E remote "${remote.key}" \u7684 @fulgurjs/federation \u7248\u672C\u4E0D\u4E00\u81F4`,
|
|
1166
|
+
cause: `\u5BBF\u4E3B ${options.pluginVersion} vs \u8FDC\u7A0B ${remoteVersion}\uFF08pnpm tarball \u65AD\u94FE/\u6F0F\u5347\u7EA7\u5E38\u89C1\uFF09`,
|
|
1167
|
+
fix: "\u7EDF\u4E00\u5347\u7EA7\u5404\u5E94\u7528\u4F9D\u8D56\u5230\u540C\u4E00\u7248\u672C\uFF1Apnpm add -D @fulgurjs/federation@<version> \u5E76\u91CD\u542F dev server",
|
|
1168
|
+
details: { host: options.pluginVersion, remote: remoteVersion }
|
|
1169
|
+
})
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
schema[remote.key] = { exposes: [], exists: false };
|
|
1174
|
+
const port = u.port ? Number(u.port) : u.protocol === "https:" ? 443 : 80;
|
|
1175
|
+
const portOpen = await isPortReachable(u.hostname, port);
|
|
1176
|
+
console.warn(
|
|
1177
|
+
formatFulgurDiagnostic({
|
|
1178
|
+
code: "DEV-001",
|
|
1179
|
+
symptom: `remote "${remote.key}" \u7684 dev manifest \u4E0D\u53EF\u8FBE\uFF08${u.href}\uFF09\u2014\u2014\u8BE5 remote \u7684\u8DEF\u7531\u5B58\u5728\u6027\u6821\u9A8C\u5C06\u8DF3\u8FC7`,
|
|
1180
|
+
cause: portOpen ? "\u7AEF\u53E3\u6709\u8FDB\u7A0B\u76D1\u542C\u4F46 manifest \u7AEF\u70B9\u5F02\u5E38\uFF1Aremote dev server \u53EF\u80FD\u4EE5\u975E\u8054\u90A6\u914D\u7F6E\u542F\u52A8\uFF0C\u6216 base/\u8DEF\u5F84\u4E0E remotes[].dev \u4E0D\u4E00\u81F4" : "\u7AEF\u53E3\u65E0\u8FDB\u7A0B\u76D1\u542C\uFF1Aremote dev server \u672A\u542F\u52A8\uFF0C\u6216 remotes[].dev \u7AEF\u53E3\u5199\u9519\uFF08server.origin \u9519\u4F4D\u540C\u6B3E\uFF09",
|
|
1181
|
+
fix: "\u542F\u52A8\u5BF9\u5E94 remote \u7684 dev server\uFF1B\u6838\u5BF9\u5BBF\u4E3B federation({ remotes }) \u7684 dev URL \u4E0E remote \u5B9E\u9645 VITE_PORT/base",
|
|
1182
|
+
details: { remote: remote.key, url: u.href, port, portOpen, error: String(err?.message ?? err) }
|
|
1183
|
+
})
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return schema;
|
|
1188
|
+
}
|
|
1189
|
+
function genEmptyRemoteSchemaModule() {
|
|
1190
|
+
console.info("[fulgur] build \u671F\u8DF3\u8FC7\u8DEF\u7531 spec \u5B58\u5728\u6027\u6821\u9A8C\uFF08\u8FDC\u7A0B manifest \u4E0D\u5728\u6784\u5EFA\u673A\u672C\u5730\uFF1Bdev \u4E0B\u81EA\u52A8\u542F\u7528\uFF09");
|
|
1191
|
+
return "export default {}";
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// src/vite-cache.ts
|
|
1195
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
1196
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
1197
|
+
function syncViteCacheMarker(root, pluginVersion, log) {
|
|
1198
|
+
const viteDir = import_node_path4.default.join(root, "node_modules", ".vite");
|
|
1199
|
+
const marker = import_node_path4.default.join(viteDir, "fulgur-version.txt");
|
|
1200
|
+
try {
|
|
1201
|
+
if (import_node_fs3.default.existsSync(viteDir)) {
|
|
1202
|
+
const prev = import_node_fs3.default.existsSync(marker) ? import_node_fs3.default.readFileSync(marker, "utf8").trim() : "";
|
|
1203
|
+
if (prev === pluginVersion) return;
|
|
1204
|
+
import_node_fs3.default.rmSync(viteDir, { recursive: true, force: true });
|
|
1205
|
+
log(
|
|
1206
|
+
`[fulgur] \u63D2\u4EF6\u7248\u672C\u53D8\u5316\uFF08${prev || "\u65E0\u8BB0\u5F55"} \u2192 ${pluginVersion}\uFF09\uFF0C\u5DF2\u81EA\u52A8\u6E05\u9664 ${import_node_path4.default.relative(root, viteDir)} \u9884\u6784\u5EFA\u7F13\u5B58\uFF08\u907F\u514D\u65E7\u95E8\u9762\u7B7E\u540D 404 / DEV-009\uFF09\u3002\u672C\u6B21\u542F\u52A8\u5C06\u91CD\u65B0\u9884\u6784\u5EFA\uFF0C\u9996\u8F6E 30~60s \u5C5E\u6682\u6001\uFF08DEV-010\uFF09\u3002`
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
import_node_fs3.default.mkdirSync(viteDir, { recursive: true });
|
|
1210
|
+
import_node_fs3.default.writeFileSync(marker, pluginVersion);
|
|
1211
|
+
} catch (e) {
|
|
1212
|
+
log(
|
|
1213
|
+
`[fulgur] \u81EA\u52A8\u6E05\u7406 .vite \u7F13\u5B58\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u542F\u52A8\uFF1B\u5982\u9047\u8054\u90A6\u6A21\u5757 404 \u8BF7\u624B\u5DE5\u6267\u884C rm -rf ${viteDir}\uFF09\uFF1A${String(e.message ?? e)}`
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// src/runtime-code.gen.ts
|
|
1219
|
+
var runtime_code_gen_default = 'var Q=/^v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z.-]+)?$/;function k(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\\d+$/.test(l),w=/^\\d+$/.test(m);if(y&&w){let M=Number(l)-Number(m);if(M!==0)return M}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function T(t,n){let o=k(t),r=k(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function N(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!k(t)?.pre.length;let o=n.split(/\\s+-\\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!N(t,`>=${m}`)||!N(t,`<=${y}`))}let r=k(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(T(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(T(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=T(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgur:${n}] ${o}`),this.name="FulgurError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.4.2";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGUR_APP_CONFIG__=m)}function w(){return m}let M={},D=()=>{for(let e of o)try{e.init?.(M)}catch(i){console.warn("[fulgur] runtimePlugin init failed:",e.name,i)}},I=e=>t[e]??={};function $(e){try{M.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgur:error",{detail:e}))}function H(e="default"){return I(e),t}function K(e,i,a,u,E={}){let s=I(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=I(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||N(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(T).pop(),c.length>1||!f.includes(b)){let h=s[b],O=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let v=new g(R.SHARE_STRICT_VERSION,O,{shareKey:u,requiredVersion:d});throw $({remote:h.from,error:v}),v}console.warn(`[fulgur:MFU-010] ${O}`)}}else if(b=[...f].sort(T).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw $({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw $({remote:u,error:p}),p}let _=s[b];if(M.resolveShare){let p=await M.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function W(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let B=e=>new Promise(i=>setTimeout(i,e));function j(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgur] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return W(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw $({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return j(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return j(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let O=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-O),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(v){if(d=v,v?.code)throw v;h<b&&await B(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\\/\\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \\u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgur-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \\u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}\n`+p.join(`\n`),{remote:e.name})}j(c,e);let _=l.get(c);if(_&&_!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${_}", cannot re-init with "${a}"`);return typeof c.init=="function"&&await c.init(t),l.set(c,a),e.container=c,e.state="loaded",e.debug.status="loaded",u.fails=0,c})();e.containerPromise=E;try{return await E}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),u.fails+=1,u.fails>=ce&&(u.openUntil=Date.now()+le,u.fails=0),s instanceof g)throw $({remote:e.name,error:s}),s;if(s?.code)throw $({remote:e.name,error:s}),s;let c=new g(R.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw $({remote:e.name,error:c}),c}}function A(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:X(e.slice(i+1))}}function X(e){return e?e.startsWith(".")?e:`./${e}`:""}async function J(e,i){let{remote:a,module:u}=A(e);M.beforeLoadRemote?.({remote:a,module:u});let E=n.get(a);if(!E)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await C(E,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgur] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),$({remote:a,error:f}),await i.fallbackModule();throw f}if(!u)return s;let c=`${a}@${E.shareScope||"default"}#${u}`;r.has(c)||r.set(c,s.get(u).catch(f=>{throw r.delete(c),f instanceof g||f?.code?f:new g(R.REMOTE_LOAD_FAILED,`failed to load module "${u}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:u})}));let d;try{d=await r.get(c)}catch(f){if(i?.fallbackModule)return console.error(`[fulgur] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),$({remote:a,error:f}),await i.fallbackModule();throw f}if(M.afterLoadRemote?.({remote:a,module:u,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(R.EMPTY_EXPORTS,`no exports: ${u} @ ${a}`,{remote:a,module:u});$({remote:a,error:f}),console.error(`[fulgur:MFU-009] ${f.message}`)}return d}function Y(e){let i=n.get(e);if(!i)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function Z(e,i={}){let{remote:a}=A(e),u=n.get(a);if(!u)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let E=i.mode??"preload",s=(c,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${c}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=c,E==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(u.manifestUrl){let c=await fetch(u.manifestUrl,{cache:"no-cache"});if(c.ok){let d=await c.json();s(u.entry,"modulepreload");let f=d.exposes??{};for(let b of Object.keys(f)){let _=f[b];_?.file&&s(new URL(_.file,u.entry).href,"modulepreload");for(let p of _?.css??[])s(new URL(p,u.entry).href,"style")}return}}s(u.entry,"modulepreload")}catch(c){$({remote:a,error:new g(R.PRELOAD_FAILED,String(c?.message??c),{remote:a})})}}return typeof window>"u"||(window.__FULGUR_SCOPE__=t,window.__FULGUR_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:H,registerShare:K,registerRemotes:x,registerRemote:q,registerPlugins:G,loadShare:z,loadRemote:J,getContainer:Y,preloadRemote:Z,parseSpec:A,provideFulgurAppConfig:y,getFulgurAppConfig:w}}var V=globalThis,S=V.__FULGUR_RUNTIME__??fe();V.__FULGUR_RUNTIME__=S;try{Object.freeze(S)}catch{}var Re=F;function Se(){return S}var we=S.initSharing,be=S.registerShare,_e=S.registerRemotes,$e=S.registerRemote,Me=S.registerPlugins,ve=S.loadShare,Te=S.loadRemote,Oe=S.getContainer,ke=S.preloadRemote,Ne=S.shareScopeMap,Ie=S.provideFulgurAppConfig,je=S.getFulgurAppConfig;function Ae(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var xe=S;export{xe as default,Oe as getContainer,je as getFulgurAppConfig,Se as getRuntime,we as initSharing,Te as loadRemote,ve as loadShare,ke as preloadRemote,Ie as provideFulgurAppConfig,Me as registerPlugins,$e as registerRemote,_e as registerRemotes,be as registerShare,S as runtime,Ne as shareScopeMap,Ae as unwrapDefault,Re as version};\n';
|
|
1220
|
+
|
|
1221
|
+
// src/index.ts
|
|
1222
|
+
function readRuntimeCode() {
|
|
1223
|
+
return runtime_code_gen_default;
|
|
1224
|
+
}
|
|
1225
|
+
function normalizeBase(base) {
|
|
1226
|
+
if (!base || base === "/") return "/";
|
|
1227
|
+
return base.endsWith("/") ? base : `${base}/`;
|
|
1228
|
+
}
|
|
1229
|
+
var FULGUR_STUB_NAMESPACE = "fulgur-opt-stub";
|
|
1230
|
+
var nsExportCache = /* @__PURE__ */ new Map();
|
|
1231
|
+
function enumerateCjsExports(packageName, appRoot) {
|
|
1232
|
+
const cached = nsExportCache.get(packageName);
|
|
1233
|
+
if (cached) return cached;
|
|
1234
|
+
let names = [];
|
|
1235
|
+
try {
|
|
1236
|
+
const appRequire = (0, import_node_module2.createRequire)(import_node_path5.default.join(appRoot, "package.json"));
|
|
1237
|
+
const mod = appRequire(packageName);
|
|
1238
|
+
names = Object.keys(mod);
|
|
1239
|
+
} catch {
|
|
1240
|
+
console.warn(
|
|
1241
|
+
`[fulgur] cannot enumerate CJS exports of shared package "${packageName}" for the optimize-deps facade; the facade will export default only. If consumers destructure named exports from it, add this package to optimizeDeps.exclude to serve it through the transform pipeline instead.`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
nsExportCache.set(packageName, names);
|
|
1245
|
+
return names;
|
|
1246
|
+
}
|
|
1247
|
+
function injectInitScript(html, scriptSrc) {
|
|
1248
|
+
const tag = `<script type="module" src="${scriptSrc}"></script>`;
|
|
1249
|
+
const moduleScriptRe = /<script[^>]+type=["']module["'][^>]*>/;
|
|
1250
|
+
const m = moduleScriptRe.exec(html);
|
|
1251
|
+
if (m) {
|
|
1252
|
+
return html.slice(0, m.index) + tag + html.slice(m.index);
|
|
1253
|
+
}
|
|
1254
|
+
if (html.includes("</head>")) {
|
|
1255
|
+
return html.replace("</head>", `${tag}
|
|
1256
|
+
</head>`);
|
|
1257
|
+
}
|
|
1258
|
+
return tag + html;
|
|
1259
|
+
}
|
|
1260
|
+
function federation(options) {
|
|
1261
|
+
const warnedUnknownPrefixes = /* @__PURE__ */ new Set();
|
|
1262
|
+
let remoteSchemaPromise = null;
|
|
1263
|
+
const state = {
|
|
1264
|
+
command: "serve",
|
|
1265
|
+
base: "/",
|
|
1266
|
+
exposeAbsPaths: {},
|
|
1267
|
+
exposeFiles: {},
|
|
1268
|
+
resolvedSharedPaths: /* @__PURE__ */ new Map(),
|
|
1269
|
+
entryAbsPaths: /* @__PURE__ */ new Set(),
|
|
1270
|
+
entryInitInjected: /* @__PURE__ */ new Set()
|
|
1271
|
+
};
|
|
1272
|
+
const pre = {
|
|
1273
|
+
name: "fulgur:core",
|
|
1274
|
+
enforce: "pre",
|
|
1275
|
+
async config(userConfig, env) {
|
|
1276
|
+
state.command = env.command;
|
|
1277
|
+
const root = import_node_path5.default.resolve(userConfig.root ?? process.cwd());
|
|
1278
|
+
const normalized = normalizeOptions(options, root, env.command);
|
|
1279
|
+
state.normalized = normalized;
|
|
1280
|
+
const extra = {};
|
|
1281
|
+
if (env.command === "serve" && normalized.exposes.length > 0 && normalized.devSharedSelf) {
|
|
1282
|
+
const aliasToShare = /* @__PURE__ */ new Map();
|
|
1283
|
+
for (const s of normalized.shared) {
|
|
1284
|
+
if (s.import === false) continue;
|
|
1285
|
+
for (const a of s.aliases) if (!a.includes("/")) aliasToShare.set(a, s);
|
|
1286
|
+
}
|
|
1287
|
+
if (aliasToShare.size > 0) {
|
|
1288
|
+
const escapeRe = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1289
|
+
const filter = new RegExp(`^(${[...aliasToShare.keys()].map(escapeRe).join("|")})$`);
|
|
1290
|
+
const devBase = normalizeBase(userConfig.base ?? "/");
|
|
1291
|
+
const sharedExternal = {
|
|
1292
|
+
name: "fulgur:optimize-shared-external",
|
|
1293
|
+
setup(build) {
|
|
1294
|
+
const facadeUrlFor = (shareKey) => `${devBase}@id/__x00__virtual:fulgur-shared-ns:${shareKey}?import`;
|
|
1295
|
+
const b = build;
|
|
1296
|
+
b.onResolve({ filter: /fulgur-shared-ns:/ }, (args) => ({ path: args.path, external: true }));
|
|
1297
|
+
b.onResolve({ filter }, (args) => {
|
|
1298
|
+
if (args.kind === "entry-point" || args.kind === "entry-point-render") return null;
|
|
1299
|
+
const s = aliasToShare.get(args.path);
|
|
1300
|
+
if (!s) return null;
|
|
1301
|
+
return { path: `fulgur-stub:${s.shareKey}`, namespace: FULGUR_STUB_NAMESPACE };
|
|
1302
|
+
});
|
|
1303
|
+
b.onLoad({ filter: /^fulgur-stub:/, namespace: FULGUR_STUB_NAMESPACE }, (args) => {
|
|
1304
|
+
const shareKey = args.path.slice("fulgur-stub:".length);
|
|
1305
|
+
const url = facadeUrlFor(shareKey);
|
|
1306
|
+
return {
|
|
1307
|
+
contents: `export * from ${JSON.stringify(url)};
|
|
1308
|
+
export { default } from ${JSON.stringify(url)};
|
|
1309
|
+
`,
|
|
1310
|
+
loader: "js"
|
|
1311
|
+
};
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
extra.optimizeDeps = {
|
|
1316
|
+
esbuildOptions: { plugins: [sharedExternal] }
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (env.command === "serve") {
|
|
1321
|
+
syncViteCacheMarker(root, normalized.pluginVersion, (msg) => console.warn(msg));
|
|
1322
|
+
extra.server = {
|
|
1323
|
+
...userConfig.server ?? {},
|
|
1324
|
+
cors: userConfig.server?.cors ?? true
|
|
1325
|
+
};
|
|
1326
|
+
} else {
|
|
1327
|
+
const userTarget = userConfig.build?.target;
|
|
1328
|
+
if (userTarget) {
|
|
1329
|
+
if (/es20(0\d|1\d|20|21)/.test(String(userTarget))) {
|
|
1330
|
+
normalized.warnings.push(
|
|
1331
|
+
`build.target="${String(userTarget)}" does not support top-level await; fulgur requires es2022 or higher.`
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
} else {
|
|
1335
|
+
;
|
|
1336
|
+
extra.build = { target: "es2022" };
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
for (const w of normalized.warnings) console.warn(`[fulgur] ${w}`);
|
|
1340
|
+
return extra;
|
|
1341
|
+
},
|
|
1342
|
+
configResolved(resolved) {
|
|
1343
|
+
state.base = normalizeBase(resolved.base);
|
|
1344
|
+
if (resolved.build?.ssr) {
|
|
1345
|
+
console.warn("[fulgur] SSR builds are not supported in this version; plugin hooks disabled.");
|
|
1346
|
+
}
|
|
1347
|
+
const n0 = state.normalized;
|
|
1348
|
+
const optimize = resolved.optimizeDeps;
|
|
1349
|
+
if (n0 && optimize) {
|
|
1350
|
+
const include = optimize.include ?? [];
|
|
1351
|
+
const exclude = optimize.exclude ?? [];
|
|
1352
|
+
const excl = (pkg) => exclude.some((e) => e === pkg || e.startsWith(pkg + "/") || e.startsWith(pkg + ">"));
|
|
1353
|
+
const incl = (pkg) => include.some((e) => e === pkg || e.startsWith(pkg + "/") || e.startsWith(pkg + ">"));
|
|
1354
|
+
const KNOWN_UMD = ["@smallwei/avue"];
|
|
1355
|
+
for (const pkg of KNOWN_UMD) {
|
|
1356
|
+
if (n0.pkgDependencies[pkg] && !incl(pkg) && !excl(pkg)) {
|
|
1357
|
+
console.warn(
|
|
1358
|
+
formatFulgurDiagnostic({
|
|
1359
|
+
code: "DEV-004",
|
|
1360
|
+
symptom: `UMD-only \u4F9D\u8D56 "${pkg}" \u4E0D\u5728 optimizeDeps.include`,
|
|
1361
|
+
cause: "UMD \u5305\u53EA\u80FD\u7ECF\u9884\u6784\u5EFA\u6D88\u8D39\uFF1B\u4E0D\u58F0\u660E\u4F1A\u88AB\u88F8 CJS \u670D\u52A1\u6216\u5185\u8054\u672C\u5730 vue\uFF08\u9875\u9762\u7A7A\u767D/\u53CC\u5B9E\u4F8B\uFF09",
|
|
1362
|
+
fix: `\u52A0\u5165 optimizeDeps.include: ['${pkg}']`,
|
|
1363
|
+
details: { pkg }
|
|
1364
|
+
})
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
},
|
|
1370
|
+
resolveId(source) {
|
|
1371
|
+
let s = source;
|
|
1372
|
+
if (s.startsWith("/@id/")) s = s.slice(5);
|
|
1373
|
+
if (s.startsWith("__x00__")) s = "\0" + s.slice(7);
|
|
1374
|
+
const q = s.indexOf("?");
|
|
1375
|
+
const bare = q === -1 ? s : s.slice(0, q);
|
|
1376
|
+
const query = q === -1 ? "" : s.slice(q);
|
|
1377
|
+
const bareClean = bare.startsWith("\0") ? bare.slice(1) : bare;
|
|
1378
|
+
const n = state.normalized;
|
|
1379
|
+
if (n && !bareClean.startsWith("\0") && !bareClean.startsWith(".") && !bareClean.startsWith("/") && !bareClean.startsWith("virtual:")) {
|
|
1380
|
+
const slash = bareClean.indexOf("/");
|
|
1381
|
+
if (slash > 0) {
|
|
1382
|
+
const prefix = bareClean.slice(0, slash);
|
|
1383
|
+
const known = n.remotes.some((r) => r.key === prefix) || n.shared.some((sh) => sh.aliases.includes(prefix) || sh.aliases.some((a) => a.endsWith("/") && prefix.startsWith(a)));
|
|
1384
|
+
if (!known && !warnedUnknownPrefixes.has(prefix)) {
|
|
1385
|
+
warnedUnknownPrefixes.add(prefix);
|
|
1386
|
+
console.warn(
|
|
1387
|
+
`[fulgur] "${source}" uses prefix "${prefix}/", which is not in federation({ remotes }) or shared. If "${prefix}" is a federated remote, add it: remotes: { '${prefix}': '<url>' }. (Ignore this if it is a plain npm package.)`
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
if (bareClean === RUNTIME_VIRTUAL_ID) return RESOLVED.runtime;
|
|
1393
|
+
if (bareClean === RUNTIME_PROXY_VIRTUAL_ID) return RESOLVED.runtimeProxy;
|
|
1394
|
+
if (bareClean === INIT_VIRTUAL_ID) return RESOLVED.init;
|
|
1395
|
+
if (bareClean === "virtual:fulgur-remote-schema") return bareClean;
|
|
1396
|
+
if (bareClean === "virtual:fulgur-provides") return RESOLVED.provides;
|
|
1397
|
+
if (bareClean === "virtual:fulgur-remote-entry") return RESOLVED.remoteEntry;
|
|
1398
|
+
if (bareClean.startsWith(SHARED_NS_FACADE_PREFIX)) {
|
|
1399
|
+
return RESOLVED.sharedNsFacade(bareClean.slice(SHARED_NS_FACADE_PREFIX.length)) + query;
|
|
1400
|
+
}
|
|
1401
|
+
if (bareClean.startsWith("virtual:fulgur-cjs-ns:")) {
|
|
1402
|
+
return RESOLVED.sharedNsFacade(bareClean.slice("virtual:fulgur-cjs-ns:".length)) + query;
|
|
1403
|
+
}
|
|
1404
|
+
if (bareClean.startsWith("virtual:fulgur-shared:")) {
|
|
1405
|
+
const body = bareClean.slice("virtual:fulgur-shared:".length);
|
|
1406
|
+
return RESOLVED.sharedFacade(body) + query;
|
|
1407
|
+
}
|
|
1408
|
+
return null;
|
|
1409
|
+
},
|
|
1410
|
+
load(id) {
|
|
1411
|
+
const raw = id.startsWith("\0") ? id.slice(1) : id;
|
|
1412
|
+
const q = raw.indexOf("?");
|
|
1413
|
+
const clean = q === -1 ? raw : raw.slice(0, q);
|
|
1414
|
+
if (clean === "virtual:fulgur-runtime") return readRuntimeCode();
|
|
1415
|
+
if (clean === RESOLVED.runtimeProxy || clean === RUNTIME_PROXY_VIRTUAL_ID) return genRuntimeProxyModule();
|
|
1416
|
+
if (clean === "virtual:fulgur-init" && state.normalized) {
|
|
1417
|
+
return genInitModule(state.normalized, state.command);
|
|
1418
|
+
}
|
|
1419
|
+
if (clean === "virtual:fulgur-provides" && state.normalized) {
|
|
1420
|
+
return genDevProvides(state.normalized);
|
|
1421
|
+
}
|
|
1422
|
+
if (clean === "virtual:fulgur-remote-schema" && state.normalized) {
|
|
1423
|
+
if (state.command === "build") return genEmptyRemoteSchemaModule();
|
|
1424
|
+
remoteSchemaPromise ??= probeRemotesAndBuildSchema(state.normalized).then(
|
|
1425
|
+
(schema) => `export default ${JSON.stringify(schema)}`
|
|
1426
|
+
);
|
|
1427
|
+
return remoteSchemaPromise;
|
|
1428
|
+
}
|
|
1429
|
+
if (clean === "virtual:fulgur-remote-entry" && state.normalized) {
|
|
1430
|
+
return genBuildRemoteEntry(state.normalized, state.exposeAbsPaths);
|
|
1431
|
+
}
|
|
1432
|
+
if (clean.startsWith(SHARED_NS_FACADE_PREFIX) && state.normalized) {
|
|
1433
|
+
const body = clean.slice(SHARED_NS_FACADE_PREFIX.length);
|
|
1434
|
+
const item = state.normalized.shared.find((x) => x.shareKey === body);
|
|
1435
|
+
if (!item || item.import === false) return null;
|
|
1436
|
+
const names = enumerateCjsExports(item.import, state.normalized.root);
|
|
1437
|
+
if (item.shareKey === "vue") {
|
|
1438
|
+
for (const compat of ["isVue2", "isVue3", "Vue2", "set", "del"]) {
|
|
1439
|
+
if (!names.includes(compat)) names.push(compat);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return genSharedNsFacade(item, serializeShareCallForFacade(item), names);
|
|
1443
|
+
}
|
|
1444
|
+
if (clean.startsWith("virtual:fulgur-shared:") && state.normalized) {
|
|
1445
|
+
const body = clean.slice("virtual:fulgur-shared:".length);
|
|
1446
|
+
const f = raw.indexOf("?f=");
|
|
1447
|
+
if (f === -1) {
|
|
1448
|
+
const item2 = state.normalized.shared.find((x) => x.shareKey === body);
|
|
1449
|
+
if (item2 && item2.import !== false) {
|
|
1450
|
+
const names = /^(\.|\/)/.test(item2.import) ? [] : enumerateCjsExports(item2.import, state.normalized.root);
|
|
1451
|
+
return genSharedFacade(item2.import, names);
|
|
1452
|
+
}
|
|
1453
|
+
return null;
|
|
1454
|
+
}
|
|
1455
|
+
const entry = getFacadeEntry(raw.slice(f + 3));
|
|
1456
|
+
if (!entry) return null;
|
|
1457
|
+
if (entry.remoteName && entry.exposeName) {
|
|
1458
|
+
return genRemoteBindingFacade(entry.remoteName + "/" + entry.exposeName, entry.bindings);
|
|
1459
|
+
}
|
|
1460
|
+
const item = state.normalized.shared.find((x) => x.shareKey === entry.shareKey);
|
|
1461
|
+
if (!item || item.import === false) return null;
|
|
1462
|
+
const opts = [
|
|
1463
|
+
"shareScope: " + JSON.stringify(item.shareScope),
|
|
1464
|
+
"shareKey: " + JSON.stringify(item.shareKey),
|
|
1465
|
+
...item.requiredVersion !== false ? ["requiredVersion: " + JSON.stringify(item.requiredVersion)] : [],
|
|
1466
|
+
...item.singleton ? ["singleton: true"] : [],
|
|
1467
|
+
...item.strictVersion ? ["strictVersion: true"] : [],
|
|
1468
|
+
"fallback: () => import(" + JSON.stringify(item.import) + ")"
|
|
1469
|
+
];
|
|
1470
|
+
const call = "__fulgur_loadShare(" + JSON.stringify(item.shareKey) + ", { " + opts.join(", ") + " })";
|
|
1471
|
+
return genBindingFacade(item, entry.bindings, call);
|
|
1472
|
+
}
|
|
1473
|
+
return null;
|
|
1474
|
+
},
|
|
1475
|
+
async transform(code, id) {
|
|
1476
|
+
if (!state.normalized) return null;
|
|
1477
|
+
const clean = id.split("?")[0];
|
|
1478
|
+
if (state.command === "build" && state.entryAbsPaths.has(clean) && !state.entryInitInjected.has(clean)) {
|
|
1479
|
+
state.entryInitInjected.add(clean);
|
|
1480
|
+
const initCode = genInitModule(state.normalized, state.command);
|
|
1481
|
+
return { code: `${initCode}
|
|
1482
|
+
${code}`, map: null };
|
|
1483
|
+
}
|
|
1484
|
+
const isPureRemoteBuild = state.normalized.exposes.length > 0 && state.normalized.remotes.length === 0;
|
|
1485
|
+
const allowNodeModules = state.normalized.devSharedSelf || isPureRemoteBuild;
|
|
1486
|
+
if (/\.vue(\?|$)/.test(id)) {
|
|
1487
|
+
if (state.command === "build" && /type=script/.test(id)) {
|
|
1488
|
+
return transformModule(code, id, {
|
|
1489
|
+
options: state.normalized,
|
|
1490
|
+
rewriteShared: true,
|
|
1491
|
+
allowNodeModules,
|
|
1492
|
+
cjsRequireRewrite: true
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
return null;
|
|
1496
|
+
}
|
|
1497
|
+
if (!isTransformableId(id, allowNodeModules)) return null;
|
|
1498
|
+
return transformModule(code, id, {
|
|
1499
|
+
options: state.normalized,
|
|
1500
|
+
// build:全量改写;dev:默认仅纯 remote 改写 shared(被宿主消费的组件需协商到宿主实例),
|
|
1501
|
+
// 双角色宿主可显式 devSharedSelf: true 参与协商
|
|
1502
|
+
rewriteShared: state.command === "build" || state.normalized.devSharedSelf,
|
|
1503
|
+
allowNodeModules,
|
|
1504
|
+
cjsRequireRewrite: state.command === "build"
|
|
1505
|
+
});
|
|
1506
|
+
},
|
|
1507
|
+
transformIndexHtml: {
|
|
1508
|
+
order: "pre",
|
|
1509
|
+
handler(html) {
|
|
1510
|
+
if (!state.normalized) return html;
|
|
1511
|
+
if (state.command === "serve") {
|
|
1512
|
+
const src = `/@id/__x00__${INIT_VIRTUAL_ID}?import`;
|
|
1513
|
+
return injectInitScript(html, src);
|
|
1514
|
+
}
|
|
1515
|
+
const moduleScriptRe = /<script[^>]+type=["']module["'][^>]*>/g;
|
|
1516
|
+
const srcRe = /src=["']([^"']+)["']/;
|
|
1517
|
+
for (const tag of html.match(moduleScriptRe) ?? []) {
|
|
1518
|
+
const src = srcRe.exec(tag)?.[1];
|
|
1519
|
+
if (!src || src.startsWith("http") || src.startsWith("//")) continue;
|
|
1520
|
+
const abs = import_node_path5.default.resolve(state.normalized.root, src.startsWith("/") ? src.slice(1) : src);
|
|
1521
|
+
state.entryAbsPaths.add(abs);
|
|
1522
|
+
}
|
|
1523
|
+
return html;
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
async buildStart() {
|
|
1527
|
+
if (state.command !== "build" || !state.normalized) return;
|
|
1528
|
+
const n = state.normalized;
|
|
1529
|
+
if (n.exposes.length > 0) {
|
|
1530
|
+
for (const e of n.exposes) {
|
|
1531
|
+
const resolved = await this.resolve(e.import, import_node_path5.default.join(n.root, "index.html"));
|
|
1532
|
+
if (resolved) {
|
|
1533
|
+
state.exposeAbsPaths[e.import] = resolved.id.split("?")[0];
|
|
1534
|
+
} else {
|
|
1535
|
+
this.error(`[fulgur] expose "${e.import}" could not be resolved from ${n.root}`);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
this.emitFile({
|
|
1539
|
+
type: "chunk",
|
|
1540
|
+
id: "virtual:fulgur-remote-entry",
|
|
1541
|
+
fileName: n.filename.replace(/^\//, ""),
|
|
1542
|
+
preserveSignature: "allow-extension"
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
},
|
|
1546
|
+
async generateBundle(_opts, bundle) {
|
|
1547
|
+
const n = state.normalized;
|
|
1548
|
+
if (state.command !== "build" || !n || n.exposes.length === 0 || !n.manifest) return;
|
|
1549
|
+
const facadeToChunk = {};
|
|
1550
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
1551
|
+
if (chunk.type !== "chunk") continue;
|
|
1552
|
+
const facade = chunk.facadeModuleId?.split("?")[0];
|
|
1553
|
+
if (facade) {
|
|
1554
|
+
const viteMeta = chunk.viteMetadata;
|
|
1555
|
+
facadeToChunk[facade] = {
|
|
1556
|
+
file: fileName,
|
|
1557
|
+
css: viteMeta ? [...viteMeta.importedCss ?? []] : []
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
for (const e of n.exposes) {
|
|
1562
|
+
const abs = state.exposeAbsPaths[e.import];
|
|
1563
|
+
const hit = abs ? facadeToChunk[abs] : void 0;
|
|
1564
|
+
if (hit) state.exposeFiles[e.name] = hit;
|
|
1565
|
+
}
|
|
1566
|
+
const entryChunkName = Object.keys(bundle).find((k) => bundle[k].type === "chunk" && k === n.filename);
|
|
1567
|
+
const manifest = genProdManifest(n, state.exposeFiles, entryChunkName ?? n.filename);
|
|
1568
|
+
this.emitFile({
|
|
1569
|
+
type: "asset",
|
|
1570
|
+
fileName: "fulgur-manifest.json",
|
|
1571
|
+
source: JSON.stringify(manifest, null, 2)
|
|
1572
|
+
});
|
|
1573
|
+
},
|
|
1574
|
+
configureServer(server) {
|
|
1575
|
+
const n = state.normalized;
|
|
1576
|
+
if (!n) return;
|
|
1577
|
+
warmResolvedSharedPaths(server);
|
|
1578
|
+
if (n.exposes.length > 0 || n.remotes.length > 0) {
|
|
1579
|
+
console.warn(
|
|
1580
|
+
formatFulgurDiagnostic({
|
|
1581
|
+
code: "DEV-010",
|
|
1582
|
+
symptom: 'dev \u51B7\u542F\u52A8\u9884\u6784\u5EFA\u7A97\u53E3\uFF1A\u9996\u6B21\u542F\u52A8\u6216\u6E05 node_modules/.vite \u540E\u9996\u8F6E 30~60s \u5185\uFF0C\u8054\u90A6\u6A21\u5757\u8BF7\u6C42\u53EF\u80FD\u51FA\u73B0\u77AC\u65F6 504 / "ce" / Outdated Optimize Dep',
|
|
1583
|
+
cause: "vite \u4F9D\u8D56\u9884\u6784\u5EFA\uFF08\u542B fulgur \u5916\u90E8\u5316\u6869\uFF09\u5C1A\u672A\u5C31\u7EEA\uFF0C\u5C5E\u9884\u6784\u5EFA\u6682\u6001\u800C\u975E\u56DE\u5F52\uFF1B\u9996\u8F6E\u7ED3\u675F\u540E\u81EA\u884C\u6062\u590D",
|
|
1584
|
+
fix: "\u5148\u771F\u5B9E\u6253\u5F00\u4E00\u6B21\u9875\u9762\u9884\u70ED\uFF08\u7B49\u5230\u7F51\u7EDC\u7A7A\u95F2\uFF09\uFF0C\u518D\u505A\u9A8C\u6536\u65AD\u8A00\u6216\u4EBA\u5DE5\u5224\u65AD\uFF1B\u91CD\u590D\u51FA\u73B0\u624D\u6309 DEV-009 \u6E05\u7F13\u5B58\u6392\u67E5"
|
|
1585
|
+
})
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
if (n.exposes.length > 0) {
|
|
1589
|
+
const baseNorm = state.base;
|
|
1590
|
+
server.middlewares.use((req, res, next) => {
|
|
1591
|
+
const raw = (req.url ?? "").split("?")[0];
|
|
1592
|
+
const stripped = raw.startsWith(baseNorm) ? `/${raw.slice(baseNorm.length)}` : raw;
|
|
1593
|
+
if (stripped === "/@fulgur-entry.js" || stripped === "/@fulgur-manifest.json") {
|
|
1594
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1595
|
+
res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS");
|
|
1596
|
+
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
1597
|
+
if (req.method === "OPTIONS") {
|
|
1598
|
+
res.statusCode = 204;
|
|
1599
|
+
res.end();
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
if (stripped === "/@fulgur-entry.js") {
|
|
1603
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
1604
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
1605
|
+
res.end(genDevRemoteEntry(n, baseNorm));
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
1609
|
+
res.end(JSON.stringify(genDevManifest(n, baseNorm)));
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
next();
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
if (n.remotes.length > 0 && n.dts) {
|
|
1616
|
+
server.httpServer?.once("listening", () => {
|
|
1617
|
+
void generateDevTypes(n, server);
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
if (n.remotes.length > 0) {
|
|
1621
|
+
server.httpServer?.once("listening", () => {
|
|
1622
|
+
setTimeout(() => {
|
|
1623
|
+
remoteSchemaPromise ??= probeRemotesAndBuildSchema(n).then(
|
|
1624
|
+
(schema) => `export default ${JSON.stringify(schema)}`
|
|
1625
|
+
);
|
|
1626
|
+
}, 5e3);
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
return () => {
|
|
1630
|
+
server.middlewares.use((req, res, next) => {
|
|
1631
|
+
const url = req.url ?? "";
|
|
1632
|
+
if (req.method === "GET" && url.includes("fulgur")) {
|
|
1633
|
+
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1634
|
+
res.end(
|
|
1635
|
+
formatFulgurDiagnostic({
|
|
1636
|
+
code: "DEV-009",
|
|
1637
|
+
symptom: `\u8054\u90A6\u6A21\u5757\u8BF7\u6C42 404\uFF1A${url.slice(0, 120)}`,
|
|
1638
|
+
cause: "node_modules/.vite \u9884\u6784\u5EFA\u7F13\u5B58\u4E0E\u5F53\u524D\u63D2\u4EF6\u4EA7\u7269\u4E0D\u4E00\u81F4\uFF08immutable \u7F13\u5B58\u6309 ?f= \u7B7E\u540D\u957F\u671F\u6301\u6709\uFF0C\u63D2\u4EF6 dist \u66F4\u65B0\u540E\u65E7\u7B7E\u540D\u5FC5 404\uFF09",
|
|
1639
|
+
fix: "rm -rf node_modules/.vite \u540E\u91CD\u542F dev server\uFF0C\u5E76\u66F4\u6362\u5168\u65B0\u6D4F\u89C8\u5668 profile\uFF08\u6D4F\u89C8\u5668\u4E5F\u6301\u6709\u65E7\u7F13\u5B58\uFF09"
|
|
1640
|
+
})
|
|
1641
|
+
);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
next();
|
|
1645
|
+
});
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1649
|
+
const post = {
|
|
1650
|
+
name: "fulgur:vue-post",
|
|
1651
|
+
enforce: "post",
|
|
1652
|
+
async transform(code, id) {
|
|
1653
|
+
if (!state.normalized) return null;
|
|
1654
|
+
const clean = id.split("?")[0];
|
|
1655
|
+
if (state.command === "serve" && code.includes("virtual:fulgur-runtime") && !code.includes("virtual:fulgur-runtime-proxy") && isExposeTargetFile(clean, state.normalized.root, state.normalized.exposes)) {
|
|
1656
|
+
return { code: code.split("virtual:fulgur-runtime").join("virtual:fulgur-runtime-proxy"), map: null };
|
|
1657
|
+
}
|
|
1658
|
+
if (code.includes("virtual:fulgur-runtime")) return null;
|
|
1659
|
+
if (state.command === "build" && !/\.vue(\?|$)/.test(id)) return null;
|
|
1660
|
+
if (/type=(style|template)/.test(id)) return null;
|
|
1661
|
+
const isJsLike = /\.(m|c)?[jt]sx?$/.test(clean) || clean.endsWith(".vue");
|
|
1662
|
+
if (!isJsLike) return null;
|
|
1663
|
+
const devRewriteAll = state.command === "serve" && state.normalized.devSharedSelf;
|
|
1664
|
+
if (clean.includes("node_modules") && !id.includes(".vite/deps") && !devRewriteAll) return null;
|
|
1665
|
+
const rewriteShared = state.command === "build" || state.normalized.devSharedSelf;
|
|
1666
|
+
const remapSpecifier = (spec) => {
|
|
1667
|
+
if (state.normalized.shared.some((s) => s.aliases.includes(spec))) return spec;
|
|
1668
|
+
const depMatch = spec.match(/\/node_modules\/\.vite\/deps\/([^/?]+)\.js/);
|
|
1669
|
+
if (depMatch) {
|
|
1670
|
+
const depName = depMatch[1];
|
|
1671
|
+
return state.normalized.shared.some((s) => s.aliases.includes(depName)) ? depName : null;
|
|
1672
|
+
}
|
|
1673
|
+
const cleanSpec = spec.startsWith("/@fs/") ? spec.slice(5) : spec;
|
|
1674
|
+
const cleanPath = cleanSpec.split("?")[0];
|
|
1675
|
+
if (!cleanPath.startsWith("/")) return null;
|
|
1676
|
+
return state.resolvedSharedPaths.get(cleanPath) ?? null;
|
|
1677
|
+
};
|
|
1678
|
+
return transformModule(code, id, { options: state.normalized, remapSpecifier, rewriteShared });
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
function warmResolvedSharedPaths(server) {
|
|
1682
|
+
const n = state.normalized;
|
|
1683
|
+
if (!n) return;
|
|
1684
|
+
for (const s of n.shared) {
|
|
1685
|
+
if (s.import === false) continue;
|
|
1686
|
+
void server.pluginContainer.resolveId(s.import, import_node_path5.default.join(n.root, "index.html")).then((r) => {
|
|
1687
|
+
if (r?.id) state.resolvedSharedPaths.set(r.id.split("?")[0], s.configKey);
|
|
1688
|
+
}).catch(() => {
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return [pre, post];
|
|
1693
|
+
}
|
|
1694
|
+
var index_default = federation;
|
|
1695
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1696
|
+
0 && (module.exports = {
|
|
1697
|
+
federation
|
|
1698
|
+
});
|