fix(frontend): re-root federated Console router to avoid host __root collision (MF v2 dev)#2508
Conversation
…collision Embedded Console (MF v2) renders Cloud UI's root route instead of its own in dev, because both apps compile a module with the identical id ./src/routes/__root.tsx and the shared rsbuild/MF dev runtime resolves the host's. That substitutes Cloud UI's RootComponent (react NuqsAdapter, Builder.io <Content>, <CommandPalette>/KBar) as Console's embedded root, which: crashes on useKBar (no KBarProvider in this subtree) -> empty sidebar; and leaves Console's useQueryState consumers without a matching nuqs adapter context -> NUQS-404. Re-root the federated router onto Console's own federatedRootRoute (a Console-unique module path that can't collide), and give it RouterSync + RequireAuth (RequireAuth triggers the user-data fetch that gates the endpoint-compatibility fetch and the embedded sidebar items). Standalone (app.tsx/__root.tsx) and prod are unaffected.
c0705fc to
8aebc0c
Compare
The embedded MF-v2 Console renders federatedRootRoute, not __root.tsx's RootLayout, so it was missing <DebugHelper /> — the component that registers the Cmd+Shift+D debug dialog (dev-only). Render it in the federated root too, gated on NODE_ENV=development, mirroring __root.tsx.
|
@claude review |
|
Claude finished @c-julin's task in 2m 2s —— View job Code Review — PR #2508: re-root federated Console router
Thorough writeup — the root-cause analysis (host/remote Verified ✅
Worth confirming
Minor / cosmetic
No blocking issues from my read — items 1 and 2 are the ones worth a quick confirmation before merge. |
Mirror __root.tsx's embedded AppContent in the MF-v2 federated layout (which now renders in prod where enable-console-mf-v2 is on): - Render <Toaster> — sonner is not an MF-shared singleton, so the host's Toaster can't surface Console's toasts; without this, toast() is silent in embedded mode. - Wrap <Outlet> in the pt-8 spacing div to match AppContent. (Fullscreen-route branch is intentionally omitted — no route sets staticData.fullscreen on master; it lands with the SQL workspace.)
Summary
When Console is embedded in Cloud UI via Module Federation v2 (
enable-console-mf-v2), the embedded app is broken in dev: empty sidebar, a KBar crash (getState is not a function), and a nuqsNUQS-404("nuqs requires an adapter") on pages like Topics/Security. All three are one root cause.Root cause
Console's federated entry (
console-app.tsx) builds its router from the generatedrouteTree, whose root comes fromsrc/routes/__root.tsx. Cloud UI (the host) also has asrc/routes/__root.tsxsince its TanStack Router migration — an identical rspack module id (./src/routes/__root.tsx) in both apps. In the shared rsbuild/MF dev runtime, the host's already-registered module wins that collision, so Console's route tree ends up with Cloud UI's root route (itsRootComponent) but Console's child routes.Cloud UI's
RootComponentrenders<CommandPalette/>(KBar) and anuqs/adapters/react<NuqsAdapter>. Inside Console's subtree there's noKBarProvider→useKBarthrows → Console's render aborts → sidebar never populates. And the only nuqs provider present is Cloud UI's react adapter on Cloud UI's nuqs context, while Console'suseQueryStateconsumers read Console's own nuqs instance → no matching provider → NUQS-404.Why this is surfacing now
It needed a conjunction of recent/rare conditions that only just lined up:
src/routes/__root.tsxwith the same module id as Console's. Before that, nothing to collide with.enable-console-mf-v2 = false). Only the v2 path has Console build its own router fromrouteTree.gen(which imports__root). The default v1 legacy injector mounts differently and doesn't hit this.__rootin prod. (It also only became observable in localdev once Console hot-reload actually started loading the dev bundle — previously localdev was serving the deployed prod Console image.)So this is a latent dev-only MF hazard, newly exposed — not a regression in normal usage, and not present in production.
Fix
Two files, embedded MF-v2 path only:
console-app.tsx—createFederatedRouteTree()re-parents the generated child routes onto Console's ownfederatedRootRoute(fromfederated-routes.tsx, a Console-unique module path that can't collide with the host), andcreateRouteruses that tree. This guarantees the embedded app renders Console's own root regardless of the dev module-graph crossover.federated-routes.tsx—FederatedRootLayoutgainsRouterSync+RequireAuth.RequireAuthis what triggersapi.refreshUserData()→ the endpoint-compatibility fetch → the embedded sidebar items (without it the sidebar stays empty). Mirrors__root.tsx'sEmbeddedLayout.Standalone (
app.tsx/__root.tsx) and the legacy embedded entry keep using the generatedrouteTreeunchanged.Verification (localdev, embedded in Cloud UI, dev build)
For the reviewer
getParentRouteto point atfederatedRootRoute. Safe because the federated./Appand the standalone/legacy entries don't run in the same runtime simultaneously (MF v2 loads only./App). If you'd prefer to avoid any shared-object mutation, the alternative is generating the embedded tree from a Console-unique generated file — heavier; happy to switch.