71
72
import {readTemporaryReference} from './ReactFlightTemporaryReferences';
73
74
+import {logComponentRender} from './ReactFlightPerformanceTrack';
75
+
76
import {
77
REACT_LAZY_TYPE,
78
REACT_ELEMENT_TYPE,
126
| {+[key: string]: JSONValue}
127
| $ReadOnlyArray<JSONValue>;
128
129
+type ProfilingResult = {
130
+ endTime: number,
131
+};
132
+
133
const ROW_ID = 0;
134
const ROW_TAG = 1;
135
const ROW_LENGTH = 2;
150
value: null | Array<(T) => mixed>,
151
reason: null | Array<(mixed) => mixed>,
152
_response: Response,
147
- _debugInfo?: null | ReactDebugInfo,
153
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
154
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
155
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
156
};
157
type BlockedChunk<T> = {
159
value: null | Array<(T) => mixed>,
160
reason: null | Array<(mixed) => mixed>,
161
_response: Response,
155
- _debugInfo?: null | ReactDebugInfo,
162
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
163
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
164
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
165
};
166
type ResolvedModelChunk<T> = {
168
value: UninitializedModel,
169
reason: null,
170
_response: Response,
163
- _debugInfo?: null | ReactDebugInfo,
171
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
172
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
173
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
174
};
175
type ResolvedModuleChunk<T> = {
177
value: ClientReference<T>,
178
reason: null,
179
_response: Response,
171
- _debugInfo?: null | ReactDebugInfo,
180
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
181
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
182
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
183
};
184
type InitializedChunk<T> = {
186
value: T,
187
reason: null | FlightStreamController,
188
_response: Response,
179
- _debugInfo?: null | ReactDebugInfo,
189
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
190
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
191
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
192
};
193
type InitializedStreamChunk<
197
value: T,
198
reason: FlightStreamController,
199
_response: Response,
189
- _debugInfo?: null | ReactDebugInfo,
200
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
201
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
202
then(resolve: (ReadableStream) => mixed, reject?: (mixed) => mixed): void,
203
};
204
type ErroredChunk<T> = {
206
value: null,
207
reason: mixed,
208
_response: Response,
197
- _debugInfo?: null | ReactDebugInfo,
209
+ _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only
210
+ _debugInfo?: null | ReactDebugInfo, // DEV-only
211
then(resolve: (T) => mixed, reject?: (mixed) => mixed): void,
212
};
213
type SomeChunk<T> =
229
this.value = value;
230
this.reason = reason;
231
this._response = response;
232
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
233
+ this._children = [];
234
+ }
235
if (__DEV__) {
236
this._debugInfo = null;
237
}
564
errored: boolean,
565
};
566
let initializingHandler: null | InitializationHandler = null;
567
+let initializingChunk: null | BlockedChunk<any> = null;
568
569
function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
570
const prevHandler = initializingHandler;
571
+ const prevChunk = initializingChunk;
572
initializingHandler = null;
573
574
const resolvedModel = chunk.value;
581
cyclicChunk.value = null;
582
cyclicChunk.reason = null;
583
584
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
585
+ initializingChunk = cyclicChunk;
586
+ }
587
+
588
try {
589
const value: T = parseModel(chunk._response, resolvedModel);
590
// Invoke any listeners added while resolving this model. I.e. cyclic
617
erroredChunk.reason = error;
618
} finally {
619
initializingHandler = prevHandler;
620
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
621
+ initializingChunk = prevChunk;
622
+ }
623
}
624
}
625
647
triggerErrorOnChunk(chunk, error);
648
}
649
});
650
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
651
+ flushComponentPerformance(getChunk(response, 0));
652
+ }
653
}
654
655
function nullRefGetter() {
1238
const path = reference.split(':');
1239
const id = parseInt(path[0], 16);
1240
const chunk = getChunk(response, id);
1241
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
1242
+ if (initializingChunk !== null && isArray(initializingChunk._children)) {
1243
+ initializingChunk._children.push(chunk);
1244
+ }
1245
+ }
1246
switch (chunk.status) {
1247
case RESOLVED_MODEL:
1248
initializeModelChunk(chunk);
1392
// Lazy node
1393
const id = parseInt(value.slice(2), 16);
1394
const chunk = getChunk(response, id);
1395
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
1396
+ if (
1397
+ initializingChunk !== null &&
1398
+ isArray(initializingChunk._children)
1399
+ ) {
1400
+ initializingChunk._children.push(chunk);
1401
+ }
1402
+ }
1403
// We create a React.lazy wrapper around any lazy values.
1404
// When passed into React, we'll know how to suspend on this.
1405
return createLazyChunkWrapper(chunk);
1412
}
1413
const id = parseInt(value.slice(2), 16);
1414
const chunk = getChunk(response, id);
1415
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
1416
+ if (
1417
+ initializingChunk !== null &&
1418
+ isArray(initializingChunk._children)
1419
+ ) {
1420
+ initializingChunk._children.push(chunk);
1421
+ }
1422
+ }
1423
return chunk;
1424
}
1425
case 'S': {
2753
resolveBuffer(response, id, view);
2754
}
2755
2756
+function flushComponentPerformance(root: SomeChunk<any>): number {
2757
+ if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
2758
+ return 0;
2759
+ }
2760
+ // Write performance.measure() entries for Server Components in tree order.
2761
+ // This must be done at the end to collect the end time from the whole tree.
2762
+ if (!isArray(root._children)) {
2763
+ // We have already written this chunk. If this was a cycle, then this will
2764
+ // be -Infinity and it won't contribute to the parent end time.
2765
+ // If this was already emitted by another sibling then we reused the same
2766
+ // chunk in two places. We should extend the current end time as if it was
2767
+ // rendered as part of this tree.
2768
+ const previousResult: ProfilingResult = root._children;
2769
+ return previousResult.endTime;
2770
+ }
2771
+ const children = root._children;
2772
+ if (root.status === RESOLVED_MODEL) {
2773
+ // If the model is not initialized by now, do that now so we can find its
2774
+ // children. This part is a little sketchy since it significantly changes
2775
+ // the performance characteristics of the app by profiling.
2776
+ initializeModelChunk(root);
2777
+ }
2778
+ const result: ProfilingResult = {endTime: -Infinity};
2779
+ root._children = result;
2780
+ let childrenEndTime = -Infinity;
2781
+ for (let i = 0; i < children.length; i++) {
2782
+ const childEndTime = flushComponentPerformance(children[i]);
2783
+ if (childEndTime > childrenEndTime) {
2784
+ childrenEndTime = childEndTime;
2785
+ }
2786
+ }
2787
+ const debugInfo = root._debugInfo;
2788
+ if (debugInfo) {
2789
+ let endTime = 0;
2790
+ for (let i = debugInfo.length - 1; i >= 0; i--) {
2791
+ const info = debugInfo[i];
2792
+ if (typeof info.time === 'number') {
2793
+ endTime = info.time;
2794
+ if (endTime > childrenEndTime) {
2795
+ childrenEndTime = endTime;
2796
+ }
2797
+ }
2798
+ if (typeof info.name === 'string' && i > 0) {
2799
+ // $FlowFixMe: Refined.
2800
+ const componentInfo: ReactComponentInfo = info;
2801
+ const startTimeInfo = debugInfo[i - 1];
2802
+ if (typeof startTimeInfo.time === 'number') {
2803
+ const startTime = startTimeInfo.time;
2804
+ logComponentRender(
2805
+ componentInfo,
2806
+ startTime,
2807
+ endTime,
2808
+ childrenEndTime,
2809
+ );
2810
+ }
2811
+ }
2812
+ }
2813
+ }
2814
+ return (result.endTime = childrenEndTime);
2815
+}
2816
+
2817
function processFullBinaryRow(
2818
response: Response,
2819
id: number,