Skip to content

Commit ad147a6

Browse files
refactor(examples): stage RSC metadata on snapshot hook
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
1 parent f19ca90 commit ad147a6

1 file changed

Lines changed: 205 additions & 64 deletions

File tree

examples/rsbuild-rsc-federation/src/framework/mf-rsc-registration-runtime-plugin.js

Lines changed: 205 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ function createVirtualModuleId(kind, key) {
2121
return `mf_rsc_${kind}_${stableHash(key)}`;
2222
}
2323

24+
function toUniqueStringArray(items) {
25+
const deduped = new Set();
26+
for (const item of items) {
27+
if (typeof item === 'string' && item) {
28+
deduped.add(item);
29+
}
30+
}
31+
return [...deduped];
32+
}
33+
2434
function normalizeExportedModule(moduleValue) {
2535
if (
2636
moduleValue &&
@@ -72,6 +82,98 @@ function normalizeServerActions(value) {
7282
return actions;
7383
}
7484

85+
function createRscRegistrationPayload(manifestJson) {
86+
const exposes = Array.isArray(manifestJson?.exposes)
87+
? manifestJson.exposes
88+
: [];
89+
const shared = Array.isArray(manifestJson?.shared) ? manifestJson.shared : [];
90+
const exposePayload = [];
91+
const sharedPayload = [];
92+
93+
for (const expose of exposes) {
94+
const rscMeta = expose?.rsc;
95+
if (!rscMeta) {
96+
continue;
97+
}
98+
const clientReferences = normalizeStringArray(rscMeta.clientReferences);
99+
const serverActions = normalizeServerActions(rscMeta.serverActions);
100+
if (clientReferences.length === 0 && serverActions.length === 0) {
101+
continue;
102+
}
103+
const moduleIdentity =
104+
rscMeta.lookup || rscMeta.resource || expose.path || expose.name || '.';
105+
exposePayload.push({
106+
exposePath: expose.path,
107+
moduleIdentity,
108+
manifestKeys: toUniqueStringArray([
109+
rscMeta.resource,
110+
rscMeta.lookup,
111+
moduleIdentity,
112+
]),
113+
clientReferences,
114+
serverActions,
115+
});
116+
}
117+
118+
for (const sharedItem of shared) {
119+
const rscMeta = sharedItem?.rsc;
120+
if (!rscMeta || typeof sharedItem?.name !== 'string' || !sharedItem.name) {
121+
continue;
122+
}
123+
const clientReferences = normalizeStringArray(rscMeta.clientReferences);
124+
const serverActions = normalizeServerActions(rscMeta.serverActions);
125+
if (clientReferences.length === 0 && serverActions.length === 0) {
126+
continue;
127+
}
128+
const moduleIdentity = rscMeta.lookup || rscMeta.resource || sharedItem.name;
129+
sharedPayload.push({
130+
shareKey: sharedItem.name,
131+
moduleIdentity,
132+
manifestKeys: toUniqueStringArray([
133+
rscMeta.resource,
134+
rscMeta.lookup,
135+
moduleIdentity,
136+
]),
137+
clientReferences,
138+
serverActions,
139+
});
140+
}
141+
142+
return {
143+
sourceId: manifestJson?.id || manifestJson?.name || '',
144+
buildVersion: manifestJson?.metaData?.buildInfo?.buildVersion || '',
145+
exposePayload,
146+
sharedPayload,
147+
};
148+
}
149+
150+
function buildManifestIdentityKeys({
151+
remoteAlias,
152+
remoteName,
153+
remoteEntry,
154+
remoteVersion,
155+
remoteBuildVersion,
156+
manifestUrl,
157+
manifestJson,
158+
}) {
159+
const keys = [
160+
remoteAlias && `alias:${remoteAlias}`,
161+
remoteName && `name:${remoteName}`,
162+
remoteEntry && `entry:${remoteEntry}`,
163+
remoteVersion && remoteName && `version:${remoteName}:${remoteVersion}`,
164+
remoteBuildVersion &&
165+
remoteName &&
166+
`build:${remoteName}:${remoteBuildVersion}`,
167+
manifestUrl && `manifestUrl:${manifestUrl}`,
168+
manifestJson?.id && `manifestId:${manifestJson.id}`,
169+
manifestJson?.name && `manifestName:${manifestJson.name}`,
170+
manifestJson?.metaData?.buildInfo?.buildVersion &&
171+
manifestJson?.name &&
172+
`manifestBuild:${manifestJson.name}:${manifestJson.metaData.buildInfo.buildVersion}`,
173+
];
174+
return toUniqueStringArray(keys);
175+
}
176+
75177
function ensureRscManifest() {
76178
const existing = __webpack_require__.rscM;
77179
if (existing) {
@@ -178,6 +280,7 @@ function toExposeRequest(remoteAlias, exposePath) {
178280

179281
export default function mfRscRegistrationRuntimePlugin() {
180282
const registrationFingerprintByAlias = new Map();
283+
const stagedPayloadByIdentity = new Map();
181284

182285
const loadRemoteModule = (request) =>
183286
__webpack_require__.federation.instance
@@ -242,102 +345,89 @@ export default function mfRscRegistrationRuntimePlugin() {
242345
});
243346
};
244347

245-
const registerRscDataFromManifest = (remoteAlias, manifestJson) => {
246-
const exposes = Array.isArray(manifestJson?.exposes)
247-
? manifestJson.exposes
248-
: [];
249-
const shared = Array.isArray(manifestJson?.shared) ? manifestJson.shared : [];
348+
const cacheStagedPayload = (identityKeys, payload) => {
349+
for (const key of identityKeys) {
350+
stagedPayloadByIdentity.set(key, payload);
351+
}
352+
};
353+
354+
const getStagedPayload = (identityKeys) => {
355+
for (const key of identityKeys) {
356+
const stagedPayload = stagedPayloadByIdentity.get(key);
357+
if (stagedPayload) {
358+
return stagedPayload;
359+
}
360+
}
361+
return undefined;
362+
};
363+
364+
const registerRscPayload = (remoteAlias, payload) => {
250365
const fingerprint = JSON.stringify({
251-
id: manifestJson?.id || manifestJson?.name || '',
252-
buildVersion: manifestJson?.metaData?.buildInfo?.buildVersion || '',
253-
exposeRscKeys: exposes
254-
.filter((item) => item?.rsc)
255-
.map((item) => item.rsc.lookup || item.rsc.resource || item.path || ''),
256-
sharedRscKeys: shared
257-
.filter((item) => item?.rsc)
258-
.map((item) => item.rsc.lookup || item.rsc.resource || item.name || ''),
366+
id: payload.sourceId,
367+
buildVersion: payload.buildVersion,
368+
exposeRscKeys: payload.exposePayload.map(
369+
(item) => item.moduleIdentity || '',
370+
),
371+
sharedRscKeys: payload.sharedPayload.map(
372+
(item) => item.moduleIdentity || '',
373+
),
259374
});
260375
if (registrationFingerprintByAlias.get(remoteAlias) === fingerprint) {
261376
return;
262377
}
263378

264-
for (const expose of exposes) {
265-
const rscMeta = expose?.rsc;
266-
if (!rscMeta) {
267-
continue;
268-
}
269-
270-
const clientReferences = normalizeStringArray(rscMeta.clientReferences);
271-
const serverActions = normalizeServerActions(rscMeta.serverActions);
272-
const exposeRequest = toExposeRequest(remoteAlias, expose.path);
273-
const moduleIdentity = rscMeta.lookup || rscMeta.resource || exposeRequest;
274-
const manifestKeys = new Set(
275-
[rscMeta.resource, rscMeta.lookup, moduleIdentity].filter(Boolean),
276-
);
277-
278-
if (clientReferences.length > 0) {
379+
for (const expose of payload.exposePayload) {
380+
const exposeRequest = toExposeRequest(remoteAlias, expose.exposePath);
381+
if (expose.clientReferences.length > 0) {
279382
const moduleId = createVirtualModuleId(
280383
'client_expose',
281-
`${remoteAlias}:${moduleIdentity}`,
384+
`${remoteAlias}:${expose.moduleIdentity}`,
282385
);
283-
registerClientReferenceModule(moduleId, clientReferences, () =>
386+
registerClientReferenceModule(moduleId, expose.clientReferences, () =>
284387
loadRemoteModule(exposeRequest),
285388
);
286-
for (const moduleKey of manifestKeys) {
389+
for (const moduleKey of expose.manifestKeys) {
287390
ensureClientManifestEntry(moduleKey, moduleId);
288391
}
289392
}
290393

291-
if (serverActions.length > 0) {
394+
if (expose.serverActions.length > 0) {
292395
const moduleId = createVirtualModuleId(
293396
'action_expose',
294-
`${remoteAlias}:${moduleIdentity}`,
397+
`${remoteAlias}:${expose.moduleIdentity}`,
295398
);
296-
registerServerActionModule(moduleId, serverActions, () =>
399+
registerServerActionModule(moduleId, expose.serverActions, () =>
297400
loadRemoteModule(exposeRequest),
298401
);
299-
for (const action of serverActions) {
402+
for (const action of expose.serverActions) {
300403
ensureServerActionManifestEntry(action.id, moduleId);
301404
}
302405
}
303406
}
304407

305-
for (const sharedItem of shared) {
306-
const rscMeta = sharedItem?.rsc;
307-
if (!rscMeta || typeof sharedItem?.name !== 'string' || !sharedItem.name) {
308-
continue;
309-
}
310-
311-
const shareKey = sharedItem.name;
312-
const clientReferences = normalizeStringArray(rscMeta.clientReferences);
313-
const serverActions = normalizeServerActions(rscMeta.serverActions);
314-
const moduleIdentity = rscMeta.lookup || rscMeta.resource || shareKey;
315-
const manifestKeys = new Set(
316-
[rscMeta.resource, rscMeta.lookup, moduleIdentity].filter(Boolean),
317-
);
318-
319-
if (clientReferences.length > 0) {
408+
for (const sharedItem of payload.sharedPayload) {
409+
if (sharedItem.clientReferences.length > 0) {
320410
const moduleId = createVirtualModuleId(
321411
'client_shared',
322-
`${remoteAlias}:${moduleIdentity}`,
412+
`${remoteAlias}:${sharedItem.moduleIdentity}`,
323413
);
324-
registerClientReferenceModule(moduleId, clientReferences, () =>
325-
loadSharedModule(shareKey),
414+
registerClientReferenceModule(moduleId, sharedItem.clientReferences, () =>
415+
loadSharedModule(sharedItem.shareKey),
326416
);
327-
for (const moduleKey of manifestKeys) {
417+
for (const moduleKey of sharedItem.manifestKeys) {
328418
ensureClientManifestEntry(moduleKey, moduleId);
329419
}
330420
}
331421

332-
if (serverActions.length > 0) {
422+
if (sharedItem.serverActions.length > 0) {
333423
const moduleId = createVirtualModuleId(
334424
'action_shared',
335-
`${remoteAlias}:${moduleIdentity}`,
425+
`${remoteAlias}:${sharedItem.moduleIdentity}`,
336426
);
337-
registerServerActionModule(moduleId, serverActions, () =>
338-
loadSharedModule(shareKey),
427+
registerServerActionModule(moduleId, sharedItem.serverActions, () =>
428+
loadSharedModule(sharedItem.shareKey),
339429
);
340-
for (const action of serverActions) {
430+
for (const action of sharedItem.serverActions) {
341431
ensureServerActionManifestEntry(action.id, moduleId);
342432
}
343433
}
@@ -352,6 +442,7 @@ export default function mfRscRegistrationRuntimePlugin() {
352442
args?.remote?.alias ||
353443
args?.remoteInfo?.alias ||
354444
args?.remote?.name;
445+
const remoteName = args?.remoteInfo?.name || args?.remote?.name;
355446
if (!remoteAlias) {
356447
return;
357448
}
@@ -361,19 +452,69 @@ export default function mfRscRegistrationRuntimePlugin() {
361452
typeof configuredEntry === 'string' && configuredEntry.endsWith('.json')
362453
? configuredEntry
363454
: undefined;
364-
const manifestJson =
365-
(manifestUrl && (await readManifestJson(args.origin, manifestUrl))) ||
366-
pickManifestFromSnapshotCache(args.origin, args?.remoteInfo?.name);
455+
const identityKeys = buildManifestIdentityKeys({
456+
remoteAlias,
457+
remoteName,
458+
remoteEntry: configuredEntry,
459+
remoteVersion: args?.remoteSnapshot?.version || args?.remoteInfo?.version,
460+
remoteBuildVersion:
461+
args?.remoteSnapshot?.buildVersion || args?.remoteInfo?.buildVersion,
462+
manifestUrl,
463+
});
464+
465+
let payload = getStagedPayload(identityKeys);
466+
if (!payload) {
467+
const manifestJson =
468+
(manifestUrl && (await readManifestJson(args.origin, manifestUrl))) ||
469+
pickManifestFromSnapshotCache(args.origin, args?.remoteInfo?.name);
470+
if (!manifestJson) {
471+
return;
472+
}
473+
payload = createRscRegistrationPayload(manifestJson);
474+
const manifestIdentityKeys = buildManifestIdentityKeys({
475+
remoteAlias,
476+
remoteName,
477+
remoteEntry: configuredEntry,
478+
remoteVersion:
479+
args?.remoteSnapshot?.version || args?.remoteInfo?.version,
480+
remoteBuildVersion:
481+
args?.remoteSnapshot?.buildVersion || args?.remoteInfo?.buildVersion,
482+
manifestUrl,
483+
manifestJson,
484+
});
485+
cacheStagedPayload(manifestIdentityKeys, payload);
486+
}
367487

368-
if (!manifestJson) {
488+
if (!payload) {
369489
return;
370490
}
371491

372-
registerRscDataFromManifest(remoteAlias, manifestJson);
492+
registerRscPayload(remoteAlias, payload);
373493
};
374494

375495
return {
376496
name: 'mf-rsc-registration-runtime-plugin',
497+
async loadRemoteSnapshot(args) {
498+
if (args?.from !== 'manifest' || !args?.manifestJson) {
499+
return args;
500+
}
501+
const remoteAlias = args?.moduleInfo?.alias || args?.moduleInfo?.name;
502+
const remoteName = args?.moduleInfo?.name;
503+
const payload = createRscRegistrationPayload(args.manifestJson);
504+
const identityKeys = buildManifestIdentityKeys({
505+
remoteAlias,
506+
remoteName,
507+
remoteEntry: args?.moduleInfo?.entry,
508+
remoteVersion: args?.remoteSnapshot?.version || args?.moduleInfo?.version,
509+
remoteBuildVersion:
510+
args?.remoteSnapshot?.buildVersion ||
511+
args?.manifestJson?.metaData?.buildInfo?.buildVersion,
512+
manifestUrl: args?.manifestUrl,
513+
manifestJson: args?.manifestJson,
514+
});
515+
cacheStagedPayload(identityKeys, payload);
516+
return args;
517+
},
377518
async afterResolve(args) {
378519
await tryRegisterManifest(args);
379520
return args;

0 commit comments

Comments
 (0)