Conversation
e010a1f to
da11904
Compare
commit: |
| // intercept client hmr to propagate client boundary invalidation to server environment | ||
| const oldSend = server.environments.client.hot.send | ||
| server.environments.client.hot.send = async function ( | ||
| this, | ||
| ...args: any[] | ||
| ) { | ||
| const e = args[0] as vite.UpdatePayload | ||
| if (e && typeof e === 'object' && e.type === 'update') { | ||
| for (const update of e.updates) { | ||
| if (update.type === 'js-update') { | ||
| const mod = | ||
| server.environments.client.moduleGraph.urlToModuleMap.get( | ||
| update.path, | ||
| ) | ||
| if (mod && mod.id && manager.clientReferenceMetaMap[mod.id]) { | ||
| const serverMod = | ||
| server.environments.rsc!.moduleGraph.getModuleById(mod.id) | ||
| if (serverMod) { | ||
| server.environments.rsc!.moduleGraph.invalidateModule( | ||
| serverMod, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return oldSend.apply(this, args as any) | ||
| } |
There was a problem hiding this comment.
Since this only covers the case where client boundary gets hmr-ed, technically there are edge cases like client reference itself doesn't self-accept or invalidateModule is directly used, so they causes full-reload instead.
There was a problem hiding this comment.
However, patching invalidateModule directly will break the first test case "non-client-reference client hmr" as this is more aggressive invalidation.
const oldInvalidateModule = server.environments.client.moduleGraph.invalidateModule
server.environments.client.moduleGraph.invalidateModule = function (this, ...args) {
const mod = args[0];
if (mod && mod.id && manager.clientReferenceMetaMap[mod.id]) {
const serverMod =
server.environments.rsc!.moduleGraph.getModuleById(mod.id)
if (serverMod) {
server.environments.rsc!.moduleGraph.invalidateModule(
serverMod,
)
}
}
return oldInvalidateModule.apply(this, args);
}|
I think this still has an issue when Here changing However, upon refresh, it will probably end up with: |
Description
Reproduction
hmr-client-dep2/client-dep.tsThis is because when
client-dep.tsis modified, it invalidatesclient.tsx(on client environment) for hmr, which becomes a new module fetched asclient.tsx?t=xxx, butclient.tsxon server environment (which exists as "proxy module" withregisterClientReference) is not invalidated. When re-rendering RSC, server payload includes a client component with old module referenceclient.tsxinstead of newclient.tsx?t=xxx, which has a different identity asTestHmrClientDep2functional component and thus React re-mounts node.Details
Screencast.From.2025-08-27.16-06-40.mp4
TODO
moduleGraph.invalidateModulefrom client environment to rsc environment.