@module-federation/vite 1.23.1 → 1.23.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/README.md +36 -0
- package/lib/{dtsConstants-Bs4FukGL.js → dtsConstants-r5OfegUr.js} +1 -1
- package/lib/index.d.ts +17 -0
- package/lib/index.js +543 -123
- package/lib/{logger--uKHOVjR.js → logger-DOwfTlw5.js} +1 -1
- package/lib/{pluginDts-DxgS2FEd.js → pluginDts-DnEX3cp_.js} +3 -3
- package/lib/{ssrEntryLoader-DmqLXUmm.js → ssrEntryLoader-DEH3eV1e.js} +51 -24
- package/lib/{ssrVmStrategy-B_e3yqa2.js → ssrVmStrategy-COMHOCN2.js} +15 -20
- package/lib/utils/ssrEntryLoader.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -402,6 +402,42 @@ federation({
|
|
|
402
402
|
`provideExternalRuntime` injects a local runtime plugin that publishes `runtime-core` on `globalThis._FEDERATION_RUNTIME_CORE`. `externalRuntime` rewrites imports of `@module-federation/runtime-core` to read that global. A container that also `exposes` (e.g. a host consumed by its own remotes) may provide the runtime too, as long as exactly one container on the page does and it is loaded before any `externalRuntime` remote evaluates (a second provider is ignored with a `Detect multiple module federation runtime!` warning; a remote evaluated before the provider throws `_FEDERATION_RUNTIME_CORE is missing`).
|
|
403
403
|
The `externalRuntime` rewrite applies to the browser remote graph; SSR remote entries continue to resolve `@module-federation/runtime-core` from Node so they do not depend on the browser global.
|
|
404
404
|
|
|
405
|
+
## Host provides all shared (`experiments.hostProvidesAllShared`)
|
|
406
|
+
|
|
407
|
+
For topologies where **one host provides every shared dependency** and remotes only consume them, shared dependencies can be resolved by a native browser [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) instead of the Module Federation runtime. No `loadShare` / prebuild glue is generated and `eager` is not needed: shared modules are ordinary static ES imports, so code that reads a shared value at module-evaluation time (`class X extends SharedBase`, decorator metadata) just works.
|
|
408
|
+
|
|
409
|
+
**Remote** (a container with `exposes`):
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
federation({
|
|
413
|
+
name: "remote",
|
|
414
|
+
exposes: { "./Button": "./src/Button.tsx" },
|
|
415
|
+
shared: { react: {}, "react-dom": {}, "react/jsx-runtime": {} },
|
|
416
|
+
experiments: { hostProvidesAllShared: true },
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
The remote build emits one fixed-name entry per expose (`./Button` → `Button.js`), keeps every shared key as a bare external import and writes a standard `mf-manifest.json` next to the entries, with no `remoteEntry` since there is no runtime container. `metaData.publicPath` comes from Vite's `base` (or `publicPath`); `auto` is inferred from the manifest URL by the host.
|
|
421
|
+
|
|
422
|
+
**Host** (no `exposes`):
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
federation({
|
|
426
|
+
name: "host",
|
|
427
|
+
remotes: { remote: "https://cdn.example.com/remote/mf-manifest.json" },
|
|
428
|
+
shared: { react: {}, "react-dom": {}, "react/jsx-runtime": {} },
|
|
429
|
+
experiments: { hostProvidesAllShared: true },
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
The host emits one re-export chunk per shared key, which the bundler dedupes with the host's own imports, and injects a `<script type="importmap">` into `index.html` mapping each shared key to that chunk and each remote expose (`remote/Button`) to the entry listed in the manifest. A manifest may also be a path relative to the host root (for example `../remote/dist/mf-manifest.json`). A manifest that has a `remoteEntry` belongs to a Module Federation runtime remote and is rejected. The dev server serves the same map, pointing shared keys at virtual modules that resolve to the host's pre-bundled dependencies, so host and remotes share one instance in dev too.
|
|
434
|
+
|
|
435
|
+
A remote that imports an unshared subpath of a shared package (`rxjs/operators` with only `rxjs` shared) bundles a private copy of that package's internals; the remote build warns and names the key to add to `shared`.
|
|
436
|
+
|
|
437
|
+
`dts` keeps working through the manifest's `metaData.types`: a remote emits its types archive next to `mf-manifest.json` and a host referencing the manifest by URL consumes it from there.
|
|
438
|
+
|
|
439
|
+
Not supported in this mode, since the Module Federation runtime is not loaded: version negotiation and fallbacks, share aliasing (`request` / `shareKey`), `import: false`, runtime plugins, SSR, tree-shaking artifacts, `loadRemote` / runtime-registered remotes, and interop with webpack / rspack containers. Options that only the runtime or `remoteEntry.js` pipeline reads (for example `filename`, `shareStrategy`, `runtimePlugins`, `eager`, `requiredVersion`) are ignored; one warning names them. `singleton` needs no setting: every shared key has exactly one instance. The import map is fixed when the host is built, so a remote must keep its expose entry names stable across deploys. The inline import map changes with every host build; a strict CSP needs a nonce or hash for it.
|
|
440
|
+
|
|
405
441
|
## ⚠️ `codeSplitting` is managed by the plugin
|
|
406
442
|
|
|
407
443
|
Do not set `build.rollupOptions.output.codeSplitting` or
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as getNodeModulesSuffix, r as getSharedRuntimeKey } from "./sharedKeyMatcher-Od_nYxJA.js";
|
|
2
|
-
import { r as mfWarn, t as createModuleFederationError } from "./logger
|
|
2
|
+
import { r as mfWarn, t as createModuleFederationError } from "./logger-DOwfTlw5.js";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, realpathSync } from "fs";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
import * as path$1 from "node:path";
|
package/lib/index.d.ts
CHANGED
|
@@ -167,6 +167,23 @@ interface PluginExperimentsOptions {
|
|
|
167
167
|
provideExternalRuntime?: boolean;
|
|
168
168
|
/** Generate the React SSR/hydration island capability for eligible exposes. */
|
|
169
169
|
ssrMode?: 'ISLAND';
|
|
170
|
+
/**
|
|
171
|
+
* The host provides every shared dependency; remotes bring none. Shared keys
|
|
172
|
+
* are resolved by a native browser import map instead of the Module
|
|
173
|
+
* Federation runtime: the host emits one entry chunk per shared key and
|
|
174
|
+
* injects `<script type="importmap">`, remotes keep shared imports as bare
|
|
175
|
+
* specifiers and emit one fixed-name entry per expose plus a standard
|
|
176
|
+
* `mf-manifest.json` (without `remoteEntry`) the host maps from.
|
|
177
|
+
*
|
|
178
|
+
* Shared modules become ordinary static ES imports, so there is no
|
|
179
|
+
* `loadShare` glue and no `eager` is needed for code that reads a shared
|
|
180
|
+
* value at module-evaluation time. There is no version negotiation and no
|
|
181
|
+
* runtime API: `requiredVersion`, `strictVersion`, `shareStrategy`,
|
|
182
|
+
* `runtimePlugins` and `manifest` are ignored.
|
|
183
|
+
*
|
|
184
|
+
* @see https://github.com/module-federation/vite/issues/1381
|
|
185
|
+
*/
|
|
186
|
+
hostProvidesAllShared?: boolean;
|
|
170
187
|
}
|
|
171
188
|
type SsrEntryLoaderStrategy = 'temp-file' | 'vm';
|
|
172
189
|
type SsrEntryLoaderConfig = {
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as resolvePublicPath, a as matchesSharedSource, c as filterId, d as getNodeModulesSuffix, f as isAssetLikeImport, g as normalizeNodeModulePath, h as isViteOptimizableEntry, i as invalidateSharedKeyMatcher, l as getBasePath$1, m as isNuxtClientBase, n as getSharedRequest, p as isNodeModulePath, r as getSharedRuntimeKey, s as ensureTrailingSlash, t as findSharedKey, u as getCommonSharedSubpaths, v as stripQueryAndHash, y as stripViteFsPrefix } from "./sharedKeyMatcher-Od_nYxJA.js";
|
|
2
|
-
import { a as CodeRewriter, c as
|
|
3
|
-
import { C as isReactCoreShare, S as REACT_DOM_CLIENT_SHARE, _ as resolveModulePath, a as getIsRolldown, b as supportsCodeSplittingGroupDebugName, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as isPackageExportAvailable, g as resolveImportPath, h as packageNameEncode, i as getInstalledPackageJson, l as getSharedCacheDescriptor, m as packageNameDecode, n as getDependencyNames, o as getPackageDetectionCwd, p as isPackageInstalled, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as setPackageDetectionCwd, x as REACT_CLIENT_INTERNALS_KEY, y as sharedCacheHelperCode } from "./dtsConstants-
|
|
2
|
+
import { a as CodeRewriter, c as isAbsoluteUrl, d as resolveHashPlaceholderFileName, i as createCodePositionMap, l as normalizePathForImport, o as createSourceMap, r as mfWarn, t as createModuleFederationError, u as rebaseImport } from "./logger-DOwfTlw5.js";
|
|
3
|
+
import { C as isReactCoreShare, S as REACT_DOM_CLIENT_SHARE, _ as resolveModulePath, a as getIsRolldown, b as supportsCodeSplittingGroupDebugName, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as isPackageExportAvailable, g as resolveImportPath, h as packageNameEncode, i as getInstalledPackageJson, l as getSharedCacheDescriptor, m as packageNameDecode, n as getDependencyNames, o as getPackageDetectionCwd, p as isPackageInstalled, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as setPackageDetectionCwd, x as REACT_CLIENT_INTERNALS_KEY, y as sharedCacheHelperCode } from "./dtsConstants-r5OfegUr.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import * as fs$2 from "fs";
|
|
6
6
|
import { existsSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "fs";
|
|
@@ -15,6 +15,8 @@ import * as fs$1 from "node:fs";
|
|
|
15
15
|
import { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "node:fs";
|
|
16
16
|
import { pathToFileURL as pathToFileURL$1 } from "node:url";
|
|
17
17
|
import { isIPv6 } from "node:net";
|
|
18
|
+
import { inferAutoPublicPath } from "@module-federation/sdk";
|
|
19
|
+
import { readFile } from "node:fs/promises";
|
|
18
20
|
//#region src/utils/regexEscape.ts
|
|
19
21
|
const REGEXP_SPECIAL_CHARS_RE = /[.*+?^${}()|[\]\\]/g;
|
|
20
22
|
/**
|
|
@@ -723,7 +725,8 @@ function normalizeExperiments(experiments) {
|
|
|
723
725
|
return {
|
|
724
726
|
externalRuntime: experiments?.externalRuntime === true,
|
|
725
727
|
provideExternalRuntime: experiments?.provideExternalRuntime === true,
|
|
726
|
-
ssrMode: experiments?.ssrMode === "ISLAND" ? "ISLAND" : void 0
|
|
728
|
+
ssrMode: experiments?.ssrMode === "ISLAND" ? "ISLAND" : void 0,
|
|
729
|
+
hostProvidesAllShared: experiments?.hostProvidesAllShared === true
|
|
727
730
|
};
|
|
728
731
|
}
|
|
729
732
|
function normalizeSsrEntryLoader(ssrEntryLoader) {
|
|
@@ -3738,16 +3741,22 @@ function generateLocalSharedImportMap(options) {
|
|
|
3738
3741
|
const __mfGetCachedReactFamily = (keys, reactKeys, localVersion, requiredExport) => {
|
|
3739
3742
|
const cache = globalThis.__mf_module_cache__?.share;
|
|
3740
3743
|
const react = reactKeys.map((key) => cache?.[key]).find((value) => value !== undefined);
|
|
3741
|
-
const
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3744
|
+
const isOlder = (version) => {
|
|
3745
|
+
const actual = String(version || '').split(/[^0-9]+/).map(Number);
|
|
3746
|
+
const expected = String(localVersion || '').split(/[^0-9]+/).map(Number);
|
|
3747
|
+
for (let index = 0; index < Math.max(actual.length, expected.length); index++) {
|
|
3748
|
+
if ((actual[index] || 0) < (expected[index] || 0)) return true;
|
|
3749
|
+
if ((actual[index] || 0) > (expected[index] || 0)) return false;
|
|
3750
|
+
}
|
|
3751
|
+
return false;
|
|
3752
|
+
};
|
|
3753
|
+
if (isOlder(react?.version ?? react?.default?.version)) return undefined;
|
|
3748
3754
|
for (const key of keys) {
|
|
3749
3755
|
const cached = cache?.[key];
|
|
3750
3756
|
if (cached === undefined) continue;
|
|
3757
|
+
// A host seed from an older React family is not coherent with the newer cached react
|
|
3758
|
+
const cachedVersion = cached?.version ?? cached?.default?.version;
|
|
3759
|
+
if (cachedVersion !== undefined && isOlder(cachedVersion)) continue;
|
|
3751
3760
|
if (!requiredExport || typeof cached?.[requiredExport] === 'function' || typeof cached?.default?.[requiredExport] === 'function') return cached;
|
|
3752
3761
|
}
|
|
3753
3762
|
return undefined;
|
|
@@ -5830,6 +5839,22 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build", options
|
|
|
5830
5839
|
}));
|
|
5831
5840
|
}
|
|
5832
5841
|
` : ""}
|
|
5842
|
+
// The seed called this container's get() directly, so the Runtime's
|
|
5843
|
+
// provider never learned it is in use and a remote could load its own
|
|
5844
|
+
// copy (#1396). Mark it once the host is bound to the local copy.
|
|
5845
|
+
for (const [pkg, share] of Object.entries(usedShared)) {
|
|
5846
|
+
if (!share.lib || share.treeShaking || share.shareConfig?.import === false) continue;
|
|
5847
|
+
const cacheDescriptor = __mfGetSharedCacheDescriptor(pkg, share.shareConfig?.singleton, share.version, share.scope);
|
|
5848
|
+
if (__mfReadSharedCacheOwner(__mfModuleCache.share, cacheDescriptor) !== ${cacheOwner}) continue;
|
|
5849
|
+
for (const scopeName of Array.isArray(share.scope) ? share.scope : [share.scope || "default"]) {
|
|
5850
|
+
const versions = runtime.shareScopeMap?.[scopeName]?.[pkg];
|
|
5851
|
+
const provider = versions?.[share.version];
|
|
5852
|
+
if (!provider || provider.get !== share.get || provider.lib) continue;
|
|
5853
|
+
if (Object.values(versions).some((other) => other !== provider && (other?.lib || other?.loaded))) continue;
|
|
5854
|
+
provider.lib = share.lib;
|
|
5855
|
+
provider.loaded = true;
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5833
5858
|
return runtime;
|
|
5834
5859
|
})();
|
|
5835
5860
|
}
|
|
@@ -8058,7 +8083,7 @@ function ${helperName}(names) {
|
|
|
8058
8083
|
}
|
|
8059
8084
|
//#endregion
|
|
8060
8085
|
//#region package.json
|
|
8061
|
-
var version$1 = "1.23.
|
|
8086
|
+
var version$1 = "1.23.2";
|
|
8062
8087
|
//#endregion
|
|
8063
8088
|
//#region src/virtualModules/virtualExposesSSR.ts
|
|
8064
8089
|
/**
|
|
@@ -8160,6 +8185,8 @@ function generateRemoteEntrySSR(options) {
|
|
|
8160
8185
|
const sharedSingletons = ${JSON.stringify(sharedSingletons)};
|
|
8161
8186
|
const moduleCacheKey = Symbol.for(${JSON.stringify(MODULE_CACHE_SHARE_SCOPE_KEY)});
|
|
8162
8187
|
let exposesMapPromise;
|
|
8188
|
+
// One identity per container, so a re-entrant init with the same initScope finds it.
|
|
8189
|
+
const initToken = { from: ${JSON.stringify(options.name)} };
|
|
8163
8190
|
|
|
8164
8191
|
function createShareInitError(errors) {
|
|
8165
8192
|
const details = errors.map(({ scopeName, pkg, error }) => {
|
|
@@ -8188,7 +8215,6 @@ function generateRemoteEntrySSR(options) {
|
|
|
8188
8215
|
remotes: [],
|
|
8189
8216
|
shared: {},
|
|
8190
8217
|
});
|
|
8191
|
-
const initToken = { from: ${JSON.stringify(options.name)} };
|
|
8192
8218
|
if (initScope.indexOf(initToken) >= 0) return;
|
|
8193
8219
|
initScope.push(initToken);
|
|
8194
8220
|
const shareScopeNames = Array.isArray(${JSON.stringify(options.shareScope)})
|
|
@@ -8525,18 +8551,21 @@ const Manifest = (providedOptions) => {
|
|
|
8525
8551
|
publicPath = mfOptions.publicPath === "auto" ? "auto" : resolvePublicPath(mfOptions, base, _originalConfigBase);
|
|
8526
8552
|
},
|
|
8527
8553
|
/**
|
|
8528
|
-
*
|
|
8529
|
-
*
|
|
8530
|
-
*
|
|
8531
|
-
*
|
|
8532
|
-
*
|
|
8554
|
+
* The client manifest only advertises an SSR entry its own bundle
|
|
8555
|
+
* emitted. The SSR entry usually lands in a separate build: a standalone
|
|
8556
|
+
* `vite build --ssr` (possibly with its own `filename`, e.g.
|
|
8557
|
+
* `defineConfig(({ isSsrBuild }) => ...)`) or the `ssr` environment,
|
|
8558
|
+
* which the builder may run before or after `client`. Whichever build
|
|
8559
|
+
* finishes with the SSR entry on disk points the manifest and stats at it.
|
|
8533
8560
|
*/
|
|
8534
8561
|
writeBundle(outputOptions) {
|
|
8535
8562
|
if (!mfManifestName || _command !== "build" || isConsumerProject) return;
|
|
8536
|
-
const environment = this.environment;
|
|
8537
|
-
if (!(environment ? environment.name === "ssr" : Boolean(viteConfig?.build?.ssr))) return;
|
|
8538
8563
|
const ssrEntryName = getSsrRemoteEntryFileName(mfOptions);
|
|
8539
|
-
const outDirs = [...new Set([
|
|
8564
|
+
const outDirs = [...new Set([
|
|
8565
|
+
outputOptions.dir,
|
|
8566
|
+
viteConfig?.environments?.client?.build?.outDir,
|
|
8567
|
+
viteConfig?.environments?.ssr?.build?.outDir
|
|
8568
|
+
].filter((dir) => typeof dir === "string").map((dir) => path$1.resolve(root, dir)))];
|
|
8540
8569
|
if (!outDirs.some((dir) => fs$1.existsSync(path$1.resolve(dir, ssrEntryName)))) return;
|
|
8541
8570
|
for (const dir of outDirs) for (const fileName of [mfManifestName, mfManifestStatsName]) {
|
|
8542
8571
|
if (!fileName) continue;
|
|
@@ -8545,8 +8574,12 @@ const Manifest = (providedOptions) => {
|
|
|
8545
8574
|
try {
|
|
8546
8575
|
const data = JSON.parse(fs$1.readFileSync(file, "utf8"));
|
|
8547
8576
|
const entry = data?.metaData?.ssrRemoteEntry;
|
|
8548
|
-
if (!
|
|
8549
|
-
|
|
8577
|
+
if (!data?.metaData || entry?.name === ssrEntryName) continue;
|
|
8578
|
+
data.metaData.ssrRemoteEntry = Object.assign({
|
|
8579
|
+
name: ssrEntryName,
|
|
8580
|
+
path: "",
|
|
8581
|
+
type: "module"
|
|
8582
|
+
}, typeof entry === "object" ? entry : null, { name: ssrEntryName });
|
|
8550
8583
|
fs$1.writeFileSync(file, JSON.stringify(data));
|
|
8551
8584
|
} catch {}
|
|
8552
8585
|
}
|
|
@@ -8564,7 +8597,7 @@ const Manifest = (providedOptions) => {
|
|
|
8564
8597
|
const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions);
|
|
8565
8598
|
const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
|
|
8566
8599
|
if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
|
|
8567
|
-
ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(mfOptions.filename)) :
|
|
8600
|
+
ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(mfOptions.filename)) : void 0);
|
|
8568
8601
|
const allCssAssets = mfOptions.bundleAllCSS && !disableAssetsAnalyze ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
|
|
8569
8602
|
if (allCssAssets.size > 0) {
|
|
8570
8603
|
const secondaryCss = /* @__PURE__ */ new Set();
|
|
@@ -8640,11 +8673,12 @@ const Manifest = (providedOptions) => {
|
|
|
8640
8673
|
path: "",
|
|
8641
8674
|
type: "module"
|
|
8642
8675
|
};
|
|
8643
|
-
const
|
|
8644
|
-
|
|
8676
|
+
const resolvedSsrRemoteEntryFile = _command === "serve" ? ssrRemoteEntryFile || getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(filename)) : ssrRemoteEntryFile;
|
|
8677
|
+
const ssrRemoteEntry = resolvedSsrRemoteEntryFile ? {
|
|
8678
|
+
name: resolvedSsrRemoteEntryFile,
|
|
8645
8679
|
path: _command === "serve" ? "/__mf_ssr__/" : "",
|
|
8646
8680
|
type: "module"
|
|
8647
|
-
};
|
|
8681
|
+
} : void 0;
|
|
8648
8682
|
const varRemoteEntry = varFilename ? {
|
|
8649
8683
|
name: varFilename,
|
|
8650
8684
|
path: "",
|
|
@@ -8769,6 +8803,106 @@ function getStatsFileName(manifestFileName) {
|
|
|
8769
8803
|
return parsed.dir ? normalizePathForImport(path$1.join(parsed.dir, fileName)) : fileName;
|
|
8770
8804
|
}
|
|
8771
8805
|
//#endregion
|
|
8806
|
+
//#region src/utils/sharedSource.ts
|
|
8807
|
+
const pendingEntryLookups = /* @__PURE__ */ new Map();
|
|
8808
|
+
const getEntryLookupKey = (source, importer) => `${source}\0${importer}`;
|
|
8809
|
+
/** Whether a resolveId call is one of the shared source resolver's own entry lookups. */
|
|
8810
|
+
function isSharedEntryLookup(source, importer, options) {
|
|
8811
|
+
return options.custom?.__mfSharedEntryLookup === true || pendingEntryLookups.has(getEntryLookupKey(source, importer));
|
|
8812
|
+
}
|
|
8813
|
+
/**
|
|
8814
|
+
* Runs one of the plugin's own `this.resolve()` calls (a shared entry lookup, a
|
|
8815
|
+
* parse-barrier external probe), marked so `isSharedEntryLookup` recognizes it
|
|
8816
|
+
* and the share hooks neither proxy it nor register a used share for it.
|
|
8817
|
+
*/
|
|
8818
|
+
async function resolveInternally(context, source, importer, options = {}) {
|
|
8819
|
+
const lookupKey = getEntryLookupKey(source, importer);
|
|
8820
|
+
pendingEntryLookups.set(lookupKey, (pendingEntryLookups.get(lookupKey) ?? 0) + 1);
|
|
8821
|
+
try {
|
|
8822
|
+
return await context.resolve(source, importer, {
|
|
8823
|
+
...options,
|
|
8824
|
+
skipSelf: true,
|
|
8825
|
+
custom: {
|
|
8826
|
+
...options.custom,
|
|
8827
|
+
__mfSharedEntryLookup: true
|
|
8828
|
+
}
|
|
8829
|
+
});
|
|
8830
|
+
} finally {
|
|
8831
|
+
const remaining = pendingEntryLookups.get(lookupKey) - 1;
|
|
8832
|
+
if (remaining) pendingEntryLookups.set(lookupKey, remaining);
|
|
8833
|
+
else pendingEntryLookups.delete(lookupKey);
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
/** Identify shared entries through Vite, with a cache scoped to this federation instance. */
|
|
8837
|
+
function createSharedSourceResolver(shared, getResolutionConfig) {
|
|
8838
|
+
const caches = /* @__PURE__ */ new Map();
|
|
8839
|
+
async function matchEntry(source, suffix, resolveEntry) {
|
|
8840
|
+
const packageName = getPackageNameFromNodeModulePath(source);
|
|
8841
|
+
if (!packageName) return;
|
|
8842
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
8843
|
+
if (findSharedKey(packageName, shared)) candidates.add(packageName);
|
|
8844
|
+
if (findSharedKey(suffix, shared)) candidates.add(suffix);
|
|
8845
|
+
for (const key of Object.keys(shared)) {
|
|
8846
|
+
const request = getSharedRequest(key, shared[key]);
|
|
8847
|
+
if (getPackageName(request) !== packageName) continue;
|
|
8848
|
+
if (!request.endsWith("/")) candidates.add(request);
|
|
8849
|
+
for (const subpath of getCommonSharedSubpaths(request)) if (findSharedKey(subpath, shared)) candidates.add(subpath);
|
|
8850
|
+
}
|
|
8851
|
+
if (candidates.size === 0) return;
|
|
8852
|
+
const resolvedSource = await resolveEntry(source);
|
|
8853
|
+
if (!resolvedSource) return;
|
|
8854
|
+
for (const candidate of candidates) {
|
|
8855
|
+
const entry = await resolveEntry(candidate);
|
|
8856
|
+
if (!entry) continue;
|
|
8857
|
+
const allowNodeModulesSuffixMatch = shared[findSharedKey(candidate, shared) ?? candidate]?.shareConfig?.allowNodeModulesSuffixMatch === true;
|
|
8858
|
+
if (normalizeNodeModulePath(entry) === normalizeNodeModulePath(resolvedSource) || allowNodeModulesSuffixMatch && getNodeModulesSuffix(entry) === suffix) return candidate;
|
|
8859
|
+
}
|
|
8860
|
+
}
|
|
8861
|
+
return {
|
|
8862
|
+
clear() {
|
|
8863
|
+
caches.clear();
|
|
8864
|
+
},
|
|
8865
|
+
async resolve(context, source, options = {}) {
|
|
8866
|
+
if (source.startsWith("\0") || source.includes("#")) return;
|
|
8867
|
+
const queryIndex = source.indexOf("?");
|
|
8868
|
+
const query = queryIndex === -1 ? "" : source.slice(queryIndex + 1);
|
|
8869
|
+
if (query && [...new URLSearchParams(query).keys()].some((key) => ![
|
|
8870
|
+
"v",
|
|
8871
|
+
"t",
|
|
8872
|
+
"import"
|
|
8873
|
+
].includes(key))) return;
|
|
8874
|
+
if (findSharedKey(source, shared)) return source;
|
|
8875
|
+
const suffix = getNodeModulesSuffix(source);
|
|
8876
|
+
if (!suffix) return;
|
|
8877
|
+
const normalizedSource = stripViteFsPrefix(normalizeNodeModulePath(source));
|
|
8878
|
+
const environment = context.environment ?? !!options.ssr;
|
|
8879
|
+
const cacheable = !options.isEntry && !options.scan && (!options.kind || options.kind === "import-statement") && Object.keys(options.attributes ?? {}).length === 0 && Object.keys(options.custom ?? {}).length === 0;
|
|
8880
|
+
let cache = cacheable ? caches.get(environment) : void 0;
|
|
8881
|
+
if (cacheable && !cache) {
|
|
8882
|
+
cache = /* @__PURE__ */ new Map();
|
|
8883
|
+
caches.set(environment, cache);
|
|
8884
|
+
}
|
|
8885
|
+
if (cache?.has(normalizedSource)) return cache.get(normalizedSource);
|
|
8886
|
+
const { root, conditions } = getResolutionConfig(context, options);
|
|
8887
|
+
const result = await matchEntry(normalizedSource, suffix, async (request) => {
|
|
8888
|
+
if (!path$1.isAbsolute(request) && !isPackageExportAvailable(request, {
|
|
8889
|
+
cwd: root,
|
|
8890
|
+
conditions
|
|
8891
|
+
})) return void 0;
|
|
8892
|
+
const importer = path$1.join(root, "package.json");
|
|
8893
|
+
try {
|
|
8894
|
+
const resolved = await resolveInternally(context, request, importer, options);
|
|
8895
|
+
return resolved && !resolved.external ? resolved.id : void 0;
|
|
8896
|
+
} catch {
|
|
8897
|
+
return;
|
|
8898
|
+
}
|
|
8899
|
+
});
|
|
8900
|
+
cache?.set(normalizedSource, result);
|
|
8901
|
+
return result;
|
|
8902
|
+
}
|
|
8903
|
+
};
|
|
8904
|
+
}
|
|
8905
|
+
//#endregion
|
|
8772
8906
|
//#region src/plugins/pluginModuleParseEnd.ts
|
|
8773
8907
|
function createModuleParseController() {
|
|
8774
8908
|
return {
|
|
@@ -8910,7 +9044,7 @@ function pluginModuleParseEnd_default(excludeFn, options, controller = createMod
|
|
|
8910
9044
|
if (typeof this.resolve !== "function") return;
|
|
8911
9045
|
if (controller.resolutionProbed.has(pendingId)) return;
|
|
8912
9046
|
controller.resolutionProbed.add(pendingId);
|
|
8913
|
-
this
|
|
9047
|
+
resolveInternally(this, pendingId, id).then((resolved) => {
|
|
8914
9048
|
if (!resolved?.external) return;
|
|
8915
9049
|
controller.externalSet.add(pendingId);
|
|
8916
9050
|
controller.parseStartSet.delete(pendingId);
|
|
@@ -8919,7 +9053,7 @@ function pluginModuleParseEnd_default(excludeFn, options, controller = createMod
|
|
|
8919
9053
|
};
|
|
8920
9054
|
const addPendingIds = (ids) => {
|
|
8921
9055
|
for (const pendingId of ids || []) {
|
|
8922
|
-
if (
|
|
9056
|
+
if (controller.externalSet.has(pendingId) || matchesExternal(configuredExternal, pendingId, id) || excludeFn(pendingId)) continue;
|
|
8923
9057
|
controller.parseStartSet.add(pendingId);
|
|
8924
9058
|
if (!controller.parseEndSet.has(pendingId)) probeExternal(pendingId);
|
|
8925
9059
|
}
|
|
@@ -9219,96 +9353,6 @@ function pluginProxyRemotes_default(options) {
|
|
|
9219
9353
|
};
|
|
9220
9354
|
}
|
|
9221
9355
|
//#endregion
|
|
9222
|
-
//#region src/utils/sharedSource.ts
|
|
9223
|
-
const pendingEntryLookups = /* @__PURE__ */ new Map();
|
|
9224
|
-
const getEntryLookupKey = (source, importer) => `${source}\0${importer}`;
|
|
9225
|
-
/** Whether a resolveId call is one of the shared source resolver's own entry lookups. */
|
|
9226
|
-
function isSharedEntryLookup(source, importer, options) {
|
|
9227
|
-
return options.custom?.__mfSharedEntryLookup === true || pendingEntryLookups.has(getEntryLookupKey(source, importer));
|
|
9228
|
-
}
|
|
9229
|
-
/** Identify shared entries through Vite, with a cache scoped to this federation instance. */
|
|
9230
|
-
function createSharedSourceResolver(shared, getResolutionConfig) {
|
|
9231
|
-
const caches = /* @__PURE__ */ new Map();
|
|
9232
|
-
async function matchEntry(source, suffix, resolveEntry) {
|
|
9233
|
-
const packageName = getPackageNameFromNodeModulePath(source);
|
|
9234
|
-
if (!packageName) return;
|
|
9235
|
-
const candidates = /* @__PURE__ */ new Set();
|
|
9236
|
-
if (findSharedKey(packageName, shared)) candidates.add(packageName);
|
|
9237
|
-
if (findSharedKey(suffix, shared)) candidates.add(suffix);
|
|
9238
|
-
for (const key of Object.keys(shared)) {
|
|
9239
|
-
const request = getSharedRequest(key, shared[key]);
|
|
9240
|
-
if (getPackageName(request) !== packageName) continue;
|
|
9241
|
-
if (!request.endsWith("/")) candidates.add(request);
|
|
9242
|
-
for (const subpath of getCommonSharedSubpaths(request)) if (findSharedKey(subpath, shared)) candidates.add(subpath);
|
|
9243
|
-
}
|
|
9244
|
-
if (candidates.size === 0) return;
|
|
9245
|
-
const resolvedSource = await resolveEntry(source);
|
|
9246
|
-
if (!resolvedSource) return;
|
|
9247
|
-
for (const candidate of candidates) {
|
|
9248
|
-
const entry = await resolveEntry(candidate);
|
|
9249
|
-
if (!entry) continue;
|
|
9250
|
-
const allowNodeModulesSuffixMatch = shared[findSharedKey(candidate, shared) ?? candidate]?.shareConfig?.allowNodeModulesSuffixMatch === true;
|
|
9251
|
-
if (normalizeNodeModulePath(entry) === normalizeNodeModulePath(resolvedSource) || allowNodeModulesSuffixMatch && getNodeModulesSuffix(entry) === suffix) return candidate;
|
|
9252
|
-
}
|
|
9253
|
-
}
|
|
9254
|
-
return {
|
|
9255
|
-
clear() {
|
|
9256
|
-
caches.clear();
|
|
9257
|
-
},
|
|
9258
|
-
async resolve(context, source, options = {}) {
|
|
9259
|
-
if (source.startsWith("\0") || source.includes("#")) return;
|
|
9260
|
-
const queryIndex = source.indexOf("?");
|
|
9261
|
-
const query = queryIndex === -1 ? "" : source.slice(queryIndex + 1);
|
|
9262
|
-
if (query && [...new URLSearchParams(query).keys()].some((key) => ![
|
|
9263
|
-
"v",
|
|
9264
|
-
"t",
|
|
9265
|
-
"import"
|
|
9266
|
-
].includes(key))) return;
|
|
9267
|
-
if (findSharedKey(source, shared)) return source;
|
|
9268
|
-
const suffix = getNodeModulesSuffix(source);
|
|
9269
|
-
if (!suffix) return;
|
|
9270
|
-
const normalizedSource = stripViteFsPrefix(normalizeNodeModulePath(source));
|
|
9271
|
-
const environment = context.environment ?? !!options.ssr;
|
|
9272
|
-
const cacheable = !options.isEntry && !options.scan && (!options.kind || options.kind === "import-statement") && Object.keys(options.attributes ?? {}).length === 0 && Object.keys(options.custom ?? {}).length === 0;
|
|
9273
|
-
let cache = cacheable ? caches.get(environment) : void 0;
|
|
9274
|
-
if (cacheable && !cache) {
|
|
9275
|
-
cache = /* @__PURE__ */ new Map();
|
|
9276
|
-
caches.set(environment, cache);
|
|
9277
|
-
}
|
|
9278
|
-
if (cache?.has(normalizedSource)) return cache.get(normalizedSource);
|
|
9279
|
-
const { root, conditions } = getResolutionConfig(context, options);
|
|
9280
|
-
const result = await matchEntry(normalizedSource, suffix, async (request) => {
|
|
9281
|
-
if (!path$1.isAbsolute(request) && !isPackageExportAvailable(request, {
|
|
9282
|
-
cwd: root,
|
|
9283
|
-
conditions
|
|
9284
|
-
})) return void 0;
|
|
9285
|
-
const importer = path$1.join(root, "package.json");
|
|
9286
|
-
const lookupKey = getEntryLookupKey(request, importer);
|
|
9287
|
-
pendingEntryLookups.set(lookupKey, (pendingEntryLookups.get(lookupKey) ?? 0) + 1);
|
|
9288
|
-
try {
|
|
9289
|
-
const resolved = await context.resolve(request, importer, {
|
|
9290
|
-
...options,
|
|
9291
|
-
skipSelf: true,
|
|
9292
|
-
custom: {
|
|
9293
|
-
...options.custom,
|
|
9294
|
-
__mfSharedEntryLookup: true
|
|
9295
|
-
}
|
|
9296
|
-
});
|
|
9297
|
-
return resolved && !resolved.external ? resolved.id : void 0;
|
|
9298
|
-
} catch {
|
|
9299
|
-
return;
|
|
9300
|
-
} finally {
|
|
9301
|
-
const remaining = pendingEntryLookups.get(lookupKey) - 1;
|
|
9302
|
-
if (remaining) pendingEntryLookups.set(lookupKey, remaining);
|
|
9303
|
-
else pendingEntryLookups.delete(lookupKey);
|
|
9304
|
-
}
|
|
9305
|
-
});
|
|
9306
|
-
cache?.set(normalizedSource, result);
|
|
9307
|
-
return result;
|
|
9308
|
-
}
|
|
9309
|
-
};
|
|
9310
|
-
}
|
|
9311
|
-
//#endregion
|
|
9312
9356
|
//#region src/utils/PromiseStore.ts
|
|
9313
9357
|
/**
|
|
9314
9358
|
* example:
|
|
@@ -9522,8 +9566,9 @@ function isSharedPackageDependency(sharedKey, dependency) {
|
|
|
9522
9566
|
function isConfiguredSharedPackage(pkg, shared) {
|
|
9523
9567
|
return Object.keys(shared).some((key) => getPackageName(key) === pkg);
|
|
9524
9568
|
}
|
|
9525
|
-
function shouldKeepSharedImportLocal(source, importer, sharedKey, shared, importerPackage) {
|
|
9569
|
+
function shouldKeepSharedImportLocal(source, importer, sharedKey, shared, importerPackage, federationOptions) {
|
|
9526
9570
|
if (!importer || shared[sharedKey]?.shareConfig.import === false) return false;
|
|
9571
|
+
if (hasRemotes(federationOptions)) return false;
|
|
9527
9572
|
const prebuildImporter = importer.includes("__prebuild__") ? VirtualModule.findModule(PREBUILD_TAG, importer) : void 0;
|
|
9528
9573
|
const fallbackPackage = prebuildImporter ? getPackageName(prebuildImporter.name) : importerPackage;
|
|
9529
9574
|
if (!fallbackPackage || !isConfiguredSharedPackage(fallbackPackage, shared)) return false;
|
|
@@ -9941,7 +9986,7 @@ function proxySharedModule(options) {
|
|
|
9941
9986
|
if (shouldSkipTaggedImporterProxy(key, "__loadShare__")) return;
|
|
9942
9987
|
if (shouldSkipTaggedImporterProxy(key, "__prebuild__")) return;
|
|
9943
9988
|
const shareSource = key === "vue" && sharedSource.startsWith("vue/dist/") ? key : sharedSource;
|
|
9944
|
-
if (shouldKeepSharedImportLocal(
|
|
9989
|
+
if (shouldKeepSharedImportLocal(shareSource, importer, key, shared, importerPackage, federationOptions)) {
|
|
9945
9990
|
const localSource = getPrebuildResolutionSource(shareSource, shared[key]);
|
|
9946
9991
|
return tryResolveFromProjectRoot(localSource) || localSource;
|
|
9947
9992
|
}
|
|
@@ -10900,6 +10945,360 @@ function isTestEnv() {
|
|
|
10900
10945
|
return process.env.NODE_ENV === "test" || process.env.VITEST != null || process.env.JEST_WORKER_ID != null;
|
|
10901
10946
|
}
|
|
10902
10947
|
//#endregion
|
|
10948
|
+
//#region src/utils/hostProvidesAllShared.ts
|
|
10949
|
+
/** Virtual-module prefix of the host's per-share re-export entries. */
|
|
10950
|
+
const SHARE_MODULE_PREFIX = "\0mf-host-provided-share:";
|
|
10951
|
+
/** `./a/b` → `a/b`, `.` → `index`: the fixed entry name of an expose, without `.js`. */
|
|
10952
|
+
function exposeEntryName(exposeKey) {
|
|
10953
|
+
return exposeKey === "." ? "index" : exposeKey.replace(/^\.\//, "");
|
|
10954
|
+
}
|
|
10955
|
+
/** Bare specifier a host uses for an expose: `remote` for `.`, `remote/a/b` otherwise. */
|
|
10956
|
+
function remoteSpecifier(remoteName, exposeKey) {
|
|
10957
|
+
const entry = exposeEntryName(exposeKey);
|
|
10958
|
+
return entry === "index" ? remoteName : `${remoteName}/${entry}`;
|
|
10959
|
+
}
|
|
10960
|
+
/** `id` is a remote alias or a subpath of one. */
|
|
10961
|
+
function isRemoteSpecifier(id, remoteKeys) {
|
|
10962
|
+
return remoteKeys.some((key) => id === key || id.startsWith(`${key}/`));
|
|
10963
|
+
}
|
|
10964
|
+
/**
|
|
10965
|
+
* The shared key that owns `id`: an exact match, else the longest key `id` is a subpath
|
|
10966
|
+
* of (`lodash-es/debounce` → `lodash-es`). Used to spot an unshared subpath of a shared
|
|
10967
|
+
* package, which would bundle a private copy of that package into a remote.
|
|
10968
|
+
*/
|
|
10969
|
+
function sharedKeyOwning(id, sharedKeys) {
|
|
10970
|
+
let owner;
|
|
10971
|
+
for (const key of sharedKeys) {
|
|
10972
|
+
if (id !== key && !id.startsWith(`${key}/`)) continue;
|
|
10973
|
+
if (!owner || key.length > owner.length) owner = key;
|
|
10974
|
+
}
|
|
10975
|
+
return owner;
|
|
10976
|
+
}
|
|
10977
|
+
/** Chunk name of the host's re-export entry for a shared key. */
|
|
10978
|
+
function shareChunkName(key) {
|
|
10979
|
+
return `shared/${key.replace(/^@/, "").replace(/[^\w-]+/g, "-")}`;
|
|
10980
|
+
}
|
|
10981
|
+
/** Reads a remote's `mf-manifest.json` from a URL or a path relative to `root`. */
|
|
10982
|
+
async function readHostProvidesAllSharedManifest(location, root) {
|
|
10983
|
+
let source;
|
|
10984
|
+
if (isAbsoluteUrl(location)) {
|
|
10985
|
+
const response = await fetch(location);
|
|
10986
|
+
if (!response.ok) throw new Error(`Failed to fetch ${location}: ${response.status} ${response.statusText}`);
|
|
10987
|
+
source = await response.text();
|
|
10988
|
+
} else source = await readFile(path$1.resolve(root, location), "utf8");
|
|
10989
|
+
const manifest = JSON.parse(source);
|
|
10990
|
+
if (!manifest.metaData || !Array.isArray(manifest.exposes)) throw new Error(`${location} is not a Module Federation manifest.`);
|
|
10991
|
+
if (manifest.metaData.remoteEntry) throw new Error(`${location} describes a Module Federation runtime remote (it has a remoteEntry). Build the remote with \`experiments: { hostProvidesAllShared: true }\`.`);
|
|
10992
|
+
return manifest;
|
|
10993
|
+
}
|
|
10994
|
+
/** URL prefix of a remote's files: `metaData.publicPath`, `auto` inferred from the manifest URL. */
|
|
10995
|
+
function remotePublicPath(manifest, location) {
|
|
10996
|
+
const publicPath = manifest.metaData.publicPath ?? "auto";
|
|
10997
|
+
if (publicPath !== "auto") return ensureTrailingSlash(publicPath);
|
|
10998
|
+
if (!isAbsoluteUrl(location)) throw new Error(`${location}: publicPath is "auto", which can only be inferred from a manifest URL. Reference the manifest by URL or set \`base\` / \`publicPath\` in the remote build.`);
|
|
10999
|
+
return ensureTrailingSlash(inferAutoPublicPath(location));
|
|
11000
|
+
}
|
|
11001
|
+
/** Import-map entries (`remote/Expose` → URL) for every expose of every remote. */
|
|
11002
|
+
async function collectRemoteImports(remotes, root) {
|
|
11003
|
+
const imports = {};
|
|
11004
|
+
for (const [alias, remote] of Object.entries(remotes)) {
|
|
11005
|
+
const manifest = await readHostProvidesAllSharedManifest(remote.entry, root);
|
|
11006
|
+
const publicPath = remotePublicPath(manifest, remote.entry);
|
|
11007
|
+
for (const expose of manifest.exposes) {
|
|
11008
|
+
const file = expose.assets.js.sync[0];
|
|
11009
|
+
if (!expose.path || !file) continue;
|
|
11010
|
+
imports[remoteSpecifier(alias, expose.path)] = isAbsoluteUrl(remote.entry) ? new URL(file, new URL(publicPath, remote.entry)).href : publicPath + file;
|
|
11011
|
+
}
|
|
11012
|
+
}
|
|
11013
|
+
return imports;
|
|
11014
|
+
}
|
|
11015
|
+
/** Top-level options that only the Module Federation runtime or `remoteEntry.js` pipeline reads. */
|
|
11016
|
+
const IGNORED_OPTIONS = [
|
|
11017
|
+
"filename",
|
|
11018
|
+
"ssrFilename",
|
|
11019
|
+
"varFilename",
|
|
11020
|
+
"library",
|
|
11021
|
+
"runtime",
|
|
11022
|
+
"shareScope",
|
|
11023
|
+
"shareStrategy",
|
|
11024
|
+
"runtimePlugins",
|
|
11025
|
+
"implementation",
|
|
11026
|
+
"getPublicPath",
|
|
11027
|
+
"ignoreOrigin",
|
|
11028
|
+
"virtualModuleDir",
|
|
11029
|
+
"hostInitInjectLocation",
|
|
11030
|
+
"bundleAllCSS",
|
|
11031
|
+
"treeShakingDir",
|
|
11032
|
+
"injectTreeShakingUsedExports",
|
|
11033
|
+
"treeShakingSharedPlugins",
|
|
11034
|
+
"treeShakingSharedExcludePlugins",
|
|
11035
|
+
"moduleParseTimeout",
|
|
11036
|
+
"moduleParseIdleTimeout",
|
|
11037
|
+
"target",
|
|
11038
|
+
"ssrExternals",
|
|
11039
|
+
"ssrEntryLoader",
|
|
11040
|
+
"disableRemote",
|
|
11041
|
+
"disableShared",
|
|
11042
|
+
"disableSnapshot"
|
|
11043
|
+
];
|
|
11044
|
+
const IGNORED_EXPERIMENTS = [
|
|
11045
|
+
"externalRuntime",
|
|
11046
|
+
"provideExternalRuntime",
|
|
11047
|
+
"ssrMode"
|
|
11048
|
+
];
|
|
11049
|
+
/** Per-share options: version negotiation, aliasing and fallbacks do not exist in this mode. */
|
|
11050
|
+
const IGNORED_SHARE_OPTIONS = [
|
|
11051
|
+
"eager",
|
|
11052
|
+
"requiredVersion",
|
|
11053
|
+
"strictVersion",
|
|
11054
|
+
"import",
|
|
11055
|
+
"request",
|
|
11056
|
+
"shareKey",
|
|
11057
|
+
"name",
|
|
11058
|
+
"version",
|
|
11059
|
+
"shareScope",
|
|
11060
|
+
"treeShaking",
|
|
11061
|
+
"allowNodeModulesSuffixMatch",
|
|
11062
|
+
"suppressMissingImportWarning"
|
|
11063
|
+
];
|
|
11064
|
+
const isSet = (value) => value !== void 0 && value !== false && !(Array.isArray(value) && value.length === 0);
|
|
11065
|
+
/** A single warning naming every option the user set that import-map mode ignores. */
|
|
11066
|
+
function getIgnoredOptionWarning(options) {
|
|
11067
|
+
const shared = options.shared;
|
|
11068
|
+
const ignored = [
|
|
11069
|
+
...IGNORED_OPTIONS.filter((key) => isSet(options[key])).map((key) => `\`${key}\``),
|
|
11070
|
+
...IGNORED_EXPERIMENTS.filter((key) => isSet(options.experiments?.[key])).map((key) => `\`experiments.${key}\``),
|
|
11071
|
+
...shared && !Array.isArray(shared) ? Object.entries(shared).flatMap(([key, config]) => typeof config === "object" && config !== null ? IGNORED_SHARE_OPTIONS.filter((option) => option === "import" ? config.import !== void 0 : isSet(config[option])).map((option) => `\`shared.${key}.${option}\``) : []) : []
|
|
11072
|
+
];
|
|
11073
|
+
if (ignored.length === 0) return void 0;
|
|
11074
|
+
return `experiments.hostProvidesAllShared: ${ignored.join(", ")} ignored. The Module Federation runtime is not used and the host provides the only copy of every shared key.`;
|
|
11075
|
+
}
|
|
11076
|
+
//#endregion
|
|
11077
|
+
//#region src/plugins/pluginHostProvidesAllSharedHost.ts
|
|
11078
|
+
const ESM_SYNTAX_RE = /(^|[\s;])(export|import)[\s{*]/m;
|
|
11079
|
+
const DEFAULT_EXPORT_RE = /export\s+default\b|export\s*\{[^}]*\bas\s+default\b/;
|
|
11080
|
+
const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
|
|
11081
|
+
/**
|
|
11082
|
+
* Export names of a CommonJS package, read by loading it in Node from the project root
|
|
11083
|
+
* (what the bundler's interop exposes as named imports). Empty when it cannot be loaded
|
|
11084
|
+
* in Node, e.g. because it touches `window`.
|
|
11085
|
+
*/
|
|
11086
|
+
function cjsExportNames(key, root) {
|
|
11087
|
+
try {
|
|
11088
|
+
const exported = createRequire(path$1.join(root, "package.json"))(key);
|
|
11089
|
+
return Object.keys(exported ?? {}).filter((name) => name !== "default" && name !== "__esModule" && IDENTIFIER_RE.test(name));
|
|
11090
|
+
} catch {
|
|
11091
|
+
return [];
|
|
11092
|
+
}
|
|
11093
|
+
}
|
|
11094
|
+
/** Shape of a shared module, sniffed from its real package entry (never a pre-bundle). */
|
|
11095
|
+
function detectShareShape(key, entry, root) {
|
|
11096
|
+
const source = entry && fs$1.existsSync(entry) ? fs$1.readFileSync(entry, "utf8") : "";
|
|
11097
|
+
if (source !== "" && !ESM_SYNTAX_RE.test(source)) return {
|
|
11098
|
+
kind: "cjs",
|
|
11099
|
+
names: cjsExportNames(key, root)
|
|
11100
|
+
};
|
|
11101
|
+
return {
|
|
11102
|
+
kind: "esm",
|
|
11103
|
+
hasDefault: DEFAULT_EXPORT_RE.test(source)
|
|
11104
|
+
};
|
|
11105
|
+
}
|
|
11106
|
+
/**
|
|
11107
|
+
* Body of the re-export entry for one shared key. `export *` never forwards `default` and
|
|
11108
|
+
* forwards nothing named from CommonJS, so an ESM module adds `default` only when it has
|
|
11109
|
+
* one, and a CommonJS module re-exports each key explicitly (`react/jsx-runtime` → `jsx`,
|
|
11110
|
+
* `jsxs`) so remotes can `import { jsxs }`.
|
|
11111
|
+
*/
|
|
11112
|
+
function shareModuleCode(key, shape) {
|
|
11113
|
+
const spec = JSON.stringify(key);
|
|
11114
|
+
if (shape.kind === "cjs") return `import __mf_cjs from ${spec};\nexport default __mf_cjs;\n${shape.names.length ? `export const { ${shape.names.join(", ")} } = __mf_cjs;\n` : ""}`;
|
|
11115
|
+
return `export * from ${spec};\n${shape.hasDefault ? `export { default } from ${spec};\n` : ""}`;
|
|
11116
|
+
}
|
|
11117
|
+
/** BUILD: shared key → URL of its emitted entry chunk. */
|
|
11118
|
+
function bundledShareImports(bundle, base) {
|
|
11119
|
+
const imports = {};
|
|
11120
|
+
for (const chunk of Object.values(bundle)) if (chunk.type === "chunk" && chunk.facadeModuleId?.startsWith("\0mf-host-provided-share:")) imports[chunk.facadeModuleId.slice(24)] = `${base}${chunk.fileName}`;
|
|
11121
|
+
return imports;
|
|
11122
|
+
}
|
|
11123
|
+
/**
|
|
11124
|
+
* DEV: shared key → dev-server URL of its virtual re-export module. Vite serves `\0` ids
|
|
11125
|
+
* at `/@id/__x00__<id>`; import analysis then rewrites the module's own `from '<key>'` to
|
|
11126
|
+
* the same pre-bundle URL the host's code gets, so host and remotes share one instance.
|
|
11127
|
+
*/
|
|
11128
|
+
function devShareImports(sharedKeys, base) {
|
|
11129
|
+
const prefix = `${base}@id/__x00__${SHARE_MODULE_PREFIX.slice(1)}`;
|
|
11130
|
+
return Object.fromEntries(sharedKeys.map((key) => [key, `${prefix}${key}`]));
|
|
11131
|
+
}
|
|
11132
|
+
function importMapTag(imports) {
|
|
11133
|
+
return {
|
|
11134
|
+
tag: "script",
|
|
11135
|
+
attrs: { type: "importmap" },
|
|
11136
|
+
children: JSON.stringify({ imports }, null, 2),
|
|
11137
|
+
injectTo: "head-prepend"
|
|
11138
|
+
};
|
|
11139
|
+
}
|
|
11140
|
+
/**
|
|
11141
|
+
* Host side of `experiments.hostProvidesAllShared`. The host provides every shared key:
|
|
11142
|
+
*
|
|
11143
|
+
* 1. one re-export module per shared key, emitted in a build as a `preserveSignature:
|
|
11144
|
+
* 'strict'` entry chunk that the bundler dedupes with the host's own imports, and served
|
|
11145
|
+
* in dev as a virtual module; either way host and remotes land on one module instance;
|
|
11146
|
+
* 2. an import map in `index.html` pointing each shared key at that module and each remote
|
|
11147
|
+
* expose at the entry listed in the remote's `mf-manifest.json`.
|
|
11148
|
+
*/
|
|
11149
|
+
function pluginHostProvidesAllSharedHost(options) {
|
|
11150
|
+
const sharedKeys = Object.keys(options.shared);
|
|
11151
|
+
const remoteKeys = Object.keys(options.remotes);
|
|
11152
|
+
let isBuild = true;
|
|
11153
|
+
let base = "/";
|
|
11154
|
+
let root = process.cwd();
|
|
11155
|
+
let remoteImports = {};
|
|
11156
|
+
let resolveEntry;
|
|
11157
|
+
return {
|
|
11158
|
+
name: "module-federation:host-provides-all-shared-host",
|
|
11159
|
+
enforce: "pre",
|
|
11160
|
+
configResolved(config) {
|
|
11161
|
+
isBuild = config.command === "build";
|
|
11162
|
+
base = ensureTrailingSlash(config.base);
|
|
11163
|
+
root = config.root;
|
|
11164
|
+
resolveEntry = isBuild ? void 0 : config.createResolver();
|
|
11165
|
+
},
|
|
11166
|
+
async buildStart() {
|
|
11167
|
+
remoteImports = await collectRemoteImports(options.remotes, root);
|
|
11168
|
+
if (!isBuild) return;
|
|
11169
|
+
for (const key of sharedKeys) this.emitFile({
|
|
11170
|
+
type: "chunk",
|
|
11171
|
+
id: `${SHARE_MODULE_PREFIX}${key}`,
|
|
11172
|
+
name: shareChunkName(key),
|
|
11173
|
+
preserveSignature: "strict"
|
|
11174
|
+
});
|
|
11175
|
+
},
|
|
11176
|
+
resolveId(id) {
|
|
11177
|
+
if (id.startsWith("\0mf-host-provided-share:")) return id;
|
|
11178
|
+
if (!isRemoteSpecifier(id, remoteKeys)) return null;
|
|
11179
|
+
return {
|
|
11180
|
+
id: isBuild ? id : remoteImports[id] ?? id,
|
|
11181
|
+
external: true
|
|
11182
|
+
};
|
|
11183
|
+
},
|
|
11184
|
+
async load(id) {
|
|
11185
|
+
if (!id.startsWith("\0mf-host-provided-share:")) return null;
|
|
11186
|
+
const key = id.slice(24);
|
|
11187
|
+
const entry = resolveEntry ? await resolveEntry(key) : (await this.resolve(key, void 0, { skipSelf: true }))?.id;
|
|
11188
|
+
return shareModuleCode(key, detectShareShape(key, entry && stripQueryAndHash(entry), root));
|
|
11189
|
+
},
|
|
11190
|
+
transformIndexHtml: {
|
|
11191
|
+
order: "post",
|
|
11192
|
+
handler(html, context) {
|
|
11193
|
+
const shares = context.bundle ? bundledShareImports(context.bundle, base) : devShareImports(sharedKeys, base);
|
|
11194
|
+
return {
|
|
11195
|
+
html,
|
|
11196
|
+
tags: [importMapTag({
|
|
11197
|
+
...remoteImports,
|
|
11198
|
+
...shares
|
|
11199
|
+
})]
|
|
11200
|
+
};
|
|
11201
|
+
}
|
|
11202
|
+
}
|
|
11203
|
+
};
|
|
11204
|
+
}
|
|
11205
|
+
//#endregion
|
|
11206
|
+
//#region src/plugins/pluginHostProvidesAllSharedRemote.ts
|
|
11207
|
+
/**
|
|
11208
|
+
* Remote side of `experiments.hostProvidesAllShared`. The remote ships only its own code:
|
|
11209
|
+
*
|
|
11210
|
+
* - one fixed-name, signature-preserving entry per expose (`./Button` → `Button.js`), so
|
|
11211
|
+
* the host can map `<remote>/Button` to a stable URL;
|
|
11212
|
+
* - every shared key, and every configured remote, stays an external bare import that the
|
|
11213
|
+
* host's import map resolves at runtime;
|
|
11214
|
+
* - a standard `mf-manifest.json` without `remoteEntry`, listing each expose's entry file.
|
|
11215
|
+
*/
|
|
11216
|
+
function pluginHostProvidesAllSharedRemote(options) {
|
|
11217
|
+
const sharedKeys = Object.keys(options.shared);
|
|
11218
|
+
const remoteKeys = Object.keys(options.remotes);
|
|
11219
|
+
const isExternal = (id) => sharedKeys.includes(id) || isRemoteSpecifier(id, remoteKeys);
|
|
11220
|
+
const unsharedSubpaths = /* @__PURE__ */ new Map();
|
|
11221
|
+
let publicPath = "/";
|
|
11222
|
+
return {
|
|
11223
|
+
name: "module-federation:host-provides-all-shared-remote",
|
|
11224
|
+
enforce: "pre",
|
|
11225
|
+
apply: "build",
|
|
11226
|
+
config(config) {
|
|
11227
|
+
const root = path$1.resolve(config.root ?? process.cwd());
|
|
11228
|
+
return { build: { rollupOptions: {
|
|
11229
|
+
input: Object.fromEntries(Object.entries(options.exposes).map(([key, expose]) => [exposeEntryName(key), path$1.resolve(root, expose.import)])),
|
|
11230
|
+
external: isExternal,
|
|
11231
|
+
preserveEntrySignatures: "strict",
|
|
11232
|
+
output: {
|
|
11233
|
+
entryFileNames: "[name].js",
|
|
11234
|
+
chunkFileNames: "assets/[name]-[hash].js"
|
|
11235
|
+
}
|
|
11236
|
+
} } };
|
|
11237
|
+
},
|
|
11238
|
+
configResolved(config) {
|
|
11239
|
+
publicPath = options.publicPath ?? config.base;
|
|
11240
|
+
},
|
|
11241
|
+
buildStart() {
|
|
11242
|
+
unsharedSubpaths.clear();
|
|
11243
|
+
},
|
|
11244
|
+
resolveId(id, importer) {
|
|
11245
|
+
if (importer && !isExternal(id) && sharedKeyOwning(id, sharedKeys)) unsharedSubpaths.set(id, importer);
|
|
11246
|
+
return null;
|
|
11247
|
+
},
|
|
11248
|
+
buildEnd() {
|
|
11249
|
+
for (const [id, importer] of unsharedSubpaths) this.warn(`"${id}" (imported by ${importer}) is a subpath of a shared package but is not shared itself, so a private copy is bundled into this remote. Add it to \`shared\`.`);
|
|
11250
|
+
},
|
|
11251
|
+
generateBundle(_outputOptions, bundle) {
|
|
11252
|
+
const { name } = options;
|
|
11253
|
+
const exposes = [];
|
|
11254
|
+
for (const key of Object.keys(options.exposes)) {
|
|
11255
|
+
const entryName = exposeEntryName(key);
|
|
11256
|
+
const chunk = Object.values(bundle).find((item) => item.type === "chunk" && item.isEntry && item.name === entryName);
|
|
11257
|
+
if (!chunk) continue;
|
|
11258
|
+
exposes.push({
|
|
11259
|
+
id: `${name}:${entryName}`,
|
|
11260
|
+
name: entryName,
|
|
11261
|
+
path: key,
|
|
11262
|
+
assets: {
|
|
11263
|
+
js: {
|
|
11264
|
+
sync: [chunk.fileName],
|
|
11265
|
+
async: []
|
|
11266
|
+
},
|
|
11267
|
+
css: {
|
|
11268
|
+
sync: [],
|
|
11269
|
+
async: []
|
|
11270
|
+
}
|
|
11271
|
+
}
|
|
11272
|
+
});
|
|
11273
|
+
}
|
|
11274
|
+
const manifest = {
|
|
11275
|
+
id: name,
|
|
11276
|
+
name,
|
|
11277
|
+
metaData: {
|
|
11278
|
+
name,
|
|
11279
|
+
type: "app",
|
|
11280
|
+
buildInfo: {
|
|
11281
|
+
buildVersion: getBuildVersion(),
|
|
11282
|
+
buildName: name
|
|
11283
|
+
},
|
|
11284
|
+
globalName: name,
|
|
11285
|
+
pluginVersion: version$1,
|
|
11286
|
+
publicPath,
|
|
11287
|
+
types: resolveTypesMeta(options.dts)
|
|
11288
|
+
},
|
|
11289
|
+
shared: [],
|
|
11290
|
+
remotes: [],
|
|
11291
|
+
exposes
|
|
11292
|
+
};
|
|
11293
|
+
this.emitFile({
|
|
11294
|
+
type: "asset",
|
|
11295
|
+
fileName: typeof options.manifest === "object" && options.manifest.fileName || "mf-manifest.json",
|
|
11296
|
+
source: `${JSON.stringify(manifest, null, 2)}\n`
|
|
11297
|
+
});
|
|
11298
|
+
}
|
|
11299
|
+
};
|
|
11300
|
+
}
|
|
11301
|
+
//#endregion
|
|
10903
11302
|
//#region src/utils/normalizeOptimizeDeps.ts
|
|
10904
11303
|
var normalizeOptimizeDeps_default = {
|
|
10905
11304
|
name: "normalizeOptimizeDeps",
|
|
@@ -11514,9 +11913,21 @@ function applyBuildTimeRuntimeDefines(define, options, { target, isAstro, defaul
|
|
|
11514
11913
|
});
|
|
11515
11914
|
if (options.target && define.ENV_TARGET !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${define.ENV_TARGET}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
|
|
11516
11915
|
}
|
|
11916
|
+
/** Dynamic remote type hints are a runtime plugin; hostProvidesAllShared has no runtime. */
|
|
11917
|
+
function withoutDynamicRemoteTypeHints(options) {
|
|
11918
|
+
if (options.dev === false) return options;
|
|
11919
|
+
const dev = options.dev === true || options.dev === void 0 ? {} : options.dev;
|
|
11920
|
+
return {
|
|
11921
|
+
...options,
|
|
11922
|
+
dev: {
|
|
11923
|
+
...dev,
|
|
11924
|
+
disableDynamicRemoteTypeHints: true
|
|
11925
|
+
}
|
|
11926
|
+
};
|
|
11927
|
+
}
|
|
11517
11928
|
function loadPluginDts(options) {
|
|
11518
11929
|
if (options.dts === false) return [];
|
|
11519
|
-
return [import("./pluginDts-
|
|
11930
|
+
return [import("./pluginDts-DnEX3cp_.js").then(({ default: pluginDts }) => pluginDts(options))];
|
|
11520
11931
|
}
|
|
11521
11932
|
const INJECT_EXTERNAL_RUNTIME_CORE_PLUGIN = "@module-federation/vite/injectExternalRuntimeCorePlugin";
|
|
11522
11933
|
function isInjectExternalRuntimeCorePlugin(specifier) {
|
|
@@ -11545,11 +11956,16 @@ function applyExternalRuntimeExperiments(options) {
|
|
|
11545
11956
|
function federation(mfUserOptions) {
|
|
11546
11957
|
if (isTestEnv()) return [];
|
|
11547
11958
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
11959
|
+
if (!options.name) throw createModuleFederationError("name is required");
|
|
11960
|
+
if (options.experiments.hostProvidesAllShared) {
|
|
11961
|
+
const ignored = getIgnoredOptionWarning(mfUserOptions);
|
|
11962
|
+
if (ignored) mfWarn(ignored);
|
|
11963
|
+
return [Object.keys(options.exposes).length > 0 ? pluginHostProvidesAllSharedRemote(options) : pluginHostProvidesAllSharedHost(options), ...loadPluginDts(withoutDynamicRemoteTypeHints(options))];
|
|
11964
|
+
}
|
|
11548
11965
|
applyExternalRuntimeExperiments(options);
|
|
11549
11966
|
const isVinext = hasPackageDependency("vinext");
|
|
11550
|
-
const {
|
|
11967
|
+
const { shared, filename, hostInitInjectLocation } = options;
|
|
11551
11968
|
const hasTreeShakingShared = Object.values(shared).some((share) => !!share.shareConfig.treeShaking);
|
|
11552
|
-
if (!name) throw createModuleFederationError("name is required");
|
|
11553
11969
|
const remoteEntryId = getRemoteEntryId(options);
|
|
11554
11970
|
const virtualExposesId = getVirtualExposesId(options);
|
|
11555
11971
|
const moduleParseController = createModuleParseController();
|
|
@@ -11567,6 +11983,7 @@ function federation(mfUserOptions) {
|
|
|
11567
11983
|
let rootResolveConditions;
|
|
11568
11984
|
let ssrResolveConditions;
|
|
11569
11985
|
let ssrTarget = "node";
|
|
11986
|
+
let inheritedEnvTarget;
|
|
11570
11987
|
const emittedRuntimeCapabilityWarnings = /* @__PURE__ */ new Set();
|
|
11571
11988
|
const getLoadHookExportConditions = (context, loadOptions) => {
|
|
11572
11989
|
const environment = context.environment;
|
|
@@ -12041,11 +12458,13 @@ function federation(mfUserOptions) {
|
|
|
12041
12458
|
const isAstro = hasPackageDependency("astro");
|
|
12042
12459
|
const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
|
|
12043
12460
|
if (!config.define) config.define = {};
|
|
12461
|
+
const userSetEnvTarget = "ENV_TARGET" in config.define;
|
|
12044
12462
|
applyBuildTimeRuntimeDefines(config.define, options, {
|
|
12045
12463
|
target: resolvedTarget,
|
|
12046
12464
|
isAstro,
|
|
12047
12465
|
defaultDisableSnapshot: resolvedTarget === "node" ? true : void 0
|
|
12048
12466
|
});
|
|
12467
|
+
inheritedEnvTarget = userSetEnvTarget ? void 0 : config.define.ENV_TARGET;
|
|
12049
12468
|
for (const warning of getRuntimeCapabilityConfigurationWarnings(options)) {
|
|
12050
12469
|
if (emittedRuntimeCapabilityWarnings.has(warning)) continue;
|
|
12051
12470
|
emittedRuntimeCapabilityWarnings.add(warning);
|
|
@@ -12061,6 +12480,7 @@ function federation(mfUserOptions) {
|
|
|
12061
12480
|
if (!isServerEnvironment(name, config)) return;
|
|
12062
12481
|
const isAstro = hasPackageDependency("astro");
|
|
12063
12482
|
config.define = { ...config.define ?? {} };
|
|
12483
|
+
if (inheritedEnvTarget !== void 0 && config.define.ENV_TARGET === inheritedEnvTarget) delete config.define.ENV_TARGET;
|
|
12064
12484
|
applyBuildTimeRuntimeDefines(config.define, options, {
|
|
12065
12485
|
target: options.target ?? "node",
|
|
12066
12486
|
isAstro,
|
|
@@ -363,4 +363,4 @@ const moduleFederationConsole = {
|
|
|
363
363
|
const mfWarn = moduleFederationConsole.warn;
|
|
364
364
|
const mfError = moduleFederationConsole.error;
|
|
365
365
|
//#endregion
|
|
366
|
-
export { CodeRewriter as a,
|
|
366
|
+
export { CodeRewriter as a, isAbsoluteUrl as c, resolveHashPlaceholderFileName as d, createCodePositionMap as i, normalizePathForImport as l, mfError as n, createSourceMap as o, mfWarn as r, EXTERNAL_URL_RE as s, createModuleFederationError as t, rebaseImport as u };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { g as resolveImportPath, u as hasPackageDependency } from "./dtsConstants-
|
|
1
|
+
import { l as normalizePathForImport, n as mfError, t as createModuleFederationError } from "./logger-DOwfTlw5.js";
|
|
2
|
+
import { g as resolveImportPath, u as hasPackageDependency } from "./dtsConstants-r5OfegUr.js";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
5
|
-
import os from "os";
|
|
6
5
|
import { normalizeOptions } from "@module-federation/sdk";
|
|
6
|
+
import os from "os";
|
|
7
7
|
import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
|
|
8
8
|
import { rpc } from "@module-federation/dts-plugin/core";
|
|
9
9
|
//#region src/plugins/pluginDts.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CodeRewriter, i as createCodePositionMap, r as mfWarn, s as EXTERNAL_URL_RE } from "./logger
|
|
1
|
+
import { a as CodeRewriter, i as createCodePositionMap, r as mfWarn, s as EXTERNAL_URL_RE } from "./logger-DOwfTlw5.js";
|
|
2
2
|
//#region src/utils/fetchWithTimeout.ts
|
|
3
3
|
const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
|
|
4
4
|
const DEFAULT_SSR_FETCH_MAX_BYTES = 10 * 1024 * 1024;
|
|
@@ -483,25 +483,30 @@ async function getSSREntry(remoteEntryUrl, maxAgeMs, fetchTimeoutMs, fetchMaxByt
|
|
|
483
483
|
return record.promise;
|
|
484
484
|
}
|
|
485
485
|
/**
|
|
486
|
-
* Drop
|
|
487
|
-
* being reused.
|
|
488
|
-
*
|
|
486
|
+
* Drop a remote graph's temp-file caches after a version change so old
|
|
487
|
+
* artifacts stop being reused. A generated module can be shared by multiple
|
|
488
|
+
* remote graphs, so cleanup is scoped by graph owner rather than URL origin.
|
|
489
489
|
*/
|
|
490
490
|
function dropRemoteTempFileCaches(remoteEntryUrl) {
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
for (const [key] of tempFileCache) if (JSON.parse(key).find((part) => typeof part === "string" && /^https?:\/\//.test(part))?.startsWith(origin)) {
|
|
491
|
+
const staleRecords = [];
|
|
492
|
+
for (const [key, record] of tempFileRecords) {
|
|
493
|
+
if (!record.owners.delete(remoteEntryUrl) || record.owners.size > 0) continue;
|
|
495
494
|
tempFileCache.delete(key);
|
|
496
495
|
tempFilePathCache.delete(key);
|
|
496
|
+
staleRecords.push(record);
|
|
497
497
|
}
|
|
498
498
|
scheduleTempFileCleanup(staleRecords);
|
|
499
499
|
}
|
|
500
|
-
/**
|
|
501
|
-
|
|
500
|
+
/**
|
|
501
|
+
* Invalidate all SSR loader caches owned by a remote. The runtime loading map
|
|
502
|
+
* is cleared synchronously so `revalidate()` can safely clear module caches
|
|
503
|
+
* immediately after invoking this function.
|
|
504
|
+
*/
|
|
505
|
+
function invalidateRemoteCaches(remoteEntryUrl) {
|
|
506
|
+
invalidateRuntimeRemoteEntry(remoteEntryUrl);
|
|
502
507
|
dropRemoteTempFileCaches(remoteEntryUrl);
|
|
503
508
|
scheduleVmCacheCleanup(remoteEntryUrl);
|
|
504
|
-
|
|
509
|
+
return clearRunnerCaches(remoteEntryUrl);
|
|
505
510
|
}
|
|
506
511
|
/**
|
|
507
512
|
* The VM strategy owns separate in-memory module-graph caches. Keep their
|
|
@@ -512,6 +517,12 @@ function scheduleVmCacheCleanup(remoteEntryUrl) {
|
|
|
512
517
|
if (!vmStrategyModulePromise) return;
|
|
513
518
|
vmStrategyModulePromise.then(({ clearVmStrategyCaches }) => clearVmStrategyCaches(remoteEntryUrl)).catch(() => {});
|
|
514
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* A dev-mode ModuleRunner is created per origin (one Vite dev server) and is
|
|
522
|
+
* shared by every remote that server hosts, so its module cache can only be
|
|
523
|
+
* cleared as a whole. Origin scoping is therefore the finest granularity here;
|
|
524
|
+
* clearing only costs a re-evaluation, no generated files are removed.
|
|
525
|
+
*/
|
|
515
526
|
async function clearRunnerCaches(remoteEntryUrl) {
|
|
516
527
|
let remoteOrigin;
|
|
517
528
|
if (remoteEntryUrl) {
|
|
@@ -526,7 +537,9 @@ async function clearRunnerCaches(remoteEntryUrl) {
|
|
|
526
537
|
}
|
|
527
538
|
/** Invalidate all SSR loader caches across every remote. */
|
|
528
539
|
function invalidateAllRemoteCaches() {
|
|
540
|
+
invalidateRuntimeRemoteEntry();
|
|
529
541
|
const staleRecords = [...tempFileRecords.values()];
|
|
542
|
+
for (const record of staleRecords) record.owners.clear();
|
|
530
543
|
tempFileCache.clear();
|
|
531
544
|
tempFilePathCache.clear();
|
|
532
545
|
scheduleTempFileCleanup(staleRecords);
|
|
@@ -534,6 +547,17 @@ function invalidateAllRemoteCaches() {
|
|
|
534
547
|
clearRunnerCaches();
|
|
535
548
|
}
|
|
536
549
|
/**
|
|
550
|
+
* runtime-core deduplicates remote entry loads in a process-global map. The
|
|
551
|
+
* SSR loader's module cache invalidation must evict the matching runtime entry
|
|
552
|
+
* too, otherwise the next loadRemote() returns the pre-revalidation container
|
|
553
|
+
* without invoking this loader again.
|
|
554
|
+
*/
|
|
555
|
+
function invalidateRuntimeRemoteEntry(remoteEntryUrl) {
|
|
556
|
+
const globalLoading = globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__;
|
|
557
|
+
if (!globalLoading) return;
|
|
558
|
+
for (const key of Object.keys(globalLoading)) if (remoteEntryUrl === void 0 || key === remoteEntryUrl || key.endsWith(`:${remoteEntryUrl}`)) delete globalLoading[key];
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
537
561
|
* Drop the loader's caches so the next `loadEntry` re-resolves and re-fetches
|
|
538
562
|
* remote SSR entries. Pass a remote entry URL to scope the invalidation to one
|
|
539
563
|
* remote; call with no arguments to invalidate everything.
|
|
@@ -579,6 +603,7 @@ function scheduleTempFileCleanup(records) {
|
|
|
579
603
|
async function cleanupTempFileRecord(record) {
|
|
580
604
|
try {
|
|
581
605
|
await record.promise.catch(() => void 0);
|
|
606
|
+
if (record.owners.size > 0) return;
|
|
582
607
|
if (tempFileRecords.get(record.cacheKey) !== record) return;
|
|
583
608
|
const filePath = await record.filePathPromise;
|
|
584
609
|
const { rmSync } = await _fs();
|
|
@@ -733,7 +758,7 @@ function getTempFileImportUrl(filePath, versionKey) {
|
|
|
733
758
|
* a remote redeploy (new manifest → new key) produces new files and bypasses
|
|
734
759
|
* Node's ESM module cache instead of serving the stale build.
|
|
735
760
|
*/
|
|
736
|
-
async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs") {
|
|
761
|
+
async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", owner) {
|
|
737
762
|
const cacheKey = JSON.stringify([
|
|
738
763
|
fetchTimeoutMs,
|
|
739
764
|
fetchMaxBytes,
|
|
@@ -745,6 +770,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
|
|
|
745
770
|
if (visited.has(url)) return visited.get(url);
|
|
746
771
|
const cached = tempFileCache.get(cacheKey);
|
|
747
772
|
if (cached) {
|
|
773
|
+
if (owner) tempFileRecords.get(cacheKey)?.owners.add(owner);
|
|
748
774
|
pending.add(cached);
|
|
749
775
|
const reserved = tempFilePathCache.get(cacheKey);
|
|
750
776
|
const tmpFile = reserved ? await reserved : await cached;
|
|
@@ -768,7 +794,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
|
|
|
768
794
|
const relImports = specifiers.map(({ value }) => value).filter((value) => isRelativeSpecifier(value) && !isVitePreloadHelperSpecifier(value)).map((value) => new URL(value, base).href);
|
|
769
795
|
const subMap = /* @__PURE__ */ new Map();
|
|
770
796
|
await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
|
|
771
|
-
const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension);
|
|
797
|
+
const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
|
|
772
798
|
subMap.set(u, getTempFileImportUrl(tmpPath, versionKey));
|
|
773
799
|
}));
|
|
774
800
|
const requireShimUrl = sharedPkgMap?.size && specifiers.some(({ value }) => isNodeModuleSpecifier(value)) ? await getRequireShimUrl(tmpDir, sharedPkgMap, contextKey) : void 0;
|
|
@@ -781,7 +807,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
|
|
|
781
807
|
cacheKey,
|
|
782
808
|
filePathPromise: tmpFilePromise,
|
|
783
809
|
promise,
|
|
784
|
-
|
|
810
|
+
owners: owner ? /* @__PURE__ */ new Set([owner]) : /* @__PURE__ */ new Set()
|
|
785
811
|
};
|
|
786
812
|
tempFileRecords.set(cacheKey, record);
|
|
787
813
|
tempFileCache.set(cacheKey, promise);
|
|
@@ -793,9 +819,9 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
|
|
|
793
819
|
});
|
|
794
820
|
return promise;
|
|
795
821
|
}
|
|
796
|
-
async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs") {
|
|
822
|
+
async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", owner) {
|
|
797
823
|
const pending = /* @__PURE__ */ new Set();
|
|
798
|
-
const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension);
|
|
824
|
+
const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
|
|
799
825
|
await Promise.all(pending);
|
|
800
826
|
return rootFile;
|
|
801
827
|
}
|
|
@@ -808,7 +834,7 @@ async function importTempModule(filePath, versionKey) {
|
|
|
808
834
|
let warnedVmUnavailable = false;
|
|
809
835
|
let vmStrategyModulePromise;
|
|
810
836
|
function getVmStrategyModule() {
|
|
811
|
-
if (!vmStrategyModulePromise) vmStrategyModulePromise = import("./ssrVmStrategy-
|
|
837
|
+
if (!vmStrategyModulePromise) vmStrategyModulePromise = import("./ssrVmStrategy-COMHOCN2.js");
|
|
812
838
|
return vmStrategyModulePromise;
|
|
813
839
|
}
|
|
814
840
|
/**
|
|
@@ -819,7 +845,7 @@ function getVmStrategyModule() {
|
|
|
819
845
|
function warnStrategyFallback(strategy, url, fallback, error) {
|
|
820
846
|
mfWarn(`SSR entry loader: strategy "${strategy}" failed to load ${url}; falling back to ${fallback}.`, error);
|
|
821
847
|
}
|
|
822
|
-
async function tryVmStrategy(ssrEntry, options) {
|
|
848
|
+
async function tryVmStrategy(ssrEntry, options, rootEntryUrl) {
|
|
823
849
|
const { loadViaVmStrategy, isVmStrategyAvailable } = await getVmStrategyModule();
|
|
824
850
|
if (!await isVmStrategyAvailable()) {
|
|
825
851
|
if (!warnedVmUnavailable) {
|
|
@@ -835,10 +861,11 @@ async function tryVmStrategy(ssrEntry, options) {
|
|
|
835
861
|
fetchTimeoutMs: options.fetchTimeoutMs,
|
|
836
862
|
fetchMaxBytes: options.fetchMaxBytes,
|
|
837
863
|
cacheContext: options.cacheContext,
|
|
838
|
-
federationInstance: options.federationInstance
|
|
864
|
+
federationInstance: options.federationInstance,
|
|
865
|
+
rootEntryUrl
|
|
839
866
|
});
|
|
840
867
|
}
|
|
841
|
-
async function loadSSRRemoteEntry(ssrEntry, options) {
|
|
868
|
+
async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
|
|
842
869
|
const { url, type, versionKey } = ssrEntry;
|
|
843
870
|
const { resolvedShared } = options;
|
|
844
871
|
if (type === "commonjs-module" || type === "commonjs") {
|
|
@@ -865,7 +892,7 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
|
|
|
865
892
|
}
|
|
866
893
|
}
|
|
867
894
|
if (options.strategy === "vm") try {
|
|
868
|
-
const fromVm = await tryVmStrategy(ssrEntry, options);
|
|
895
|
+
const fromVm = await tryVmStrategy(ssrEntry, options, rootEntryUrl);
|
|
869
896
|
if (fromVm) return fromVm;
|
|
870
897
|
} catch (error) {
|
|
871
898
|
if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
|
|
@@ -877,7 +904,7 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
|
|
|
877
904
|
const sharedPkgMap = new Map(Object.entries(resolvedShared));
|
|
878
905
|
const extension = type === "commonjs-module" || type === "commonjs" ? ".js" : ".mjs";
|
|
879
906
|
try {
|
|
880
|
-
return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension), versionKey);
|
|
907
|
+
return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension, rootEntryUrl), versionKey);
|
|
881
908
|
} catch (error) {
|
|
882
909
|
if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
|
|
883
910
|
warnStrategyFallback("temp-file", url, "the federation runtime's loader", error);
|
|
@@ -914,11 +941,11 @@ function ssrEntryLoaderPlugin(options = {}) {
|
|
|
914
941
|
} : resolved;
|
|
915
942
|
const ssrEntry = await getSSREntry(remoteInfo.entry, loadOptions.maxAgeMs, loadOptions.fetchTimeoutMs, loadOptions.fetchMaxBytes);
|
|
916
943
|
if (!ssrEntry) return;
|
|
917
|
-
const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions);
|
|
944
|
+
const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions, remoteInfo.entry);
|
|
918
945
|
if (!mod) return;
|
|
919
946
|
return mod;
|
|
920
947
|
}
|
|
921
948
|
};
|
|
922
949
|
}
|
|
923
950
|
//#endregion
|
|
924
|
-
export {
|
|
951
|
+
export { DEFAULT_SSR_FETCH_MAX_BYTES as a, readResponseTextBounded as c, ssrEntryLoaderPlugin as i, neutralizeBrowserPreloadHelpers as n, DEFAULT_SSR_FETCH_TIMEOUT_MS as o, revalidate as r, fetchWithTimeout as s, SsrEntryHttpError as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as findSharedKey } from "./sharedKeyMatcher-Od_nYxJA.js";
|
|
2
|
-
import {
|
|
2
|
+
import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-DEH3eV1e.js";
|
|
3
3
|
//#region src/utils/ssrVmStrategy.ts
|
|
4
4
|
/**
|
|
5
5
|
* vm.SourceTextModule strategy for loading remote SSR entries.
|
|
@@ -144,30 +144,24 @@ function addVmCacheOwner(ownersByCacheKey, cacheKey, rootEntryUrl) {
|
|
|
144
144
|
owners.add(rootEntryUrl);
|
|
145
145
|
ownersByCacheKey.set(cacheKey, owners);
|
|
146
146
|
}
|
|
147
|
-
function clearOwnedVmCache(cache, ownersByCacheKey,
|
|
147
|
+
function clearOwnedVmCache(cache, ownersByCacheKey, remoteEntryUrl) {
|
|
148
148
|
for (const [cacheKey, owners] of ownersByCacheKey) {
|
|
149
|
-
if (!
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
for (const owner of owners) if (getUrlOrigin(owner) === remoteOrigin) owners.delete(owner);
|
|
155
|
-
if (owners.size === 0) {
|
|
156
|
-
cache.delete(cacheKey);
|
|
157
|
-
ownersByCacheKey.delete(cacheKey);
|
|
158
|
-
}
|
|
149
|
+
if (remoteEntryUrl !== void 0 && !owners.delete(remoteEntryUrl)) continue;
|
|
150
|
+
if (remoteEntryUrl !== void 0 && owners.size > 0) continue;
|
|
151
|
+
cache.delete(cacheKey);
|
|
152
|
+
ownersByCacheKey.delete(cacheKey);
|
|
159
153
|
}
|
|
160
154
|
}
|
|
161
155
|
/**
|
|
162
156
|
* Drop cached VM module graphs after a remote revalidation. A module graph can
|
|
163
|
-
* contain URLs from another origin,
|
|
164
|
-
*
|
|
157
|
+
* contain URLs from another origin, and one origin can serve several remotes,
|
|
158
|
+
* so ownership is tracked by the owning remote entry URL rather than inferred
|
|
159
|
+
* from each module URL. A module shared by several graphs stays cached while
|
|
160
|
+
* another remote still owns it.
|
|
165
161
|
*/
|
|
166
162
|
function clearVmStrategyCaches(remoteEntryUrl) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
clearOwnedVmCache(httpModuleCache, httpModuleCacheOwners, remoteOrigin);
|
|
170
|
-
clearOwnedVmCache(namespaceCache, namespaceCacheOwners, remoteOrigin);
|
|
163
|
+
clearOwnedVmCache(httpModuleCache, httpModuleCacheOwners, remoteEntryUrl);
|
|
164
|
+
clearOwnedVmCache(namespaceCache, namespaceCacheOwners, remoteEntryUrl);
|
|
171
165
|
}
|
|
172
166
|
function getBodyPreview(body) {
|
|
173
167
|
return body.slice(0, 240).replace(/\s+/g, " ").trim();
|
|
@@ -243,12 +237,13 @@ async function importDynamically(vm, specifier, referencingModule, options) {
|
|
|
243
237
|
async function loadViaVmStrategy(entryUrl, options) {
|
|
244
238
|
const vm = await getVmApi();
|
|
245
239
|
if (!vm) return null;
|
|
240
|
+
const rootEntryUrl = options.rootEntryUrl ?? entryUrl;
|
|
246
241
|
const graphOptions = {
|
|
247
242
|
...options,
|
|
248
|
-
rootEntryUrl
|
|
243
|
+
rootEntryUrl
|
|
249
244
|
};
|
|
250
245
|
const cacheKey = `${getVmCacheContextKey(options)}::${options.versionKey}::${entryUrl}`;
|
|
251
|
-
addVmCacheOwner(namespaceCacheOwners, cacheKey,
|
|
246
|
+
addVmCacheOwner(namespaceCacheOwners, cacheKey, rootEntryUrl);
|
|
252
247
|
if (!namespaceCache.has(cacheKey)) namespaceCache.set(cacheKey, (async () => {
|
|
253
248
|
const entryModule = await getHttpModule(vm, entryUrl, graphOptions);
|
|
254
249
|
const linker = (specifier, referencingModule) => linkModule(vm, specifier, referencingModule, graphOptions);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-
|
|
1
|
+
import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-DEH3eV1e.js";
|
|
2
2
|
export { SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
|