@fulgurjs/federation 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +68 -9
- package/client.d.ts +17 -1
- package/dist/{chunk-UDLQALHC.js → chunk-RP6HNIQT.js} +1 -1
- package/dist/cli.js +147 -3
- package/dist/index.cjs +536 -86
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +538 -86
- package/dist/runtime.js +3 -3
- package/dist/vue.cjs +170 -40
- package/dist/vue.js +171 -41
- package/docs/P5-vite7-8/345/205/274/345/256/271/347/237/251/351/230/265.md +4 -2
- package/package.json +6 -6
package/dist/vue.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
ErrorCodes,
|
|
3
3
|
FgError
|
|
4
4
|
} from "./chunk-3VBYOVH4.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-RP6HNIQT.js";
|
|
6
6
|
|
|
7
7
|
// src/vue.ts
|
|
8
8
|
import { defineAsyncComponent, defineComponent, h } from "vue";
|
|
@@ -210,11 +210,15 @@ var DEFAULT_TIMEOUT = 15e3;
|
|
|
210
210
|
var DEFAULT_RETRIES = 2;
|
|
211
211
|
var BREAKER_THRESHOLD = 5;
|
|
212
212
|
var BREAKER_RESET_MS = 3e4;
|
|
213
|
+
var BACKOFF_MAX_MS = 4e3;
|
|
214
|
+
var BACKOFF_JITTER = 0.2;
|
|
213
215
|
function createRuntime() {
|
|
214
|
-
const shareScopeMap2 =
|
|
216
|
+
const shareScopeMap2 = /* @__PURE__ */ Object.create(null);
|
|
215
217
|
const remotes = /* @__PURE__ */ new Map();
|
|
216
218
|
const plugins = [];
|
|
217
219
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
220
|
+
const remoteManifests = /* @__PURE__ */ new Map();
|
|
221
|
+
const stylesheetLoads = /* @__PURE__ */ new WeakMap();
|
|
218
222
|
const containerInitScopes = /* @__PURE__ */ new WeakMap();
|
|
219
223
|
const hooks = {};
|
|
220
224
|
const applyPlugins = () => {
|
|
@@ -226,7 +230,7 @@ function createRuntime() {
|
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
};
|
|
229
|
-
const getScope = (name) => shareScopeMap2[name] ??=
|
|
233
|
+
const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
|
|
230
234
|
function emitError(info) {
|
|
231
235
|
try {
|
|
232
236
|
hooks.onRemoteError?.(info);
|
|
@@ -242,22 +246,49 @@ function createRuntime() {
|
|
|
242
246
|
}
|
|
243
247
|
function registerShare2(scopeName, name, version, get, opts = {}) {
|
|
244
248
|
const scope = getScope(scopeName || "default");
|
|
245
|
-
const
|
|
246
|
-
|
|
249
|
+
const own = Object.hasOwn(scope, name) ? scope[name] : void 0;
|
|
250
|
+
let byName = own && typeof own === "object" ? own : void 0;
|
|
251
|
+
if (!byName) {
|
|
252
|
+
byName = /* @__PURE__ */ Object.create(null);
|
|
253
|
+
scope[name] = byName;
|
|
254
|
+
}
|
|
255
|
+
if (Object.hasOwn(byName, version) && byName[version]) {
|
|
247
256
|
return;
|
|
248
257
|
}
|
|
249
258
|
byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
|
|
250
259
|
}
|
|
260
|
+
function assertRemoteParams(r) {
|
|
261
|
+
const check = (v, name, int10) => {
|
|
262
|
+
if (v === void 0) return;
|
|
263
|
+
const ok = typeof v === "number" && (int10 ? Number.isInteger(v) && v >= 0 && v <= 10 : Number.isFinite(v) && v > 0);
|
|
264
|
+
if (!ok) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`[fulgurjs] registerRemote("${r.name}"): ${name} must be a ${int10 ? "integer 0..10" : "finite positive number"}, got ${String(v)}`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
check(r.timeout, "timeout");
|
|
271
|
+
check(r.retries, "retries", true);
|
|
272
|
+
if (r.breaker) {
|
|
273
|
+
check(r.breaker.threshold, "breaker.threshold");
|
|
274
|
+
check(r.breaker.resetMs, "breaker.resetMs");
|
|
275
|
+
}
|
|
276
|
+
}
|
|
251
277
|
function registerRemotes2(list) {
|
|
252
278
|
for (const r of list) {
|
|
279
|
+
assertRemoteParams(r);
|
|
253
280
|
const prev = remotes.get(r.name);
|
|
254
281
|
remotes.set(r.name, {
|
|
255
282
|
timeout: DEFAULT_TIMEOUT,
|
|
256
283
|
retries: DEFAULT_RETRIES,
|
|
257
284
|
...prev,
|
|
258
285
|
...r,
|
|
286
|
+
// 熔断参数随最新配置刷新;计数状态(fails/openUntil)跨注册保留——半开窗口不被重置
|
|
287
|
+
breakerThreshold: r.breaker?.threshold ?? prev?.breakerThreshold ?? BREAKER_THRESHOLD,
|
|
288
|
+
breakerResetMs: r.breaker?.resetMs ?? prev?.breakerResetMs ?? BREAKER_RESET_MS,
|
|
259
289
|
state: prev?.state ?? "idle",
|
|
260
290
|
breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
|
|
291
|
+
entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
|
|
261
292
|
debug: prev?.debug ?? { entry: r.entry, status: "idle" }
|
|
262
293
|
});
|
|
263
294
|
}
|
|
@@ -331,6 +362,14 @@ function createRuntime() {
|
|
|
331
362
|
entry.loaded = true;
|
|
332
363
|
return entry.get();
|
|
333
364
|
}
|
|
365
|
+
function sanitizeUrl(u) {
|
|
366
|
+
try {
|
|
367
|
+
const x = new URL(u, typeof location < "u" ? location.href : "http://f.invalid");
|
|
368
|
+
return x.protocol + "//" + x.host + x.pathname;
|
|
369
|
+
} catch {
|
|
370
|
+
return String(u).replace(/[?#].*$/, "");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
334
373
|
function withTimeout(p, ms, label) {
|
|
335
374
|
return new Promise((resolve, reject) => {
|
|
336
375
|
const t = setTimeout(() => reject(new Error(`timeout after ${ms}ms: ${label}`)), ms);
|
|
@@ -363,14 +402,16 @@ function createRuntime() {
|
|
|
363
402
|
}
|
|
364
403
|
}
|
|
365
404
|
async function importEntry(url, remote) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
),
|
|
371
|
-
remote.timeout ?? DEFAULT_TIMEOUT,
|
|
372
|
-
`load remoteEntry ${url}`
|
|
405
|
+
const cached = remote.entryInflight.get(url);
|
|
406
|
+
const inflight = cached ?? import(
|
|
407
|
+
/* @vite-ignore */
|
|
408
|
+
url
|
|
373
409
|
);
|
|
410
|
+
if (!cached) {
|
|
411
|
+
remote.entryInflight.set(url, inflight);
|
|
412
|
+
inflight.catch(() => remote.entryInflight.delete(url));
|
|
413
|
+
}
|
|
414
|
+
return withTimeout(inflight, remote.timeout ?? DEFAULT_TIMEOUT, `load remoteEntry ${url}`);
|
|
374
415
|
}
|
|
375
416
|
async function acquireContainer(remote, overrides) {
|
|
376
417
|
const scopeKey = remote.shareScope || "default";
|
|
@@ -401,7 +442,11 @@ function createRuntime() {
|
|
|
401
442
|
const loaders = [];
|
|
402
443
|
if (remote.promise) {
|
|
403
444
|
loaders.push(async () => {
|
|
404
|
-
const resolved = await
|
|
445
|
+
const resolved = await withTimeout(
|
|
446
|
+
remote.promise(),
|
|
447
|
+
remote.timeout ?? DEFAULT_TIMEOUT,
|
|
448
|
+
`resolve promise remote "${remote.name}"`
|
|
449
|
+
);
|
|
405
450
|
const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
|
|
406
451
|
validateContainerInterface(container2, remote);
|
|
407
452
|
return container2;
|
|
@@ -432,25 +477,26 @@ function createRuntime() {
|
|
|
432
477
|
lastErr = err;
|
|
433
478
|
if (err?.code) throw err;
|
|
434
479
|
if (attempt < maxRetries) {
|
|
435
|
-
await sleep(200 * 2 ** attempt);
|
|
480
|
+
await sleep(Math.min(BACKOFF_MAX_MS, 200 * 2 ** attempt) * (1 - BACKOFF_JITTER * (1 - 2 * Math.random())));
|
|
436
481
|
}
|
|
437
482
|
}
|
|
438
483
|
}
|
|
439
484
|
if (succeeded) break;
|
|
440
485
|
}
|
|
441
486
|
if (!succeeded || !container) {
|
|
487
|
+
const shown = sanitizeUrl(remote.entry);
|
|
442
488
|
const hints = [];
|
|
443
489
|
if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
|
|
444
|
-
hints.push(`1. is the remote dev server running?
|
|
445
|
-
hints.push(`2. is the URL correct in federation({ remotes })? dev
|
|
446
|
-
hints.push(`3. CORS: the remote dev server must allow cross-origin requests (
|
|
490
|
+
hints.push(`1. is the remote dev server running? open ${shown} in a browser \u2014 it must return JS, not HTML`);
|
|
491
|
+
hints.push(`2. is the URL correct in federation({ remotes })? dev/prod entries can differ`);
|
|
492
|
+
hints.push(`3. CORS: the remote dev server must allow cross-origin requests (check its server.cors)`);
|
|
447
493
|
} else {
|
|
448
|
-
hints.push(`1. is the remote deployed
|
|
449
|
-
hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS
|
|
494
|
+
hints.push(`1. is the remote deployed? open ${shown} in a browser \u2014 it must return JS`);
|
|
495
|
+
hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS (check try_files)`);
|
|
450
496
|
}
|
|
451
497
|
throw new FgError(
|
|
452
498
|
ErrorCodes.REMOTE_LOAD_FAILED,
|
|
453
|
-
`failed to load remote "${remote.name}" from ${
|
|
499
|
+
`failed to load remote "${remote.name}" from ${shown}: ${String(lastErr?.message ?? lastErr)}
|
|
454
500
|
` + hints.join("\n"),
|
|
455
501
|
{ remote: remote.name }
|
|
456
502
|
);
|
|
@@ -482,8 +528,8 @@ function createRuntime() {
|
|
|
482
528
|
remote.debug.status = "failed";
|
|
483
529
|
remote.debug.error = String(err?.message ?? err);
|
|
484
530
|
b.fails += 1;
|
|
485
|
-
if (b.fails >=
|
|
486
|
-
b.openUntil = Date.now() +
|
|
531
|
+
if (b.fails >= remote.breakerThreshold) {
|
|
532
|
+
b.openUntil = Date.now() + remote.breakerResetMs;
|
|
487
533
|
b.fails = 0;
|
|
488
534
|
}
|
|
489
535
|
if (err instanceof FgError) {
|
|
@@ -501,7 +547,7 @@ function createRuntime() {
|
|
|
501
547
|
throw wrapped;
|
|
502
548
|
}
|
|
503
549
|
}
|
|
504
|
-
function
|
|
550
|
+
function parseSpec2(spec) {
|
|
505
551
|
const idx = spec.indexOf("/");
|
|
506
552
|
if (idx === -1) return { remote: spec, module: "" };
|
|
507
553
|
return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
|
|
@@ -511,8 +557,12 @@ function createRuntime() {
|
|
|
511
557
|
return m.startsWith(".") ? m : `./${m}`;
|
|
512
558
|
}
|
|
513
559
|
async function loadRemote2(spec, opts) {
|
|
514
|
-
const { remote: name, module } =
|
|
515
|
-
|
|
560
|
+
const { remote: name, module } = parseSpec2(spec);
|
|
561
|
+
try {
|
|
562
|
+
hooks.beforeLoadRemote?.({ remote: name, module });
|
|
563
|
+
} catch (hookErr) {
|
|
564
|
+
console.warn("[fulgurjs] beforeLoadRemote hook error (ignored):", hookErr);
|
|
565
|
+
}
|
|
516
566
|
const remote = remotes.get(name);
|
|
517
567
|
if (!remote) {
|
|
518
568
|
throw new FgError(
|
|
@@ -523,7 +573,8 @@ function createRuntime() {
|
|
|
523
573
|
}
|
|
524
574
|
let container;
|
|
525
575
|
try {
|
|
526
|
-
|
|
576
|
+
const stylesReady = module && remote.manifestUrl ? preloadRemote2(spec) : Promise.resolve();
|
|
577
|
+
[container] = await Promise.all([acquireContainer(remote, { retries: opts?.retries }), stylesReady]);
|
|
527
578
|
} catch (err) {
|
|
528
579
|
if (opts?.fallbackModule) {
|
|
529
580
|
console.error(`[fulgurjs] loadRemote("${spec}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`, err);
|
|
@@ -559,7 +610,11 @@ function createRuntime() {
|
|
|
559
610
|
}
|
|
560
611
|
throw err;
|
|
561
612
|
}
|
|
562
|
-
|
|
613
|
+
try {
|
|
614
|
+
hooks.afterLoadRemote?.({ remote: name, module, module_ns: ns });
|
|
615
|
+
} catch (hookErr) {
|
|
616
|
+
console.warn("[fulgurjs] afterLoadRemote hook error (ignored):", hookErr);
|
|
617
|
+
}
|
|
563
618
|
if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
|
|
564
619
|
const err = new FgError(
|
|
565
620
|
ErrorCodes.EMPTY_EXPORTS,
|
|
@@ -579,15 +634,44 @@ function createRuntime() {
|
|
|
579
634
|
return acquireContainer(remote);
|
|
580
635
|
}
|
|
581
636
|
async function preloadRemote2(spec, opts = {}) {
|
|
582
|
-
const { remote: name } =
|
|
637
|
+
const { remote: name, module } = parseSpec2(spec);
|
|
583
638
|
const remote = remotes.get(name);
|
|
584
639
|
if (!remote) {
|
|
585
640
|
throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
|
|
586
641
|
}
|
|
587
642
|
const mode = opts.mode ?? "preload";
|
|
643
|
+
const waitForStylesheet = (link, href) => {
|
|
644
|
+
if (link.sheet) return Promise.resolve();
|
|
645
|
+
const pending = stylesheetLoads.get(link);
|
|
646
|
+
if (pending) return pending;
|
|
647
|
+
const loaded = new Promise((resolve) => {
|
|
648
|
+
const finish = () => {
|
|
649
|
+
link.removeEventListener("load", finish);
|
|
650
|
+
link.removeEventListener("error", failed);
|
|
651
|
+
resolve();
|
|
652
|
+
};
|
|
653
|
+
const failed = () => {
|
|
654
|
+
emitError({
|
|
655
|
+
remote: name,
|
|
656
|
+
error: new FgError(ErrorCodes.PRELOAD_FAILED, `failed to load remote stylesheet ${href}`, {
|
|
657
|
+
remote: name
|
|
658
|
+
})
|
|
659
|
+
});
|
|
660
|
+
finish();
|
|
661
|
+
};
|
|
662
|
+
link.addEventListener("load", finish, { once: true });
|
|
663
|
+
link.addEventListener("error", failed, { once: true });
|
|
664
|
+
if (link.sheet) finish();
|
|
665
|
+
});
|
|
666
|
+
stylesheetLoads.set(link, loaded);
|
|
667
|
+
return loaded;
|
|
668
|
+
};
|
|
588
669
|
const inject = (href, as) => {
|
|
589
|
-
if (typeof document === "undefined") return;
|
|
590
|
-
|
|
670
|
+
if (typeof document === "undefined") return Promise.resolve();
|
|
671
|
+
const existing = document.querySelector(`link[href="${href}"]`);
|
|
672
|
+
if (existing) {
|
|
673
|
+
return as === "style" && mode === "preload" ? stylesheetLoads.get(existing) ?? Promise.resolve() : Promise.resolve();
|
|
674
|
+
}
|
|
591
675
|
const link = document.createElement("link");
|
|
592
676
|
link.rel = as === "style" ? "stylesheet" : "modulepreload";
|
|
593
677
|
link.href = href;
|
|
@@ -595,24 +679,69 @@ function createRuntime() {
|
|
|
595
679
|
;
|
|
596
680
|
link.fetchPriority = "low";
|
|
597
681
|
}
|
|
682
|
+
const loaded = as === "style" ? waitForStylesheet(link, href) : Promise.resolve();
|
|
598
683
|
document.head.appendChild(link);
|
|
684
|
+
return as === "style" && mode === "preload" ? loaded : Promise.resolve();
|
|
685
|
+
};
|
|
686
|
+
const resolveAssetUrl = (asset) => {
|
|
687
|
+
const pageUrl = typeof document !== "undefined" ? document.baseURI : void 0;
|
|
688
|
+
const base = new URL(remote.entry, pageUrl || "http://fulgurjs.invalid/");
|
|
689
|
+
if (!base.pathname.endsWith("/") && !base.pathname.split("/").pop().includes(".")) {
|
|
690
|
+
base.pathname += "/";
|
|
691
|
+
}
|
|
692
|
+
return new URL(asset, base).href;
|
|
599
693
|
};
|
|
600
694
|
try {
|
|
601
695
|
if (remote.manifestUrl) {
|
|
602
|
-
|
|
603
|
-
if (
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
696
|
+
let manifestPromise = remoteManifests.get(remote.manifestUrl);
|
|
697
|
+
if (!manifestPromise) {
|
|
698
|
+
manifestPromise = fetch(remote.manifestUrl, {
|
|
699
|
+
cache: "no-cache",
|
|
700
|
+
signal: AbortSignal.timeout(8e3)
|
|
701
|
+
}).then((res) => res.ok ? res.json() : void 0);
|
|
702
|
+
remoteManifests.set(remote.manifestUrl, manifestPromise);
|
|
703
|
+
manifestPromise.then(
|
|
704
|
+
(manifest2) => {
|
|
705
|
+
if (!manifest2) remoteManifests.delete(remote.manifestUrl);
|
|
706
|
+
},
|
|
707
|
+
() => remoteManifests.delete(remote.manifestUrl)
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
const manifest = await manifestPromise;
|
|
711
|
+
if (manifest) {
|
|
712
|
+
const sv = manifest.schemaVersion;
|
|
713
|
+
if (typeof sv === "number" && (!Number.isInteger(sv) || sv > 1)) {
|
|
714
|
+
emitError({
|
|
715
|
+
remote: name,
|
|
716
|
+
error: new FgError(ErrorCodes.PRELOAD_FAILED, `unsupported manifest schemaVersion ${String(sv)} (expected 1)`, {
|
|
717
|
+
remote: name
|
|
718
|
+
})
|
|
719
|
+
});
|
|
720
|
+
await inject(remote.entry, "modulepreload");
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
await inject(remote.entry, "modulepreload");
|
|
724
|
+
const exposes = manifest.exposes;
|
|
725
|
+
const entries = Array.isArray(exposes) ? exposes.filter((item) => item && typeof item === "object").map((item) => [String(item.name ?? ""), item]) : exposes && typeof exposes === "object" ? Object.entries(exposes) : [];
|
|
726
|
+
const selected = module ? entries.filter(([key, item]) => normalizeModuleName(String(item?.name ?? key)) === module) : entries;
|
|
727
|
+
const cssLoads = [];
|
|
728
|
+
for (const [, item] of selected) {
|
|
729
|
+
if (typeof item?.file === "string") {
|
|
730
|
+
await inject(resolveAssetUrl(item.file), "modulepreload");
|
|
731
|
+
}
|
|
732
|
+
if (Array.isArray(item?.css)) {
|
|
733
|
+
for (const css of item.css) {
|
|
734
|
+
if (typeof css !== "string") continue;
|
|
735
|
+
const loading = inject(resolveAssetUrl(css), "style");
|
|
736
|
+
if (mode === "preload") cssLoads.push(loading);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
611
739
|
}
|
|
740
|
+
await Promise.all(cssLoads);
|
|
612
741
|
return;
|
|
613
742
|
}
|
|
614
743
|
}
|
|
615
|
-
inject(remote.entry, "modulepreload");
|
|
744
|
+
await inject(remote.entry, "modulepreload");
|
|
616
745
|
} catch (err) {
|
|
617
746
|
emitError({
|
|
618
747
|
remote: name,
|
|
@@ -648,7 +777,7 @@ function createRuntime() {
|
|
|
648
777
|
loadRemote: loadRemote2,
|
|
649
778
|
getContainer: getContainer2,
|
|
650
779
|
preloadRemote: preloadRemote2,
|
|
651
|
-
parseSpec
|
|
780
|
+
parseSpec: parseSpec2
|
|
652
781
|
};
|
|
653
782
|
}
|
|
654
783
|
var g = globalThis;
|
|
@@ -667,6 +796,7 @@ var loadShare = runtime.loadShare;
|
|
|
667
796
|
var loadRemote = runtime.loadRemote;
|
|
668
797
|
var getContainer = runtime.getContainer;
|
|
669
798
|
var preloadRemote = runtime.preloadRemote;
|
|
799
|
+
var parseSpec = runtime.parseSpec;
|
|
670
800
|
var shareScopeMap = runtime.shareScopeMap;
|
|
671
801
|
|
|
672
802
|
// src/vue.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
# P5:fixtures Vite 7/8 兼容矩阵(2026-09-14
|
|
1
|
+
# P5:fixtures Vite 7/8 兼容矩阵(2026-09-14 完成,2026-09-23 补充)
|
|
2
2
|
|
|
3
3
|
> 结论先行:**Vite 7.3.6 与 8.3.0 下 fixtures 三件套 dev+prod e2e 全部通过**,无需代码改动。
|
|
4
4
|
> 兼容矩阵至此覆盖 Vite 5.1.4 / 5.2.12 / 6.4.3 / 7.3.6 / 8.3.0。
|
|
5
|
+
> 2026-09-23 补充:Vite 8.3.0(Rolldown)下,expose 的静态依赖 chunk 持有 CSS 时,manifest 仍能收集到该 CSS;针对性回归测试通过。
|
|
5
6
|
|
|
6
7
|
## 测试方法
|
|
7
8
|
|
|
@@ -29,8 +30,9 @@
|
|
|
29
30
|
等钩子签名在 7/8 无破坏性变化;
|
|
30
31
|
- dev 协作引擎依赖的 `server.middlewares`、`pluginContainer.resolveId` 行为一致;
|
|
31
32
|
- build 引擎(rollup 原生输出 + `emitFile chunk`)在 7/8 下产物结构不变。
|
|
33
|
+
- `packages/plugin/tests/build-manifest-css.test.ts` 在 Vite 8.3.0/Rolldown 下验证了共享静态依赖 chunk 上的 CSS 会进入 expose manifest;这是针对 CSS 注入链的构建测试,不等同于完整的 Rolldown dev/prod e2e 矩阵。
|
|
32
34
|
|
|
33
35
|
## 遗留
|
|
34
36
|
|
|
35
|
-
- Vite 8 原生
|
|
37
|
+
- Vite 8 原生 Rolldown 的完整 fixtures dev/prod e2e 矩阵仍未执行;当前仅 CSS manifest 构建回归已验证。
|
|
36
38
|
- fixtures 只覆盖 vue 生态;react fixture 若将来补齐需同步扩 7/8 矩阵。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fulgurjs/federation",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vite Module Federation with full Webpack MF parity. Dev & production, no compromise.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,13 +62,13 @@
|
|
|
62
62
|
"CHANGELOG.md",
|
|
63
63
|
"DESIGN.md",
|
|
64
64
|
"examples",
|
|
65
|
-
"docs
|
|
66
|
-
"docs/webpack-mf
|
|
67
|
-
"docs
|
|
68
|
-
"docs/P5-vite7-8
|
|
65
|
+
"docs/迁移指南.md",
|
|
66
|
+
"docs/webpack-mf-对照与缺口.md",
|
|
67
|
+
"docs/沙箱边界审计.md",
|
|
68
|
+
"docs/P5-vite7-8兼容矩阵.md"
|
|
69
69
|
],
|
|
70
70
|
"scripts": {
|
|
71
|
-
"build": "cp ../../README.md README.md && mkdir -p docs && cp ../../docs
|
|
71
|
+
"build": "cp ../../README.md README.md && mkdir -p docs && cp ../../docs/迁移指南.md ../../docs/webpack-mf-对照与缺口.md ../../docs/沙箱边界审计.md ../../docs/P5-vite7-8兼容矩阵.md docs/ && cp ../../CHANGELOG.md CHANGELOG.md && cp ../../DESIGN.md DESIGN.md && rm -rf examples && cp -r ../../examples examples && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js && node scripts/gen-runtime.mjs && tsup src/index.ts src/pages.ts src/config.ts src/vue.ts src/context.ts --format esm,cjs --dts --external vite --target node18 --clean && esbuild src/cli.ts --bundle --format=esm --target=node18 --platform=node --outfile=dist/cli.js && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js && node scripts/check-gzip.mjs && node scripts/check-manual-codes.mjs",
|
|
72
72
|
"dev": "tsup --watch",
|
|
73
73
|
"test": "vitest run",
|
|
74
74
|
"typecheck": "tsc --noEmit",
|