1148
? null
1149
: filterStackTrace(request, task.debugStack, 1);
1150
// $FlowFixMe[cannot-write]
1151
+ componentDebugInfo.props = props;
1152
+ // $FlowFixMe[cannot-write]
1153
componentDebugInfo.debugStack = task.debugStack;
1154
// $FlowFixMe[cannot-write]
1155
componentDebugInfo.debugTask = task.debugTask;
1156
+ } else {
1157
+ // $FlowFixMe[cannot-write]
1158
+ componentDebugInfo.props = props;
1159
}
1160
// We outline this model eagerly so that we can refer to by reference as an owner.
1161
// If we had a smarter way to dedupe we might not have to do this if there ends up
1162
// being no references to this as an owner.
1158
- outlineModel(request, componentDebugInfo);
1163
+
1164
+ outlineComponentInfo(request, componentDebugInfo);
1165
emitDebugChunk(request, componentDebugID, componentDebugInfo);
1166
1167
// We've emitted the latest environment for this task so we track that.
1588
} else if (keyPath !== null) {
1589
key = keyPath + ',' + key;
1590
}
1591
+ if (__DEV__) {
1592
+ if (task.debugOwner !== null) {
1593
+ // Ensure we outline this owner if it is the first time we see it.
1594
+ // So that we can refer to it directly.
1595
+ outlineComponentInfo(request, task.debugOwner);
1596
+ }
1597
+ }
1598
const element = __DEV__
1599
? enableOwnerStacks
1600
? [
1715
task.debugStack === null
1716
? null
1717
: filterStackTrace(request, task.debugStack, 1),
1718
+ props: props,
1719
debugStack: task.debugStack,
1720
debugTask: task.debugTask,
1721
};
2142
2143
function serializeConsoleMap(
2144
request: Request,
2131
- counter: {objectCount: number},
2145
+ counter: {objectLimit: number},
2146
map: Map<ReactClientValue, ReactClientValue>,
2147
): string {
2148
// Like serializeMap but for renderConsoleValue.
2153
2154
function serializeConsoleSet(
2155
request: Request,
2142
- counter: {objectCount: number},
2156
+ counter: {objectLimit: number},
2157
set: Set<ReactClientValue>,
2158
): string {
2159
// Like serializeMap but for renderConsoleValue.
2277
}
2278
}
2279
2266
-function isReactComponentInfo(value: any): boolean {
2267
- // TODO: We don't currently have a brand check on ReactComponentInfo. Reconsider.
2268
- return (
2269
- ((typeof value.debugTask === 'object' &&
2270
- value.debugTask !== null &&
2271
- // $FlowFixMe[method-unbinding]
2272
- typeof value.debugTask.run === 'function') ||
2273
- value.debugStack instanceof Error) &&
2274
- (enableOwnerStacks
2275
- ? isArray((value: any).stack) || (value: any).stack === null
2276
- : typeof (value: any).stack === 'undefined') &&
2277
- typeof value.name === 'string' &&
2278
- typeof value.env === 'string' &&
2279
- value.owner !== undefined
2280
- );
2281
-}
2282
-
2280
let modelRoot: null | ReactClientValue = false;
2281
2282
function renderModel(
2792
);
2793
}
2794
if (__DEV__) {
2798
- if (isReactComponentInfo(value)) {
2799
- // This looks like a ReactComponentInfo. We can't serialize the ConsoleTask object so we
2800
- // need to omit it before serializing.
2801
- const componentDebugInfo: Omit<
2802
- ReactComponentInfo,
2803
- 'debugTask' | 'debugStack',
2804
- > = {
2805
- name: (value: any).name,
2806
- env: (value: any).env,
2807
- key: (value: any).key,
2808
- owner: (value: any).owner,
2809
- };
2810
- if (enableOwnerStacks) {
2811
- // $FlowFixMe[cannot-write]
2812
- componentDebugInfo.stack = (value: any).stack;
2813
- }
2814
- return componentDebugInfo;
2815
- }
2816
-
2795
if (objectName(value) !== 'Object') {
2796
callWithDebugContextInDEV(request, task, () => {
2797
console.error(
3219
3220
// We use the console encoding so that we can dedupe objects but don't necessarily
3221
// use the full serialization that requires a task.
3244
- const counter = {objectCount: 0};
3222
+ const counter = {objectLimit: 500};
3223
function replacer(
3224
this:
3225
| {+[key: string | number]: ReactClientValue}
3243
request.completedRegularChunks.push(processedChunk);
3244
}
3245
3246
+function outlineComponentInfo(
3247
+ request: Request,
3248
+ componentInfo: ReactComponentInfo,
3249
+): void {
3250
+ if (!__DEV__) {
3251
+ // These errors should never make it into a build so we don't need to encode them in codes.json
3252
+ // eslint-disable-next-line react-internal/prod-error-codes
3253
+ throw new Error(
3254
+ 'outlineComponentInfo should never be called in production mode. This is a bug in React.',
3255
+ );
3256
+ }
3257
+
3258
+ if (request.writtenObjects.has(componentInfo)) {
3259
+ // Already written
3260
+ return;
3261
+ }
3262
+
3263
+ if (componentInfo.owner != null) {
3264
+ // Ensure the owner is already outlined.
3265
+ outlineComponentInfo(request, componentInfo.owner);
3266
+ }
3267
+
3268
+ // Limit the number of objects we write to prevent emitting giant props objects.
3269
+ let objectLimit = 10;
3270
+ if (componentInfo.stack != null) {
3271
+ // Ensure we have enough object limit to encode the stack trace.
3272
+ objectLimit += componentInfo.stack.length;
3273
+ }
3274
+
3275
+ // We use the console encoding so that we can dedupe objects but don't necessarily
3276
+ // use the full serialization that requires a task.
3277
+ const counter = {objectLimit};
3278
+
3279
+ // We can't serialize the ConsoleTask/Error objects so we need to omit them before serializing.
3280
+ const componentDebugInfo: Omit<
3281
+ ReactComponentInfo,
3282
+ 'debugTask' | 'debugStack',
3283
+ > = {
3284
+ name: componentInfo.name,
3285
+ env: componentInfo.env,
3286
+ key: componentInfo.key,
3287
+ owner: componentInfo.owner,
3288
+ };
3289
+ if (enableOwnerStacks) {
3290
+ // $FlowFixMe[cannot-write]
3291
+ componentDebugInfo.stack = componentInfo.stack;
3292
+ }
3293
+ // Ensure we serialize props after the stack to favor the stack being complete.
3294
+ // $FlowFixMe[cannot-write]
3295
+ componentDebugInfo.props = componentInfo.props;
3296
+
3297
+ const id = outlineConsoleValue(request, counter, componentDebugInfo);
3298
+ request.writtenObjects.set(componentInfo, serializeByValueID(id));
3299
+}
3300
+
3301
function emitTypedArrayChunk(
3302
request: Request,
3303
id: number,
3355
// in the depth it can encode.
3356
function renderConsoleValue(
3357
request: Request,
3325
- counter: {objectCount: number},
3358
+ counter: {objectLimit: number},
3359
parent:
3360
| {+[propertyName: string | number]: ReactClientValue}
3361
| $ReadOnlyArray<ReactClientValue>,
3399
}
3400
}
3401
3369
- if (counter.objectCount > 500) {
3402
+ const writtenObjects = request.writtenObjects;
3403
+ const existingReference = writtenObjects.get(value);
3404
+ if (existingReference !== undefined) {
3405
+ // We've already emitted this as a real object, so we can
3406
+ // just refer to that by its existing reference.
3407
+ return existingReference;
3408
+ }
3409
+
3410
+ if (counter.objectLimit <= 0) {
3411
// We've reached our max number of objects to serialize across the wire so we serialize this
3412
// as a marker so that the client can error when this is accessed by the console.
3413
return serializeLimitedObject();
3414
}
3415
3375
- counter.objectCount++;
3416
+ counter.objectLimit--;
3417
3377
- const writtenObjects = request.writtenObjects;
3378
- const existingReference = writtenObjects.get(value);
3379
- // $FlowFixMe[method-unbinding]
3380
- if (typeof value.then === 'function') {
3381
- if (existingReference !== undefined) {
3382
- // We've seen this promise before, so we can just refer to the same result.
3383
- return existingReference;
3418
+ switch ((value: any).$$typeof) {
3419
+ case REACT_ELEMENT_TYPE: {
3420
+ const element: ReactElement = (value: any);
3421
+
3422
+ if (element._owner != null) {
3423
+ outlineComponentInfo(request, element._owner);
3424
+ }
3425
+ if (enableOwnerStacks) {
3426
+ let debugStack: null | ReactStackTrace = null;
3427
+ if (element._debugStack != null) {
3428
+ // Outline the debug stack so that it doesn't get cut off.
3429
+ debugStack = filterStackTrace(request, element._debugStack, 1);
3430
+ const stackId = outlineConsoleValue(
3431
+ request,
3432
+ {objectLimit: debugStack.length + 2},
3433
+ debugStack,
3434
+ );
3435
+ request.writtenObjects.set(debugStack, serializeByValueID(stackId));
3436
+ }
3437
+ return [
3438
+ REACT_ELEMENT_TYPE,
3439
+ element.type,
3440
+ element.key,
3441
+ element.props,
3442
+ element._owner,
3443
+ debugStack,
3444
+ element._store.validated,
3445
+ ];
3446
+ }
3447
+
3448
+ return [
3449
+ REACT_ELEMENT_TYPE,
3450
+ element.type,
3451
+ element.key,
3452
+ element.props,
3453
+ element._owner,
3454
+ ];
3455
}
3456
+ }
3457
3458
+ // $FlowFixMe[method-unbinding]
3459
+ if (typeof value.then === 'function') {
3460
const thenable: Thenable<any> = (value: any);
3461
switch (thenable.status) {
3462
case 'fulfilled': {
3490
return serializeInfinitePromise();
3491
}
3492
3419
- if (existingReference !== undefined) {
3420
- // We've already emitted this as a real object, so we can
3421
- // just refer to that by its existing reference.
3422
- return existingReference;
3423
- }
3424
-
3493
if (isArray(value)) {
3494
return value;
3495
}
3571
return Array.from((value: any));
3572
}
3573
3506
- if (isReactComponentInfo(value)) {
3507
- // This looks like a ReactComponentInfo. We can't serialize the ConsoleTask object so we
3508
- // need to omit it before serializing.
3509
- const componentDebugInfo: Omit<
3510
- ReactComponentInfo,
3511
- 'debugTask' | 'debugStack',
3512
- > = {
3513
- name: (value: any).name,
3514
- env: (value: any).env,
3515
- key: (value: any).key,
3516
- owner: (value: any).owner,
3517
- };
3518
- if (enableOwnerStacks) {
3519
- // $FlowFixMe[cannot-write]
3520
- componentDebugInfo.stack = (value: any).stack;
3521
- }
3522
- return componentDebugInfo;
3523
- }
3524
-
3574
// $FlowFixMe[incompatible-return]
3575
return value;
3576
}
3651
3652
function outlineConsoleValue(
3653
request: Request,
3605
- counter: {objectCount: number},
3654
+ counter: {objectLimit: number},
3655
model: ReactClientValue,
3656
): number {
3657
if (!__DEV__) {
3678
value,
3679
);
3680
} catch (x) {
3632
- return 'unknown value';
3681
+ return (
3682
+ 'Unknown Value: React could not send it from the server.\n' + x.message
3683
+ );
3684
}
3685
}
3686
3711
);
3712
}
3713
3663
- const counter = {objectCount: 0};
3714
+ const counter = {objectLimit: 500};
3715
function replacer(
3716
this:
3717
| {+[key: string | number]: ReactClientValue}
3728
value,
3729
);
3730
} catch (x) {
3680
- return 'unknown value';
3731
+ return (
3732
+ 'Unknown Value: React could not send it from the server.\n' + x.message
3733
+ );
3734
}
3735
}
3736
3737
+ // Ensure the owner is already outlined.
3738
+ if (owner != null) {
3739
+ outlineComponentInfo(request, owner);
3740
+ }
3741
+
3742
// TODO: Don't double badge if this log came from another Flight Client.
3743
const env = (0, request.environmentName)();
3744
const payload = [methodName, stackTrace, owner, env];
3762
// We outline this model eagerly so that we can refer to by reference as an owner.
3763
// If we had a smarter way to dedupe we might not have to do this if there ends up
3764
// being no references to this as an owner.
3707
- outlineModel(request, debugInfo[i]);
3765
+ outlineComponentInfo(request, (debugInfo[i]: any));
3766
}
3767
emitDebugChunk(request, id, debugInfo[i]);
3768
}