@@ -27,7 +27,6 @@ export type ServerExtractedEntry =
27
27
| ServerExtractedPromise
28
28
29
29
export interface ServerExtractedBaseEntry extends ClientExtractedBaseEntry {
30
- dataType : '__beforeLoadContext' | 'loaderData'
31
30
id : number
32
31
matchIndex : number
33
32
}
@@ -121,32 +120,21 @@ export function dehydrateRouter(router: AnyRouter) {
121
120
)
122
121
}
123
122
124
- export function extractAsyncDataToMatch (
125
- dataType : '__beforeLoadContext' | 'loaderData' ,
126
- data : any ,
123
+ export function extractAsyncLoaderData (
124
+ loaderData : any ,
127
125
ctx : {
128
126
match : AnyRouteMatch
129
127
router : AnyRouter
130
128
} ,
131
129
) {
132
- ; ( ctx . match as any ) . extracted = ( ctx . match as any ) . extracted || [ ]
133
-
134
- const extracted = ( ctx . match as any ) . extracted
135
-
136
- const replacedLoaderData = replaceBy ( data , ( value , path ) => {
137
- const type =
138
- value instanceof ReadableStream
139
- ? 'stream'
140
- : value instanceof Promise
141
- ? 'promise'
142
- : undefined
130
+ const extracted : Array < ServerExtractedEntry > = [ ]
143
131
132
+ const replaced = replaceBy ( loaderData , ( value , path ) => {
144
133
// If it's a stream, we need to tee it so we can read it multiple times
145
- if ( type === 'stream' ) {
134
+ if ( value instanceof ReadableStream ) {
146
135
const [ copy1 , copy2 ] = value . tee ( )
147
136
const entry : ServerExtractedStream = {
148
- dataType,
149
- type,
137
+ type : 'stream' ,
150
138
path,
151
139
id : extracted . length ,
152
140
matchIndex : ctx . match . index ,
@@ -155,11 +143,10 @@ export function extractAsyncDataToMatch(
155
143
156
144
extracted . push ( entry )
157
145
return copy1
158
- } else if ( type === 'promise' ) {
146
+ } else if ( value instanceof Promise ) {
159
147
const deferredPromise = defer ( value )
160
148
const entry : ServerExtractedPromise = {
161
- dataType,
162
- type,
149
+ type : 'promise' ,
163
150
path,
164
151
id : extracted . length ,
165
152
matchIndex : ctx . match . index ,
@@ -171,7 +158,7 @@ export function extractAsyncDataToMatch(
171
158
return value
172
159
} )
173
160
174
- return replacedLoaderData
161
+ return { replaced , extracted }
175
162
}
176
163
177
164
export function onMatchSettled ( opts : {
@@ -180,148 +167,118 @@ export function onMatchSettled(opts: {
180
167
} ) {
181
168
const { router, match } = opts
182
169
183
- const [ serializedBeforeLoadData , serializedLoaderData ] = (
184
- [ '__beforeLoadContext' , 'loaderData' ] as const
185
- ) . map ( ( dataType ) => {
186
- const data = extractAsyncDataToMatch ( dataType , match [ dataType ] , {
187
- router : router ,
170
+ let extracted : Array < ServerExtractedEntry > | undefined = undefined
171
+ let serializedLoaderData : any = undefined
172
+ if ( match . loaderData !== undefined ) {
173
+ const result = extractAsyncLoaderData ( match . loaderData , {
174
+ router,
188
175
match,
189
176
} )
177
+ match . loaderData = result . replaced
178
+ extracted = result . extracted
179
+ serializedLoaderData = extracted . reduce (
180
+ ( acc : any , entry : ServerExtractedEntry ) => {
181
+ return deepImmutableSetByPath ( acc , [ 'temp' , ...entry . path ] , undefined )
182
+ } ,
183
+ { temp : result . replaced } ,
184
+ ) . temp
185
+ }
190
186
191
- const extracted = ( match as any ) . extracted as
192
- | undefined
193
- | Array < ServerExtractedEntry >
194
-
195
- return extracted
196
- ? extracted . reduce (
197
- ( acc : any , entry : ServerExtractedEntry ) => {
198
- if ( entry . dataType !== dataType ) {
199
- return deepImmutableSetByPath (
200
- acc ,
201
- [ 'temp' , ...entry . path ] ,
202
- undefined ,
203
- )
204
- }
205
- return acc
206
- } ,
207
- { temp : data } ,
208
- ) . temp
209
- : data
210
- } )
187
+ const initCode = `__TSR_SSR__.initMatch(${ jsesc (
188
+ {
189
+ id : match . id ,
190
+ __beforeLoadContext : router . ssr ! . serializer . stringify (
191
+ match . __beforeLoadContext ,
192
+ ) ,
193
+ loaderData : router . ssr ! . serializer . stringify ( serializedLoaderData ) ,
194
+ error : router . ssr ! . serializer . stringify ( match . error ) ,
195
+ extracted : extracted ?. map ( ( entry ) => pick ( entry , [ 'type' , 'path' ] ) ) ,
196
+ updatedAt : match . updatedAt ,
197
+ status : match . status ,
198
+ } satisfies SsrMatch ,
199
+ {
200
+ isScriptContext : true ,
201
+ wrap : true ,
202
+ json : true ,
203
+ } ,
204
+ ) } )`
211
205
212
- const extracted = ( match as any ) . extracted as
213
- | undefined
214
- | Array < ServerExtractedEntry >
215
-
216
- if (
217
- serializedBeforeLoadData !== undefined ||
218
- serializedLoaderData !== undefined ||
219
- extracted ?. length
220
- ) {
221
- const initCode = `__TSR_SSR__.initMatch(${ jsesc (
222
- {
223
- id : match . id ,
224
- __beforeLoadContext : router . ssr ! . serializer . stringify (
225
- serializedBeforeLoadData ,
226
- ) ,
227
- loaderData : router . ssr ! . serializer . stringify ( serializedLoaderData ) ,
228
- error : router . ssr ! . serializer . stringify ( match . error ) ,
229
- extracted : extracted
230
- ? Object . fromEntries (
231
- extracted . map ( ( entry ) => {
232
- return [ entry . id , pick ( entry , [ 'type' , 'path' ] ) ]
233
- } ) ,
234
- )
235
- : { } ,
236
- updatedAt : match . updatedAt ,
237
- status : match . status ,
238
- } satisfies SsrMatch ,
239
- {
240
- isScriptContext : true ,
241
- wrap : true ,
242
- json : true ,
243
- } ,
244
- ) } )`
206
+ router . serverSsr ! . injectScript ( ( ) => initCode )
245
207
246
- router . serverSsr ! . injectScript ( ( ) => initCode )
208
+ if ( extracted ) {
209
+ extracted . forEach ( ( entry ) => {
210
+ if ( entry . type === 'promise' ) return injectPromise ( entry )
211
+ return injectStream ( entry )
212
+ } )
213
+ }
247
214
248
- if ( extracted ) {
249
- extracted . forEach ( ( entry ) => {
250
- if ( entry . type === 'promise' ) return injectPromise ( entry )
251
- return injectStream ( entry )
252
- } )
253
- }
215
+ function injectPromise ( entry : ServerExtractedPromise ) {
216
+ router . serverSsr ! . injectScript ( async ( ) => {
217
+ await entry . promise
254
218
255
- function injectPromise ( entry : ServerExtractedPromise ) {
256
- router . serverSsr ! . injectScript ( async ( ) => {
257
- await entry . promise
258
-
259
- return `__TSR_SSR__.resolvePromise(${ jsesc (
260
- {
261
- matchId : match . id ,
262
- id : entry . id ,
263
- promiseState : entry . promise [ TSR_DEFERRED_PROMISE ] ,
264
- } satisfies ResolvePromiseState ,
265
- {
266
- isScriptContext : true ,
267
- wrap : true ,
268
- json : true ,
269
- } ,
270
- ) } )`
271
- } )
272
- }
219
+ return `__TSR_SSR__.resolvePromise(${ jsesc (
220
+ {
221
+ matchId : match . id ,
222
+ id : entry . id ,
223
+ promiseState : entry . promise [ TSR_DEFERRED_PROMISE ] ,
224
+ } satisfies ResolvePromiseState ,
225
+ {
226
+ isScriptContext : true ,
227
+ wrap : true ,
228
+ json : true ,
229
+ } ,
230
+ ) } )`
231
+ } )
232
+ }
273
233
274
- function injectStream ( entry : ServerExtractedStream ) {
275
- // Inject a promise that resolves when the stream is done
276
- // We do this to keep the stream open until we're done
277
- router . serverSsr ! . injectHtml ( async ( ) => {
278
- //
279
- try {
280
- const reader = entry . stream . getReader ( )
281
- let chunk : ReadableStreamReadResult < any > | null = null
282
- while ( ! ( chunk = await reader . read ( ) ) . done ) {
283
- if ( chunk . value ) {
284
- const code = `__TSR_SSR__.injectChunk(${ jsesc (
285
- {
286
- matchId : match . id ,
287
- id : entry . id ,
288
- chunk : chunk . value ,
289
- } ,
290
- {
291
- isScriptContext : true ,
292
- wrap : true ,
293
- json : true ,
294
- } ,
295
- ) } )`
296
-
297
- router . serverSsr ! . injectScript ( ( ) => code )
298
- }
234
+ function injectStream ( entry : ServerExtractedStream ) {
235
+ // Inject a promise that resolves when the stream is done
236
+ // We do this to keep the stream open until we're done
237
+ router . serverSsr ! . injectHtml ( async ( ) => {
238
+ //
239
+ try {
240
+ const reader = entry . stream . getReader ( )
241
+ let chunk : ReadableStreamReadResult < any > | null = null
242
+ while ( ! ( chunk = await reader . read ( ) ) . done ) {
243
+ if ( chunk . value ) {
244
+ const code = `__TSR_SSR__.injectChunk(${ jsesc (
245
+ {
246
+ matchId : match . id ,
247
+ id : entry . id ,
248
+ chunk : chunk . value ,
249
+ } ,
250
+ {
251
+ isScriptContext : true ,
252
+ wrap : true ,
253
+ json : true ,
254
+ } ,
255
+ ) } )`
256
+
257
+ router . serverSsr ! . injectScript ( ( ) => code )
299
258
}
300
-
301
- router . serverSsr ! . injectScript (
302
- ( ) =>
303
- `__TSR_SSR__.closeStream(${ jsesc (
304
- {
305
- matchId : match . id ,
306
- id : entry . id ,
307
- } ,
308
- {
309
- isScriptContext : true ,
310
- wrap : true ,
311
- json : true ,
312
- } ,
313
- ) } )`,
314
- )
315
- } catch ( err ) {
316
- console . error ( 'stream read error' , err )
317
259
}
318
260
319
- return ''
320
- } )
321
- }
322
- }
261
+ router . serverSsr ! . injectScript (
262
+ ( ) =>
263
+ `__TSR_SSR__.closeStream(${ jsesc (
264
+ {
265
+ matchId : match . id ,
266
+ id : entry . id ,
267
+ } ,
268
+ {
269
+ isScriptContext : true ,
270
+ wrap : true ,
271
+ json : true ,
272
+ } ,
273
+ ) } )`,
274
+ )
275
+ } catch ( err ) {
276
+ console . error ( 'stream read error' , err )
277
+ }
323
278
324
- return null
279
+ return ''
280
+ } )
281
+ }
325
282
}
326
283
327
284
function deepImmutableSetByPath < T > ( obj : T , path : Array < string > , value : any ) : T {
0 commit comments