@@ -6,6 +6,7 @@ import * as e from './errors.js';
66import * as devalue from 'devalue' ;
77import { get_stack } from './dev.js' ;
88import { DEV } from 'esm-env' ;
9+ import { deferred } from '../shared/utils.js' ;
910
1011/**
1112 * @template T
@@ -23,8 +24,10 @@ export function hydratable(key, fn) {
2324 let entry = hydratable . lookup . get ( key ) ;
2425
2526 if ( entry !== undefined ) {
26- if ( DEV ) {
27- compare ( key , entry , encode ( key , fn ( ) , [ ] ) ) ;
27+ if ( DEV && entry . dev ) {
28+ const comparison = compare ( key , entry , encode ( key , fn ( ) , [ ] ) ) ;
29+ comparison . catch ( ( ) => { } ) ;
30+ hydratable . comparisons . push ( comparison ) ;
2831 }
2932
3033 return /** @type {T } */ ( entry . value ) ;
@@ -49,12 +52,25 @@ function encode(key, value, values, unresolved) {
4952 const entry = { value, index : - 1 } ;
5053
5154 if ( DEV ) {
52- entry . stack = get_stack ( `hydratable"` ) ?. stack ;
55+ entry . dev = {
56+ serialized : undefined ,
57+ serialize_work : [ ] ,
58+ stack : get_stack ( 'hydratable' ) ?. stack
59+ } ;
5360 }
5461
62+ let needs_thunk = false ;
5563 let serialized = devalue . uneval ( entry . value , ( value , uneval ) => {
5664 if ( value instanceof Promise ) {
57- const serialize_promise = value . then ( ( v ) => `r(${ uneval ( v ) } )` ) ;
65+ needs_thunk = true ;
66+ /** @param {string } val */
67+ const scoped_uneval = ( val ) => {
68+ const raw = `r(${ uneval ( val ) } )` ;
69+ const result = needs_thunk ? `()=>(${ raw } )` : raw ;
70+ needs_thunk = false ;
71+ return result ;
72+ } ;
73+ const serialize_promise = value . then ( scoped_uneval ) ;
5874 unresolved ?. set ( serialize_promise , key ) ;
5975 serialize_promise . finally ( ( ) => unresolved ?. delete ( serialize_promise ) ) ;
6076
@@ -66,11 +82,12 @@ function encode(key, value, values, unresolved) {
6682 // of a given hydratable are identical with a simple string comparison
6783 const result = DEV ? `d("${ index } ")` : `d(${ index } )` ;
6884
69- if ( DEV ) {
70- ( entry . promises ??= [ ] ) . push (
85+ if ( DEV && entry . dev ) {
86+ const { dev } = entry ;
87+ dev . serialize_work . push (
7188 serialize_promise . then ( ( s ) => {
7289 serialized = serialized . replace ( result , s ) ;
73- entry . serialized = serialized ;
90+ dev . serialized = serialized ;
7491 } )
7592 ) ;
7693 }
@@ -79,7 +96,8 @@ function encode(key, value, values, unresolved) {
7996 }
8097 } ) ;
8198
82- entry . index = values . push ( serialized ) - 1 ;
99+ entry . index = values . push ( needs_thunk ? `()=>(${ serialized } )` : serialized ) - 1 ;
100+ needs_thunk = false ;
83101
84102 return entry ;
85103}
@@ -90,21 +108,22 @@ function encode(key, value, values, unresolved) {
90108 * @param {HydratableLookupEntry } b
91109 */
92110async function compare ( key , a , b ) {
93- for ( const p of a . promises ?? [ ] ) {
111+ // note: these need to be loops (as opposed to Promise.all) because
112+ // additional promises can get pushed to them while we're awaiting
113+ // an earlier one
114+ for ( const p of a . dev ?. serialize_work ?? [ ] ) {
94115 await p ;
95116 }
96117
97- for ( const p of b . promises ?? [ ] ) {
118+ for ( const p of b . dev ?. serialize_work ?? [ ] ) {
98119 await p ;
99120 }
100121
101- if ( a . serialized !== b . serialized ) {
102- // TODO right now this causes an unhandled rejection — it
103- // needs to happen somewhere else
122+ if ( a . dev ?. serialized !== b . dev ?. serialized ) {
104123 e . hydratable_clobbering (
105124 key ,
106- a . stack ?? '<missing stack trace>' ,
107- b . stack ?? '<missing stack trace>'
125+ a . dev ?. stack ?? '<missing stack trace>' ,
126+ b . dev ?. stack ?? '<missing stack trace>'
108127 ) ;
109128 }
110129}
0 commit comments