@@ -448,6 +448,7 @@ export type Request = {
448
448
environmentName : ( ) => string ,
449
449
filterStackFrame : ( url : string , functionName : string ) => boolean ,
450
450
didWarnForKey : null | WeakSet < ReactComponentInfo > ,
451
+ writtenDebugObjects : WeakMap < Reference , string> ,
451
452
} ;
452
453
453
454
const {
@@ -567,6 +568,7 @@ function RequestInstance(
567
568
? defaultFilterStackFrame
568
569
: filterStackFrame ;
569
570
this . didWarnForKey = null ;
571
+ this . writtenDebugObjects = new WeakMap ( ) ;
570
572
}
571
573
572
574
let timeOrigin : number ;
@@ -3553,7 +3555,7 @@ function outlineComponentInfo(
3553
3555
) ;
3554
3556
}
3555
3557
3556
- if ( request . writtenObjects . has ( componentInfo ) ) {
3558
+ if ( request . writtenDebugObjects . has ( componentInfo ) ) {
3557
3559
// Already written
3558
3560
return ;
3559
3561
}
@@ -3606,7 +3608,10 @@ function outlineComponentInfo(
3606
3608
componentDebugInfo . props = componentInfo . props ;
3607
3609
3608
3610
const id = outlineConsoleValue ( request , counter , componentDebugInfo ) ;
3609
- request . writtenObjects . set ( componentInfo , serializeByValueID ( id ) ) ;
3611
+ const ref = serializeByValueID ( id ) ;
3612
+ request . writtenDebugObjects . set ( componentInfo , ref ) ;
3613
+ // We also store this in the main dedupe set so that it can be referenced by inline React Elements.
3614
+ request . writtenObjects . set ( componentInfo , ref ) ;
3610
3615
}
3611
3616
3612
3617
function emitIOInfoChunk (
@@ -3690,14 +3695,14 @@ function outlineIOInfo(request: Request, ioInfo: ReactIOInfo): void {
3690
3695
owner ,
3691
3696
debugStack ,
3692
3697
) ;
3693
- request . writtenObjects . set ( ioInfo , serializeByValueID ( id ) ) ;
3698
+ request . writtenDebugObjects . set ( ioInfo , serializeByValueID ( id ) ) ;
3694
3699
}
3695
3700
3696
3701
function serializeIONode (
3697
3702
request : Request ,
3698
3703
ioNode : IONode | PromiseNode ,
3699
3704
) : string {
3700
- const existingRef = request . writtenObjects . get ( ioNode ) ;
3705
+ const existingRef = request . writtenDebugObjects . get ( ioNode ) ;
3701
3706
if ( existingRef !== undefined ) {
3702
3707
// Already written
3703
3708
return existingRef ;
@@ -3740,7 +3745,7 @@ function serializeIONode(
3740
3745
stack ,
3741
3746
) ;
3742
3747
const ref = serializeByValueID ( id ) ;
3743
- request . writtenObjects . set ( ioNode , ref ) ;
3748
+ request . writtenDebugObjects . set ( ioNode , ref ) ;
3744
3749
return ref ;
3745
3750
}
3746
3751
@@ -3797,6 +3802,8 @@ function serializeEval(source: string): string {
3797
3802
return '$E ' + source ;
3798
3803
}
3799
3804
3805
+ let debugModelRoot : mixed = null ;
3806
+ let debugNoOutline : mixed = null ;
3800
3807
// This is a forked version of renderModel which should never error, never suspend and is limited
3801
3808
// in the depth it can encode.
3802
3809
function renderConsoleValue (
@@ -3840,11 +3847,57 @@ function renderConsoleValue(
3840
3847
}
3841
3848
}
3842
3849
3850
+ const writtenDebugObjects = request . writtenDebugObjects ;
3851
+ const existingDebugReference = writtenDebugObjects . get ( value ) ;
3852
+ if ( existingDebugReference !== undefined ) {
3853
+ if ( debugModelRoot === value ) {
3854
+ // This is the ID we're currently emitting so we need to write it
3855
+ // once but if we discover it again, we refer to it by id.
3856
+ debugModelRoot = null ;
3857
+ } else {
3858
+ // We've already emitted this as a debug object. We favor that version if available.
3859
+ return existingDebugReference ;
3860
+ }
3861
+ } else if ( parentPropertyName . indexOf ( ':' ) === - 1 ) {
3862
+ // TODO: If the property name contains a colon, we don't dedupe. Escape instead.
3863
+ const parentReference = writtenDebugObjects . get ( parent ) ;
3864
+ if ( parentReference !== undefined ) {
3865
+ // If the parent has a reference, we can refer to this object indirectly
3866
+ // through the property name inside that parent.
3867
+ let propertyName = parentPropertyName ;
3868
+ if ( isArray ( parent ) && parent [ 0 ] === REACT_ELEMENT_TYPE ) {
3869
+ // For elements, we've converted it to an array but we'll have converted
3870
+ // it back to an element before we read the references so the property
3871
+ // needs to be aliased.
3872
+ switch ( parentPropertyName ) {
3873
+ case '1' :
3874
+ propertyName = 'type' ;
3875
+ break ;
3876
+ case '2' :
3877
+ propertyName = 'key' ;
3878
+ break ;
3879
+ case '3' :
3880
+ propertyName = 'props' ;
3881
+ break ;
3882
+ case '4' :
3883
+ propertyName = '_owner' ;
3884
+ break ;
3885
+ }
3886
+ }
3887
+ writtenDebugObjects . set ( value , parentReference + ':' + propertyName ) ;
3888
+ } else if ( debugNoOutline !== value ) {
3889
+ // If this isn't the root object (like meta data) and we don't have an id for it, outline
3890
+ // it so that we can dedupe it by reference later.
3891
+ const outlinedId = outlineConsoleValue ( request , counter , value ) ;
3892
+ return serializeByValueID ( outlinedId ) ;
3893
+ }
3894
+ }
3895
+
3843
3896
const writtenObjects = request . writtenObjects ;
3844
3897
const existingReference = writtenObjects . get ( value ) ;
3845
3898
if ( existingReference !== undefined ) {
3846
- // We've already emitted this as a real object, so we can
3847
- // just refer to that by its existing reference .
3899
+ // We've already emitted this as a real object, so we can refer to that by its existing reference.
3900
+ // This might be slightly different serialization than what renderConsoleValue would've produced .
3848
3901
return existingReference ;
3849
3902
}
3850
3903
@@ -4068,8 +4121,8 @@ function renderConsoleValue(
4068
4121
}
4069
4122
4070
4123
// Serialize the body of the function as an eval so it can be printed.
4071
- const writtenObjects = request.writtenObjects ;
4072
- const existingReference = writtenObjects .get(value);
4124
+ const writtenDebugObjects = request.writtenDebugObjects ;
4125
+ const existingReference = writtenDebugObjects .get(value);
4073
4126
if (existingReference !== undefined) {
4074
4127
// We've already emitted this function, so we can
4075
4128
// just refer to that by its existing reference.
@@ -4085,7 +4138,7 @@ function renderConsoleValue(
4085
4138
const processedChunk = encodeReferenceChunk(request, id, serializedValue);
4086
4139
request.completedRegularChunks.push(processedChunk);
4087
4140
const reference = serializeByValueID(id);
4088
- writtenObjects .set(value, reference);
4141
+ writtenDebugObjects .set(value, reference);
4089
4142
return reference;
4090
4143
}
4091
4144
@@ -4144,6 +4197,8 @@ function serializeConsoleValue(
4144
4197
}
4145
4198
}
4146
4199
4200
+ const prevNoOutline = debugNoOutline;
4201
+ debugNoOutline = model;
4147
4202
try {
4148
4203
// $FlowFixMe[incompatible-cast] stringify can return null
4149
4204
return ( stringify ( model , replacer ) : string ) ;
@@ -4152,6 +4207,8 @@ function serializeConsoleValue(
4152
4207
return ( stringify (
4153
4208
'Unknown Value: React could not send it from the server.\n' + x . message ,
4154
4209
) : string ) ;
4210
+ } finally {
4211
+ debugNoOutline = prevNoOutline ;
4155
4212
}
4156
4213
}
4157
4214
@@ -4195,6 +4252,13 @@ function outlineConsoleValue(
4195
4252
}
4196
4253
}
4197
4254
4255
+ const id = request.nextChunkId++;
4256
+ const prevModelRoot = debugModelRoot;
4257
+ debugModelRoot = model;
4258
+ if (typeof model === 'object' && model !== null ) {
4259
+ // Future references can refer to this object by id.
4260
+ request . writtenDebugObjects . set ( model , serializeByValueID ( id ) ) ;
4261
+ }
4198
4262
let json: string;
4199
4263
try {
4200
4264
// $FlowFixMe[incompatible-cast] stringify can return null
@@ -4204,10 +4268,11 @@ function outlineConsoleValue(
4204
4268
json = ( stringify (
4205
4269
'Unknown Value: React could not send it from the server.\n' + x . message ,
4206
4270
) : string ) ;
4271
+ } finally {
4272
+ debugModelRoot = prevModelRoot ;
4207
4273
}
4208
4274
4209
4275
request.pendingChunks++;
4210
- const id = request.nextChunkId++;
4211
4276
const row = id.toString(16) + ':' + json + '\n';
4212
4277
const processedChunk = stringToChunk(row);
4213
4278
request.completedRegularChunks.push(processedChunk);
0 commit comments