@softarc/native-federation-orchestrator 4.4.0 → 4.5.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/README.md +2 -2
- package/fesm2022/@softarc/native-federation-orchestrator.mjs +523 -82
- package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
- package/fesm2022/module-federation.mjs +21 -17
- package/fesm2022/module-federation.mjs.map +2 -2
- package/fesm2022/node.mjs +453 -76
- package/fesm2022/node.mjs.map +4 -4
- package/fesm2022/sdk.mjs +63 -1
- package/fesm2022/sdk.mjs.map +3 -3
- package/package.json +1 -1
- package/quickstart.mjs +13 -13
- package/types/lib/core/1.domain/externals/version.contract.d.ts +3 -2
- package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +8 -4
- package/types/lib/core/2.app/config/mode.contract.d.ts +7 -0
- package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +4 -0
- package/types/lib/core/2.app/driver-ports/init/for-pooling-dynamic-externals.port.d.ts +8 -0
- package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/index.d.ts +2 -0
- package/types/lib/core/2.app/driving-ports/for-shared-externals-storage.port.d.ts +2 -0
- package/types/lib/core/2.app/steps/convert-to-import-map.d.ts +2 -1
- package/types/lib/core/2.app/steps/pooling/pool-dynamic-externals.d.ts +3 -0
- package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +25 -0
- package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +5 -0
- package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +25 -0
- package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +4 -0
- package/types/lib/core/2.app/steps/store-remote-entry.d.ts +2 -2
- package/types/lib/core/3.adapters/http/remote-entry-provider.d.ts +2 -1
package/fesm2022/node.mjs
CHANGED
|
@@ -1560,7 +1560,14 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1560
1560
|
);
|
|
1561
1561
|
if (config.clearStorage) STORAGE.clear();
|
|
1562
1562
|
const _cache = STORAGE.get();
|
|
1563
|
+
let _sawPoolTag = false;
|
|
1563
1564
|
return {
|
|
1565
|
+
markPoolTagPresent: function() {
|
|
1566
|
+
_sawPoolTag = true;
|
|
1567
|
+
},
|
|
1568
|
+
hasPoolTag: function() {
|
|
1569
|
+
return _sawPoolTag;
|
|
1570
|
+
},
|
|
1564
1571
|
getFromScope: function(shareScope) {
|
|
1565
1572
|
return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
|
|
1566
1573
|
},
|
|
@@ -1871,7 +1878,11 @@ var createModeConfig = (override) => {
|
|
|
1871
1878
|
};
|
|
1872
1879
|
return {
|
|
1873
1880
|
strict: strictnessConfig,
|
|
1874
|
-
profile: { ...defaultProfile, ...override.profile ?? {} }
|
|
1881
|
+
profile: { ...defaultProfile, ...override.profile ?? {} },
|
|
1882
|
+
feature: {
|
|
1883
|
+
convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false,
|
|
1884
|
+
useAutoExternalPooling: override.feature?.useAutoExternalPooling ?? false
|
|
1885
|
+
}
|
|
1875
1886
|
};
|
|
1876
1887
|
};
|
|
1877
1888
|
|
|
@@ -1890,7 +1901,7 @@ var createInitFlow = ({
|
|
|
1890
1901
|
adapters,
|
|
1891
1902
|
config
|
|
1892
1903
|
}) => {
|
|
1893
|
-
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.determineSharedExternals).then(flow.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
|
|
1904
|
+
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.determineSharedExternals).then(flow.poolSharedExternals).then(flow.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
|
|
1894
1905
|
config,
|
|
1895
1906
|
adapters,
|
|
1896
1907
|
loadRemoteModule
|
|
@@ -1901,7 +1912,7 @@ var createInitFlow = ({
|
|
|
1901
1912
|
var createInitRemoteEntryFlow = ({
|
|
1902
1913
|
flow
|
|
1903
1914
|
}) => {
|
|
1904
|
-
const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.convertToImportMap).then(flow.commitChanges);
|
|
1915
|
+
const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.poolDynamicExternals).then(flow.convertToImportMap).then(flow.commitChanges);
|
|
1905
1916
|
return (remoteEntryUrl, remote) => flow.getRemoteEntry(remoteEntryUrl, remote).then((entry) => entry.map(processDynamicRemoteEntry).orElse(Promise.resolve())).then(() => void 0);
|
|
1906
1917
|
};
|
|
1907
1918
|
|
|
@@ -1918,16 +1929,25 @@ function createGetRemoteEntries(config, ports) {
|
|
|
1918
1929
|
};
|
|
1919
1930
|
function addHostRemoteEntry(manifest) {
|
|
1920
1931
|
if (!config.hostRemoteEntry) return manifest;
|
|
1921
|
-
const { name, url,
|
|
1922
|
-
const urlWithCache = cacheTag ? `${url}?cacheTag=${cacheTag}` : url;
|
|
1932
|
+
const { name, url, integrity } = config.hostRemoteEntry;
|
|
1923
1933
|
return {
|
|
1924
1934
|
...manifest,
|
|
1925
|
-
[name]: integrity ? { url
|
|
1935
|
+
[name]: integrity ? { url, integrity } : url
|
|
1926
1936
|
};
|
|
1927
1937
|
}
|
|
1928
1938
|
function normalizeEntry(descriptor) {
|
|
1929
1939
|
return typeof descriptor === "string" ? { url: descriptor } : descriptor;
|
|
1930
1940
|
}
|
|
1941
|
+
function withCacheTag(url, cacheTag) {
|
|
1942
|
+
if (!cacheTag) return url;
|
|
1943
|
+
return `${url}${url.includes("?") ? "&" : "?"}cacheTag=${cacheTag}`;
|
|
1944
|
+
}
|
|
1945
|
+
function resolveCacheTag(remoteName) {
|
|
1946
|
+
if (config.hostRemoteEntry && remoteName === config.hostRemoteEntry.name && config.hostRemoteEntry.cacheTag) {
|
|
1947
|
+
return config.hostRemoteEntry.cacheTag;
|
|
1948
|
+
}
|
|
1949
|
+
return config.profile.cacheTag;
|
|
1950
|
+
}
|
|
1931
1951
|
async function fetchRemoteEntries(manifest) {
|
|
1932
1952
|
const fetchPromises = Object.entries(manifest).map(
|
|
1933
1953
|
([remoteName, descriptor]) => fetchRemoteEntry(remoteName, descriptor)
|
|
@@ -1948,8 +1968,9 @@ function createGetRemoteEntries(config, ports) {
|
|
|
1948
1968
|
}
|
|
1949
1969
|
});
|
|
1950
1970
|
if (skip) return false;
|
|
1971
|
+
const fetchUrl = withCacheTag(remoteEntryUrl, resolveCacheTag(remoteName));
|
|
1951
1972
|
try {
|
|
1952
|
-
const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(
|
|
1973
|
+
const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(fetchUrl, { integrity }) : await ports.remoteEntryProvider.provide(fetchUrl);
|
|
1953
1974
|
config.log.debug(
|
|
1954
1975
|
1,
|
|
1955
1976
|
`Fetched '${remoteEntry.name}' from '${remoteEntry.url}', exposing: ${JSON.stringify(remoteEntry.exposes)}`
|
|
@@ -2027,6 +2048,7 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2027
2048
|
const tag = resolveVersion(remoteEntry, external);
|
|
2028
2049
|
if (tag === null) return;
|
|
2029
2050
|
if (external.singleton) {
|
|
2051
|
+
if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
|
|
2030
2052
|
onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
|
|
2031
2053
|
} else {
|
|
2032
2054
|
addScopedExternal(remoteEntry, external, tag);
|
|
@@ -2046,8 +2068,8 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2046
2068
|
function addScopedExternal(remoteEntry, sharedInfo, tag) {
|
|
2047
2069
|
ports.scopedExternalsRepo.addExternal(remoteEntry.name, sharedInfo.packageName, {
|
|
2048
2070
|
tag,
|
|
2049
|
-
|
|
2050
|
-
|
|
2071
|
+
bundle: sharedInfo.bundle,
|
|
2072
|
+
entries: sharedInfo.entries
|
|
2051
2073
|
});
|
|
2052
2074
|
}
|
|
2053
2075
|
function resolveVersion(remoteEntry, external) {
|
|
@@ -2069,12 +2091,13 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2069
2091
|
function sharedExternalContext(remoteEntry, sharedInfo, tag) {
|
|
2070
2092
|
const scopeType = ports.sharedExternalsRepo.scopeType(sharedInfo.shareScope);
|
|
2071
2093
|
const remote = {
|
|
2072
|
-
file: sharedInfo.outFileName,
|
|
2073
2094
|
name: remoteEntry.name,
|
|
2074
2095
|
bundle: sharedInfo.bundle,
|
|
2075
2096
|
strictVersion: sharedInfo.strictVersion,
|
|
2076
2097
|
cached: false,
|
|
2077
|
-
requiredVersion: scopeType === "strict" ? tag : sharedInfo.requiredVersion || tag
|
|
2098
|
+
requiredVersion: scopeType === "strict" ? tag : sharedInfo.requiredVersion || tag,
|
|
2099
|
+
...sharedInfo.pool?.trim() ? { pool: sharedInfo.pool } : {},
|
|
2100
|
+
entries: sharedInfo.entries
|
|
2078
2101
|
};
|
|
2079
2102
|
const cached2 = ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).orElse({ dirty: false, versions: [] });
|
|
2080
2103
|
return {
|
|
@@ -2222,7 +2245,290 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2222
2245
|
}
|
|
2223
2246
|
}
|
|
2224
2247
|
|
|
2225
|
-
//
|
|
2248
|
+
// src/lib/core/2.app/steps/pooling/pool-graph.ts
|
|
2249
|
+
var SCOPED_PACKAGE = /^@([^/]+)\//;
|
|
2250
|
+
function createDSU() {
|
|
2251
|
+
const ids = /* @__PURE__ */ new Map();
|
|
2252
|
+
const parent = [];
|
|
2253
|
+
const size = [];
|
|
2254
|
+
const intern = (key) => {
|
|
2255
|
+
let id = ids.get(key);
|
|
2256
|
+
if (id === void 0) {
|
|
2257
|
+
id = parent.length;
|
|
2258
|
+
ids.set(key, id);
|
|
2259
|
+
parent.push(id);
|
|
2260
|
+
size.push(1);
|
|
2261
|
+
}
|
|
2262
|
+
return id;
|
|
2263
|
+
};
|
|
2264
|
+
const findRoot = (x) => {
|
|
2265
|
+
while (parent[x] !== x) {
|
|
2266
|
+
parent[x] = parent[parent[x]];
|
|
2267
|
+
x = parent[x];
|
|
2268
|
+
}
|
|
2269
|
+
return x;
|
|
2270
|
+
};
|
|
2271
|
+
return {
|
|
2272
|
+
union(a, b) {
|
|
2273
|
+
let rootA = findRoot(intern(a));
|
|
2274
|
+
let rootB = findRoot(intern(b));
|
|
2275
|
+
if (rootA === rootB) return;
|
|
2276
|
+
if (size[rootA] < size[rootB]) [rootA, rootB] = [rootB, rootA];
|
|
2277
|
+
parent[rootB] = rootA;
|
|
2278
|
+
size[rootA] = size[rootA] + size[rootB];
|
|
2279
|
+
},
|
|
2280
|
+
// Interns the key if unseen — an isolated key is its own component.
|
|
2281
|
+
component(key) {
|
|
2282
|
+
return findRoot(intern(key));
|
|
2283
|
+
}
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
var extNode = (name) => `ext\0${name}`;
|
|
2287
|
+
var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
|
|
2288
|
+
var scopeNode = (scope) => `scope\0${scope}`;
|
|
2289
|
+
function autoScope(name, useAutoExternalPooling) {
|
|
2290
|
+
return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
|
|
2291
|
+
}
|
|
2292
|
+
function groupByMembership(candidates, log) {
|
|
2293
|
+
const dsu = createDSU();
|
|
2294
|
+
const tagged = /* @__PURE__ */ new Set();
|
|
2295
|
+
const poolable = [];
|
|
2296
|
+
for (const candidate of candidates) {
|
|
2297
|
+
let joined = false;
|
|
2298
|
+
if (candidate.scope !== void 0) {
|
|
2299
|
+
dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
|
|
2300
|
+
joined = true;
|
|
2301
|
+
}
|
|
2302
|
+
for (const edge of candidate.tags) {
|
|
2303
|
+
dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
|
|
2304
|
+
tagged.add(candidate.name);
|
|
2305
|
+
joined = true;
|
|
2306
|
+
}
|
|
2307
|
+
if (joined) poolable.push({ name: candidate.name, value: candidate.value });
|
|
2308
|
+
}
|
|
2309
|
+
const byComponent = /* @__PURE__ */ new Map();
|
|
2310
|
+
for (const entry of poolable) {
|
|
2311
|
+
const root = dsu.component(extNode(entry.name));
|
|
2312
|
+
(byComponent.get(root) ?? byComponent.set(root, []).get(root)).push(entry);
|
|
2313
|
+
}
|
|
2314
|
+
const pools = [];
|
|
2315
|
+
for (const members of byComponent.values()) {
|
|
2316
|
+
members.sort((a, b) => a.name.localeCompare(b.name));
|
|
2317
|
+
if (members.length < 2) {
|
|
2318
|
+
const only = members[0];
|
|
2319
|
+
if (tagged.has(only.name)) {
|
|
2320
|
+
log?.warn(
|
|
2321
|
+
3,
|
|
2322
|
+
`[${only.name}] declares a 'pool' tag but no other external joined its pool; likely a typo or a missing sibling.`
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
pools.push(members);
|
|
2328
|
+
}
|
|
2329
|
+
pools.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
2330
|
+
return new Map(pools.map((members) => [members[0].name, members.map((m) => m.value)]));
|
|
2331
|
+
}
|
|
2332
|
+
function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
2333
|
+
const candidates = Object.entries(sharedExternals).map(
|
|
2334
|
+
([name, external]) => ({
|
|
2335
|
+
name,
|
|
2336
|
+
scope: autoScope(name, useAutoExternalPooling),
|
|
2337
|
+
tags: external.versions.flatMap(
|
|
2338
|
+
(v) => v.remotes.flatMap((r) => {
|
|
2339
|
+
const tag = r.pool?.trim();
|
|
2340
|
+
return tag ? [{ remote: r.name, tag }] : [];
|
|
2341
|
+
})
|
|
2342
|
+
),
|
|
2343
|
+
value: { name, external }
|
|
2344
|
+
})
|
|
2345
|
+
);
|
|
2346
|
+
return groupByMembership(candidates, log);
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// src/lib/core/2.app/steps/pooling/pool.util.ts
|
|
2350
|
+
function versionForRemote(member, remote) {
|
|
2351
|
+
return member.external.versions.find((v) => v.remotes.some((r) => r.name === remote));
|
|
2352
|
+
}
|
|
2353
|
+
function remotesInPool(members) {
|
|
2354
|
+
return [
|
|
2355
|
+
...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
|
|
2356
|
+
];
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
// src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
|
|
2360
|
+
function poolWinners(members) {
|
|
2361
|
+
return members.map((member) => {
|
|
2362
|
+
const share = member.external.versions.find((v) => v.action === "share");
|
|
2363
|
+
return {
|
|
2364
|
+
member,
|
|
2365
|
+
tag: share?.tag ?? "",
|
|
2366
|
+
providers: new Set((share?.remotes ?? []).map((r) => r.name))
|
|
2367
|
+
};
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
function selectCoverageAnchor(winners) {
|
|
2371
|
+
const coverage = /* @__PURE__ */ new Map();
|
|
2372
|
+
for (const winner of winners)
|
|
2373
|
+
for (const remote of winner.providers) coverage.set(remote, (coverage.get(remote) ?? 0) + 1);
|
|
2374
|
+
if (coverage.size === 0) return void 0;
|
|
2375
|
+
const anchorRemote = [...coverage.keys()].sort(
|
|
2376
|
+
(a, b) => coverage.get(b) - coverage.get(a) || a.localeCompare(b)
|
|
2377
|
+
)[0];
|
|
2378
|
+
return {
|
|
2379
|
+
anchorRemote,
|
|
2380
|
+
tagPerMember: Object.fromEntries(
|
|
2381
|
+
winners.filter((w) => w.providers.has(anchorRemote)).map((w) => [w.member.name, w.tag])
|
|
2382
|
+
)
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function classifyPoolConservative(members, anchor) {
|
|
2386
|
+
const classification = /* @__PURE__ */ new Map();
|
|
2387
|
+
for (const remote of remotesInPool(members)) {
|
|
2388
|
+
let coverageForced = false;
|
|
2389
|
+
let verdict = "follow";
|
|
2390
|
+
for (const member of members) {
|
|
2391
|
+
const version = versionForRemote(member, remote);
|
|
2392
|
+
if (!version) continue;
|
|
2393
|
+
const anchorTag = anchor.tagPerMember[member.name];
|
|
2394
|
+
if (!anchorTag) {
|
|
2395
|
+
coverageForced = true;
|
|
2396
|
+
continue;
|
|
2397
|
+
}
|
|
2398
|
+
if (version.tag === anchorTag) continue;
|
|
2399
|
+
if (version.action === "scope") {
|
|
2400
|
+
verdict = "scope-incompat";
|
|
2401
|
+
break;
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
classification.set(
|
|
2405
|
+
remote,
|
|
2406
|
+
verdict === "scope-incompat" ? "scope-incompat" : coverageForced ? "scope-coverage" : "follow"
|
|
2407
|
+
);
|
|
2408
|
+
}
|
|
2409
|
+
classification.set(anchor.anchorRemote, "follow");
|
|
2410
|
+
return classification;
|
|
2411
|
+
}
|
|
2412
|
+
function createPoolSharedExternals(config, ports) {
|
|
2413
|
+
return () => {
|
|
2414
|
+
const { useAutoExternalPooling } = config.feature;
|
|
2415
|
+
if (!useAutoExternalPooling && !ports.sharedExternalsRepo.hasPoolTag()) {
|
|
2416
|
+
return Promise.resolve();
|
|
2417
|
+
}
|
|
2418
|
+
for (const scope of ports.sharedExternalsRepo.getScopes()) {
|
|
2419
|
+
if (ports.sharedExternalsRepo.scopeType(scope) === "strict") continue;
|
|
2420
|
+
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
2421
|
+
try {
|
|
2422
|
+
for (const [poolName, members] of buildPools(
|
|
2423
|
+
sharedExternals,
|
|
2424
|
+
useAutoExternalPooling,
|
|
2425
|
+
config.log
|
|
2426
|
+
)) {
|
|
2427
|
+
poolFamily(poolName, members, scope);
|
|
2428
|
+
}
|
|
2429
|
+
} catch (error) {
|
|
2430
|
+
if (error instanceof NFError) return Promise.reject(error);
|
|
2431
|
+
config.log.error(3, `[${scope}] failed to pool shared externals.`, {
|
|
2432
|
+
sharedExternals,
|
|
2433
|
+
error
|
|
2434
|
+
});
|
|
2435
|
+
return Promise.reject(
|
|
2436
|
+
new NFError(`Could not pool shared externals in scope ${scope}.`, error)
|
|
2437
|
+
);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
return Promise.resolve();
|
|
2441
|
+
};
|
|
2442
|
+
function poolFamily(poolName, members, scope) {
|
|
2443
|
+
if (members.length < 2) return;
|
|
2444
|
+
const allRemotes = remotesInPool(members);
|
|
2445
|
+
if (allRemotes.length < 2) return;
|
|
2446
|
+
const anchor = selectCoverageAnchor(poolWinners(members));
|
|
2447
|
+
if (!anchor) return;
|
|
2448
|
+
const classification = classifyPoolConservative(members, anchor);
|
|
2449
|
+
config.log.debug(
|
|
2450
|
+
3,
|
|
2451
|
+
`[${scope}][pool:${poolName}] members=[${members.map((m) => m.name).join(", ")}], anchor=${anchor.anchorRemote}, remotes={${allRemotes.map((r) => `${r}:${classification.get(r)}`).join(", ")}}`
|
|
2452
|
+
);
|
|
2453
|
+
const incompatForced = allRemotes.some((r) => classification.get(r) === "scope-incompat");
|
|
2454
|
+
if (incompatForced && config.strict.strictExternalCompatibility) {
|
|
2455
|
+
config.log.error(
|
|
2456
|
+
3,
|
|
2457
|
+
`[${scope}][pool:${poolName}] A remote is version-incompatible with anchor '${anchor.anchorRemote}'.`
|
|
2458
|
+
);
|
|
2459
|
+
throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
|
|
2460
|
+
}
|
|
2461
|
+
for (const member of members) {
|
|
2462
|
+
const rebuilt = rebuildMember(member, anchor, classification);
|
|
2463
|
+
warnIfScopedOnly(poolName, member.name, rebuilt, scope);
|
|
2464
|
+
ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
function warnIfScopedOnly(poolName, memberName, rebuilt, scope) {
|
|
2468
|
+
if (rebuilt.versions.some((v) => v.action === "share")) return;
|
|
2469
|
+
const copies = rebuilt.versions.reduce((n, v) => n + v.remotes.length, 0);
|
|
2470
|
+
config.log.warn(
|
|
2471
|
+
3,
|
|
2472
|
+
`[${scope}][pool:${poolName}] '${memberName}' is scoped-only \u2014 no coherent shared build provides it; ${copies} remotes download their own copy.`
|
|
2473
|
+
);
|
|
2474
|
+
}
|
|
2475
|
+
function rebuildMember(member, anchor, classification) {
|
|
2476
|
+
const anchorTag = anchor.tagPerMember[member.name];
|
|
2477
|
+
const entries = member.external.versions.flatMap(
|
|
2478
|
+
(v) => v.remotes.map((meta) => ({ remote: meta.name, tag: v.tag, host: v.host, meta }))
|
|
2479
|
+
);
|
|
2480
|
+
if (!anchorTag) {
|
|
2481
|
+
return {
|
|
2482
|
+
dirty: false,
|
|
2483
|
+
versions: entries.length > 0 ? [
|
|
2484
|
+
{
|
|
2485
|
+
tag: entries[0].tag,
|
|
2486
|
+
host: false,
|
|
2487
|
+
action: "scope",
|
|
2488
|
+
remotes: entries.map((e) => e.meta)
|
|
2489
|
+
}
|
|
2490
|
+
] : []
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
const anchorMeta = entries.find(
|
|
2494
|
+
(e) => e.remote === anchor.anchorRemote && e.tag === anchorTag
|
|
2495
|
+
).meta;
|
|
2496
|
+
const anchorHost = member.external.versions.find((v) => v.tag === anchorTag)?.host ?? false;
|
|
2497
|
+
const scopes = (e) => {
|
|
2498
|
+
const cls = classification.get(e.remote);
|
|
2499
|
+
return cls === "scope-incompat" || cls === "scope-coverage" && e.tag !== anchorTag;
|
|
2500
|
+
};
|
|
2501
|
+
const shareOnAnchor = entries.filter(
|
|
2502
|
+
(e) => e.remote !== anchor.anchorRemote && e.tag === anchorTag && !scopes(e)
|
|
2503
|
+
);
|
|
2504
|
+
const shareVersion = {
|
|
2505
|
+
tag: anchorTag,
|
|
2506
|
+
host: anchorHost,
|
|
2507
|
+
action: "share",
|
|
2508
|
+
remotes: [anchorMeta, ...shareOnAnchor.map((e) => e.meta)]
|
|
2509
|
+
};
|
|
2510
|
+
const skipByTag = /* @__PURE__ */ new Map();
|
|
2511
|
+
for (const e of entries) {
|
|
2512
|
+
if (e.tag === anchorTag || scopes(e)) continue;
|
|
2513
|
+
const version = skipByTag.get(e.tag) ?? {
|
|
2514
|
+
tag: e.tag,
|
|
2515
|
+
host: e.host,
|
|
2516
|
+
action: "skip",
|
|
2517
|
+
remotes: []
|
|
2518
|
+
};
|
|
2519
|
+
version.remotes.push(e.meta);
|
|
2520
|
+
skipByTag.set(e.tag, version);
|
|
2521
|
+
}
|
|
2522
|
+
const scoped = entries.filter((e) => scopes(e));
|
|
2523
|
+
const scopeVersion = scoped.length > 0 ? [{ tag: anchorTag, host: false, action: "scope", remotes: scoped.map((e) => e.meta) }] : [];
|
|
2524
|
+
return {
|
|
2525
|
+
dirty: false,
|
|
2526
|
+
versions: [shareVersion, ...skipByTag.values(), ...scopeVersion]
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
// node_modules/.pnpm/@softarc+native-federation@4.3.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
|
|
2226
2532
|
var CHUNK_PREFIX = "@nf-internal";
|
|
2227
2533
|
function toChunkImport(fileName) {
|
|
2228
2534
|
if (fileName.startsWith("./")) {
|
|
@@ -2257,12 +2563,9 @@ function createGenerateImportMap(config, ports) {
|
|
|
2257
2563
|
});
|
|
2258
2564
|
addToScope(importMap, remote.scopeUrl, createScopeModules(externals, remote.scopeUrl));
|
|
2259
2565
|
for (const version of Object.values(externals)) {
|
|
2260
|
-
|
|
2261
|
-
importMap,
|
|
2262
|
-
|
|
2263
|
-
remoteName,
|
|
2264
|
-
version.file
|
|
2265
|
-
);
|
|
2566
|
+
for (const file of Object.values(version.entries)) {
|
|
2567
|
+
addIntegrity(importMap, join(remote.scopeUrl, file), remoteName, file);
|
|
2568
|
+
}
|
|
2266
2569
|
}
|
|
2267
2570
|
Object.values(externals).filter((e) => !!e.bundle).forEach((e) => registerBundleChunks(chunkBundles, remoteName, e.bundle));
|
|
2268
2571
|
}
|
|
@@ -2270,8 +2573,10 @@ function createGenerateImportMap(config, ports) {
|
|
|
2270
2573
|
}
|
|
2271
2574
|
function createScopeModules(externals, scope) {
|
|
2272
2575
|
const modules = {};
|
|
2273
|
-
for (const
|
|
2274
|
-
|
|
2576
|
+
for (const version of Object.values(externals)) {
|
|
2577
|
+
for (const [packageName, file] of Object.entries(version.entries)) {
|
|
2578
|
+
modules[packageName] = join(scope, file);
|
|
2579
|
+
}
|
|
2275
2580
|
}
|
|
2276
2581
|
return modules;
|
|
2277
2582
|
}
|
|
@@ -2291,47 +2596,49 @@ function createGenerateImportMap(config, ports) {
|
|
|
2291
2596
|
if (version.action === "scope") {
|
|
2292
2597
|
for (const remote of version.remotes) {
|
|
2293
2598
|
const remoteScope = getScope2(shareScope, remote.name, externalName);
|
|
2294
|
-
const
|
|
2295
|
-
|
|
2296
|
-
[
|
|
2297
|
-
|
|
2298
|
-
|
|
2599
|
+
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
2600
|
+
const url = join(remoteScope, file);
|
|
2601
|
+
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
2602
|
+
addIntegrity(importMap, url, remote.name, file);
|
|
2603
|
+
}
|
|
2299
2604
|
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
2300
2605
|
remote.cached = true;
|
|
2301
2606
|
}
|
|
2302
2607
|
continue;
|
|
2303
2608
|
}
|
|
2304
|
-
const scope = getScope2(shareScope, version.remotes[0].name, externalName);
|
|
2305
|
-
let targetFileUrl = join(scope, version.remotes[0].file);
|
|
2306
|
-
let targetFileSource = {
|
|
2307
|
-
name: version.remotes[0].name,
|
|
2308
|
-
file: version.remotes[0].file
|
|
2309
|
-
};
|
|
2310
2609
|
version.remotes[0].cached = true;
|
|
2610
|
+
let source = version.remotes[0];
|
|
2611
|
+
let sourceScope = getScope2(shareScope, source.name, externalName);
|
|
2311
2612
|
if (version.action === "share") {
|
|
2312
|
-
registerBundleChunks(chunkBundles,
|
|
2313
|
-
}
|
|
2314
|
-
if (version.action === "skip") {
|
|
2613
|
+
registerBundleChunks(chunkBundles, source.name, source.bundle);
|
|
2614
|
+
} else if (version.action === "skip") {
|
|
2315
2615
|
if (!override) {
|
|
2316
2616
|
override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
|
|
2317
2617
|
}
|
|
2318
2618
|
if (override !== "NOT_AVAILABLE") {
|
|
2319
2619
|
if (!overrideScope)
|
|
2320
2620
|
overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
name: override.remotes[0].name,
|
|
2324
|
-
file: override.remotes[0].file
|
|
2325
|
-
};
|
|
2621
|
+
source = override.remotes[0];
|
|
2622
|
+
sourceScope = overrideScope;
|
|
2326
2623
|
override.remotes[0].cached = true;
|
|
2327
2624
|
version.remotes[0].cached = false;
|
|
2328
2625
|
}
|
|
2329
2626
|
}
|
|
2627
|
+
const mappings = Object.entries(source.entries).map(([packageName, file]) => ({
|
|
2628
|
+
packageName,
|
|
2629
|
+
url: join(sourceScope, file),
|
|
2630
|
+
name: source.name,
|
|
2631
|
+
file
|
|
2632
|
+
}));
|
|
2330
2633
|
version.remotes.forEach((r) => {
|
|
2331
|
-
const
|
|
2332
|
-
|
|
2634
|
+
const rScope = getScope2(shareScope, r.name, externalName);
|
|
2635
|
+
for (const m of mappings) {
|
|
2636
|
+
addToScope(importMap, rScope, { [m.packageName]: m.url });
|
|
2637
|
+
}
|
|
2333
2638
|
});
|
|
2334
|
-
|
|
2639
|
+
for (const m of mappings) {
|
|
2640
|
+
addIntegrity(importMap, m.url, m.name, m.file);
|
|
2641
|
+
}
|
|
2335
2642
|
}
|
|
2336
2643
|
ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
|
|
2337
2644
|
}
|
|
@@ -2372,11 +2679,11 @@ function createGenerateImportMap(config, ports) {
|
|
|
2372
2679
|
if (version.action === "scope") {
|
|
2373
2680
|
for (const remote of version.remotes) {
|
|
2374
2681
|
const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
|
|
2375
|
-
const
|
|
2376
|
-
|
|
2377
|
-
[
|
|
2378
|
-
|
|
2379
|
-
|
|
2682
|
+
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
2683
|
+
const url = join(remoteScope, file);
|
|
2684
|
+
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
2685
|
+
addIntegrity(importMap, url, remote.name, file);
|
|
2686
|
+
}
|
|
2380
2687
|
remote.cached = true;
|
|
2381
2688
|
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
2382
2689
|
}
|
|
@@ -2387,9 +2694,11 @@ function createGenerateImportMap(config, ports) {
|
|
|
2387
2694
|
continue;
|
|
2388
2695
|
}
|
|
2389
2696
|
const scope = getScope2(GLOBAL_SCOPE, version.remotes[0].name, externalName);
|
|
2390
|
-
const
|
|
2391
|
-
|
|
2392
|
-
|
|
2697
|
+
for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
|
|
2698
|
+
const url = join(scope, file);
|
|
2699
|
+
addToGlobal(importMap, { [packageName]: url });
|
|
2700
|
+
addIntegrity(importMap, url, version.remotes[0].name, file);
|
|
2701
|
+
}
|
|
2393
2702
|
registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
|
|
2394
2703
|
version.remotes[0].cached = true;
|
|
2395
2704
|
}
|
|
@@ -2586,8 +2895,8 @@ function createUpdateCache(config, ports) {
|
|
|
2586
2895
|
storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
|
|
2587
2896
|
const { action, sharedVersion } = resolveSharedExternal(entry, external, ctx);
|
|
2588
2897
|
actions[external.packageName] = { action };
|
|
2589
|
-
if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.
|
|
2590
|
-
actions[external.packageName].override =
|
|
2898
|
+
if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.entries) {
|
|
2899
|
+
actions[external.packageName].override = resolveOverrideEntries(
|
|
2591
2900
|
entry,
|
|
2592
2901
|
external,
|
|
2593
2902
|
sharedVersion
|
|
@@ -2631,8 +2940,15 @@ function createUpdateCache(config, ports) {
|
|
|
2631
2940
|
commit();
|
|
2632
2941
|
return { action, sharedVersion };
|
|
2633
2942
|
}
|
|
2634
|
-
function
|
|
2635
|
-
return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map(
|
|
2943
|
+
function resolveOverrideEntries(remoteEntry, external, sharedVersion) {
|
|
2944
|
+
return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map(
|
|
2945
|
+
(remote) => Object.fromEntries(
|
|
2946
|
+
Object.entries(sharedVersion.remotes[0].entries).map(([packageName, file]) => [
|
|
2947
|
+
packageName,
|
|
2948
|
+
join(remote.scopeUrl, file)
|
|
2949
|
+
])
|
|
2950
|
+
)
|
|
2951
|
+
).orThrow(() => {
|
|
2636
2952
|
config.log.error(
|
|
2637
2953
|
8,
|
|
2638
2954
|
`[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
|
|
@@ -2644,14 +2960,56 @@ function createUpdateCache(config, ports) {
|
|
|
2644
2960
|
}
|
|
2645
2961
|
}
|
|
2646
2962
|
|
|
2647
|
-
// src/lib/core/2.app/steps/
|
|
2648
|
-
function
|
|
2963
|
+
// src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
|
|
2964
|
+
function createPoolDynamicExternals(config) {
|
|
2649
2965
|
return ({ entry, actions }) => {
|
|
2966
|
+
const { useAutoExternalPooling } = config.feature;
|
|
2967
|
+
if (!useAutoExternalPooling && !(entry.shared ?? []).some((e) => e.pool?.trim())) {
|
|
2968
|
+
return Promise.resolve({ entry, actions });
|
|
2969
|
+
}
|
|
2970
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
2971
|
+
for (const external of entry.shared ?? []) {
|
|
2972
|
+
const name = external.packageName;
|
|
2973
|
+
if (!external.singleton || !actions[name]) continue;
|
|
2974
|
+
if (external.shareScope === STRICT_SCOPE) continue;
|
|
2975
|
+
const tag = external.pool?.trim();
|
|
2976
|
+
const shareScope = external.shareScope ?? GLOBAL_SCOPE;
|
|
2977
|
+
const candidates = byScope.get(shareScope) ?? [];
|
|
2978
|
+
candidates.push({
|
|
2979
|
+
name,
|
|
2980
|
+
scope: autoScope(name, useAutoExternalPooling),
|
|
2981
|
+
tags: tag ? [{ remote: entry.name, tag }] : [],
|
|
2982
|
+
value: name
|
|
2983
|
+
});
|
|
2984
|
+
byScope.set(shareScope, candidates);
|
|
2985
|
+
}
|
|
2986
|
+
const scope = (name) => {
|
|
2987
|
+
actions[name].action = "scope";
|
|
2988
|
+
delete actions[name].override;
|
|
2989
|
+
};
|
|
2990
|
+
for (const candidates of byScope.values()) {
|
|
2991
|
+
for (const members of groupByMembership(candidates).values()) {
|
|
2992
|
+
const memberActions = members.map((name) => actions[name].action);
|
|
2993
|
+
if (memberActions.includes("scope")) {
|
|
2994
|
+
members.forEach(scope);
|
|
2995
|
+
} else if (memberActions.includes("share") && memberActions.includes("skip")) {
|
|
2996
|
+
members.filter((name) => actions[name].action === "share").forEach(scope);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
return Promise.resolve({ entry, actions });
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// src/lib/core/2.app/steps/convert-to-import-map.ts
|
|
3005
|
+
function createConvertToImportMap(config, ports) {
|
|
3006
|
+
const { log } = config;
|
|
3007
|
+
return async ({ entry, actions }) => {
|
|
2650
3008
|
const importMap = { imports: {} };
|
|
2651
3009
|
addExternals(entry, actions, importMap);
|
|
2652
3010
|
addRemoteInfos(entry, importMap);
|
|
2653
3011
|
log.debug(9, `[${entry.name}] Processed actions:`, actions);
|
|
2654
|
-
return
|
|
3012
|
+
return importMap;
|
|
2655
3013
|
};
|
|
2656
3014
|
function addExternals(remoteEntry, actions, importMap) {
|
|
2657
3015
|
if (!remoteEntry.shared) {
|
|
@@ -2662,9 +3020,11 @@ function createConvertToImportMap({ log }, ports) {
|
|
|
2662
3020
|
const chunkBundles = /* @__PURE__ */ new Set(["mapping-or-exposed"]);
|
|
2663
3021
|
remoteEntry.shared.forEach((external) => {
|
|
2664
3022
|
if (!external.singleton) {
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
3023
|
+
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3024
|
+
const url = join(remoteEntryScope, fileName);
|
|
3025
|
+
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
3026
|
+
addIntegrity(importMap, url, integrityMap, fileName);
|
|
3027
|
+
});
|
|
2668
3028
|
if (external?.bundle) chunkBundles.add(external?.bundle);
|
|
2669
3029
|
return;
|
|
2670
3030
|
}
|
|
@@ -2676,33 +3036,48 @@ function createConvertToImportMap({ log }, ports) {
|
|
|
2676
3036
|
return;
|
|
2677
3037
|
}
|
|
2678
3038
|
if (actions[external.packageName].action === "skip") {
|
|
3039
|
+
const override = actions[external.packageName].override;
|
|
2679
3040
|
if (!external.shareScope) return;
|
|
2680
|
-
if (
|
|
2681
|
-
|
|
2682
|
-
remoteEntryScope,
|
|
2683
|
-
|
|
2684
|
-
actions[external.packageName].override,
|
|
2685
|
-
importMap
|
|
2686
|
-
);
|
|
3041
|
+
if (override) {
|
|
3042
|
+
Object.entries(override).forEach(([packageName, url]) => {
|
|
3043
|
+
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
3044
|
+
});
|
|
2687
3045
|
return;
|
|
2688
3046
|
}
|
|
3047
|
+
log.error(
|
|
3048
|
+
9,
|
|
3049
|
+
`[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
|
|
3050
|
+
);
|
|
3051
|
+
if (config.strict.strictImportMap) throw new NFError("Could not create ImportMap.");
|
|
3052
|
+
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3053
|
+
const url = join(remoteEntryScope, fileName);
|
|
3054
|
+
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
3055
|
+
addIntegrity(importMap, url, integrityMap, fileName);
|
|
3056
|
+
});
|
|
3057
|
+
return;
|
|
2689
3058
|
}
|
|
2690
3059
|
if (external?.bundle) chunkBundles.add(external?.bundle);
|
|
2691
3060
|
if (actions[external.packageName].action === "scope") {
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
3061
|
+
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3062
|
+
const url = join(remoteEntryScope, fileName);
|
|
3063
|
+
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
3064
|
+
addIntegrity(importMap, url, integrityMap, fileName);
|
|
3065
|
+
});
|
|
2695
3066
|
return;
|
|
2696
3067
|
}
|
|
2697
3068
|
if (external.shareScope) {
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
3069
|
+
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3070
|
+
const url = join(remoteEntryScope, fileName);
|
|
3071
|
+
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
3072
|
+
addIntegrity(importMap, url, integrityMap, fileName);
|
|
3073
|
+
});
|
|
2701
3074
|
return;
|
|
2702
3075
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
3076
|
+
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3077
|
+
const url = join(remoteEntryScope, fileName);
|
|
3078
|
+
importMap.imports[packageName] = url;
|
|
3079
|
+
addIntegrity(importMap, url, integrityMap, fileName);
|
|
3080
|
+
});
|
|
2706
3081
|
});
|
|
2707
3082
|
addChunkImports(importMap, remoteEntry, remoteEntryScope, chunkBundles);
|
|
2708
3083
|
}
|
|
@@ -2749,11 +3124,13 @@ var createInitDrivers = ({
|
|
|
2749
3124
|
getRemoteEntries: createGetRemoteEntries(config, adapters),
|
|
2750
3125
|
processRemoteEntries: createProcessRemoteEntries(config, adapters),
|
|
2751
3126
|
determineSharedExternals: createDetermineSharedExternals(config, adapters),
|
|
3127
|
+
poolSharedExternals: createPoolSharedExternals(config, adapters),
|
|
2752
3128
|
generateImportMap: createGenerateImportMap(config, adapters),
|
|
2753
3129
|
commitChanges: createCommitChanges(config, adapters),
|
|
2754
3130
|
exposeModuleLoader: createExposeModuleLoader(config, adapters),
|
|
2755
3131
|
getRemoteEntry: createGetRemoteEntry(config, adapters),
|
|
2756
3132
|
updateCache: createUpdateCache(config, adapters),
|
|
3133
|
+
poolDynamicExternals: createPoolDynamicExternals(config),
|
|
2757
3134
|
convertToImportMap: createConvertToImportMap(config, adapters)
|
|
2758
3135
|
});
|
|
2759
3136
|
var INIT_FLOW_FACTORY = ({
|