From 59c2ec450eb93ede6a62c28c772a34980e7b90c0 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Wed, 17 Dec 2025 00:37:58 +0100 Subject: [PATCH 1/4] fix: generalize hydration cleanup --- packages/react-router/src/ScriptOnce.tsx | 3 +- .../react-start-client/src/StartClient.tsx | 2 +- .../react-start-client/src/hydrateStart.ts | 12 +++++ packages/react-start-client/src/index.tsx | 1 + packages/react-start-client/tsconfig.json | 6 +-- packages/router-core/src/ssr/ssr-client.ts | 47 ++----------------- packages/router-core/src/ssr/ssr-server.ts | 9 ++-- packages/router-core/src/ssr/tsrScript.ts | 28 ++++------- packages/router-core/src/ssr/types.ts | 40 ++++++++++++++++ packages/solid-router/src/ScriptOnce.tsx | 2 +- .../solid-start-client/src/hydrateStart.ts | 12 +++++ packages/solid-start-client/src/index.tsx | 2 +- .../start-client-core/src/client/index.ts | 1 + packages/start-client-core/src/index.tsx | 5 +- packages/vue-router/src/ScriptOnce.tsx | 4 +- packages/vue-router/src/Scripts.tsx | 11 ----- packages/vue-start-client/src/StartClient.tsx | 15 ++---- 17 files changed, 92 insertions(+), 108 deletions(-) create mode 100644 packages/react-start-client/src/hydrateStart.ts create mode 100644 packages/router-core/src/ssr/types.ts create mode 100644 packages/solid-start-client/src/hydrateStart.ts diff --git a/packages/react-router/src/ScriptOnce.tsx b/packages/react-router/src/ScriptOnce.tsx index f00317d52fb..97609c6fc7b 100644 --- a/packages/react-router/src/ScriptOnce.tsx +++ b/packages/react-router/src/ScriptOnce.tsx @@ -12,9 +12,8 @@ export function ScriptOnce({ children }: { children: string }) { return ( ` + return `${script}` }) }, dehydrate: async () => { @@ -223,7 +222,7 @@ export function attachRouterServerSsrUtils({ }, scopeId: SCOPE_ID, onDone: () => { - scriptBuffer.enqueue(GLOBAL_TSR + '.streamEnd=true') + scriptBuffer.enqueue(GLOBAL_TSR + '.e()') p.resolve('') }, onError: (err) => p.reject(err), diff --git a/packages/router-core/src/ssr/tsrScript.ts b/packages/router-core/src/ssr/tsrScript.ts index 084638cfa4c..69c9abe05d5 100644 --- a/packages/router-core/src/ssr/tsrScript.ts +++ b/packages/router-core/src/ssr/tsrScript.ts @@ -1,25 +1,15 @@ self.$_TSR = { - c() { - // If Vue has set the defer flag, don't remove scripts yet - wait for Vue to call cleanup() - if (self.$_TSR_DEFER) { - return - } - document.querySelectorAll('.\\$tsr').forEach((o) => { - o.remove() - }) - if (this.hydrated && this.streamEnd) { - delete self.$_TSR - delete self.$R['tsr'] - } + h() { + this.hydrated = true + this.c() + }, + e() { + this.streamEnded = true + this.c() }, - // Called by Vue after hydration is complete to perform deferred cleanup - cleanup() { - document.querySelectorAll('.\\$tsr').forEach((o) => { - o.remove() - }) - if (this.hydrated && this.streamEnd) { + c() { + if (this.hydrated && this.streamEnded) { delete self.$_TSR - delete self.$_TSR_DEFER delete self.$R['tsr'] } }, diff --git a/packages/router-core/src/ssr/types.ts b/packages/router-core/src/ssr/types.ts new file mode 100644 index 00000000000..ec0feee41a4 --- /dev/null +++ b/packages/router-core/src/ssr/types.ts @@ -0,0 +1,40 @@ +import type { Manifest } from '../manifest' +import type { MakeRouteMatch } from '../Matches' + +export interface DehydratedMatch { + i: MakeRouteMatch['id'] + b?: MakeRouteMatch['__beforeLoadContext'] + l?: MakeRouteMatch['loaderData'] + e?: MakeRouteMatch['error'] + u: MakeRouteMatch['updatedAt'] + s: MakeRouteMatch['status'] + ssr?: MakeRouteMatch['ssr'] +} + +export interface DehydratedRouter { + manifest: Manifest | undefined + dehydratedData?: any + lastMatchId?: string + matches: Array +} + +export interface TsrSsrGlobal { + router?: DehydratedRouter + // Signal that router hydration is complete + h: () => void + // Signal that stream has ended + e: () => void + // Cleanup all hydration resources and scripts + c: () => void + // p: Push script into buffer or execute immediately + p: (script: () => void) => void + buffer: Array<() => void> + // custom transformers, shortened since this is sent for each streamed value that needs a custom transformer + t?: Map any> + // this flag indicates whether the transformers were initialized + initialized?: boolean + // router is hydrated and doesnt need the streamed values anymore + hydrated?: boolean + // stream has ended + streamEnded?: boolean +} diff --git a/packages/solid-router/src/ScriptOnce.tsx b/packages/solid-router/src/ScriptOnce.tsx index 572570c8ddd..1ad12ccb071 100644 --- a/packages/solid-router/src/ScriptOnce.tsx +++ b/packages/solid-router/src/ScriptOnce.tsx @@ -15,7 +15,7 @@ export function ScriptOnce({