519
// DEV-only
520
pendingDebugChunks: number,
521
completedDebugChunks: Array<Chunk | BinaryChunk>,
522
+ debugDestination: null | Destination,
523
environmentName: () => string,
524
filterStackFrame: (
525
url: string,
640
if (__DEV__) {
641
this.pendingDebugChunks = 0;
642
this.completedDebugChunks = ([]: Array<Chunk>);
643
+ this.debugDestination = null;
644
this.environmentName =
645
environmentName === undefined
646
? () => 'Server'
1521
const componentName =
1522
(Component: any).displayName || Component.name || '';
1523
const componentEnv = (0, request.environmentName)();
1522
- request.pendingDebugChunks++;
1524
+ request.pendingChunks++;
1525
componentDebugInfo = ({
1526
name: componentName,
1527
env: componentEnv,
2276
const env = (0, request.environmentName)();
2277
advanceTaskTime(request, task, startTime);
2278
// Then emit a reference to us awaiting it in the current task.
2277
- request.pendingDebugChunks++;
2279
+ request.pendingChunks++;
2280
emitDebugChunk(request, task.id, {
2281
awaited: ((ioNode: any): ReactIOInfo), // This is deduped by this reference.
2282
env: env,
2336
} else if (awaitedNode !== null) {
2337
// Nothing in user space (unfiltered stack) awaited this.
2338
serializeIONode(request, awaitedNode, awaitedNode.promise);
2337
- request.pendingDebugChunks++;
2339
+ request.pendingChunks++;
2340
// We log the environment at the time when we ping which may be later than what the
2341
// environment was when we actually started awaiting.
2342
const env = (0, request.environmentName)();
4023
}
4024
4025
const json: string = serializeDebugModel(request, 500, debugInfo);
4024
- const row = serializeRowHeader('D', id) + json + '\n';
4025
- const processedChunk = stringToChunk(row);
4026
- request.completedDebugChunks.push(processedChunk);
4026
+ if (request.debugDestination !== null) {
4027
+ // Outline the actual timing information to the debug channel.
4028
+ const outlinedId = request.nextChunkId++;
4029
+ const debugRow = outlinedId.toString(16) + ':' + json + '\n';
4030
+ request.pendingDebugChunks++;
4031
+ request.completedDebugChunks.push(stringToChunk(debugRow));
4032
+ const row =
4033
+ serializeRowHeader('D', id) + '"$' + outlinedId.toString(16) + '"\n';
4034
+ request.completedRegularChunks.push(stringToChunk(row));
4035
+ } else {
4036
+ const row = serializeRowHeader('D', id) + json + '\n';
4037
+ request.completedRegularChunks.push(stringToChunk(row));
4038
+ }
4039
}
4040
4041
function outlineComponentInfo(
4953
// being no references to this as an owner.
4954
outlineComponentInfo(request, (info: any));
4955
// Emit a reference to the outlined one.
4944
- request.pendingDebugChunks++;
4956
+ request.pendingChunks++;
4957
emitDebugChunk(request, id, info);
4958
} else if (info.awaited) {
4959
const ioInfo = info.awaited;
4994
// $FlowFixMe[cannot-write]
4995
debugAsyncInfo.stack = debugStack;
4996
}
4985
- request.pendingDebugChunks++;
4997
+ request.pendingChunks++;
4998
emitDebugChunk(request, id, debugAsyncInfo);
4999
}
5000
} else {
4989
- request.pendingDebugChunks++;
5001
+ request.pendingChunks++;
5002
emitDebugChunk(request, id, info);
5003
}
5004
}
5100
// complete in time before aborting.
5101
// The best we can do is try to emit the stack of where this Promise was created.
5102
serializeIONode(request, node, null);
5091
- request.pendingDebugChunks++;
5103
+ request.pendingChunks++;
5104
const env = (0, request.environmentName)();
5105
const asyncInfo: ReactAsyncInfo = {
5106
awaited: ((node: any): ReactIOInfo), // This is deduped by this reference.
5129
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
5130
return;
5131
}
5120
- request.pendingDebugChunks++;
5132
+ request.pendingChunks++;
5133
const relativeTimestamp = timestamp - request.timeOrigin;
5122
- const row =
5123
- serializeRowHeader('D', id) + '{"time":' + relativeTimestamp + '}\n';
5124
- const processedChunk = stringToChunk(row);
5125
- // TODO: Move to its own priority queue.
5126
- request.completedDebugChunks.push(processedChunk);
5134
+ const json = '{"time":' + relativeTimestamp + '}';
5135
+ if (request.debugDestination !== null) {
5136
+ // Outline the actual timing information to the debug channel.
5137
+ const outlinedId = request.nextChunkId++;
5138
+ const debugRow = outlinedId.toString(16) + ':' + json + '\n';
5139
+ request.pendingDebugChunks++;
5140
+ request.completedDebugChunks.push(stringToChunk(debugRow));
5141
+ const row =
5142
+ serializeRowHeader('D', id) + '"$' + outlinedId.toString(16) + '"\n';
5143
+ request.completedRegularChunks.push(stringToChunk(row));
5144
+ } else {
5145
+ const row = serializeRowHeader('D', id) + json + '\n';
5146
+ request.completedRegularChunks.push(stringToChunk(row));
5147
+ }
5148
}
5149
5150
function advanceTaskTime(
5350
if (__DEV__) {
5351
const currentEnv = (0, request.environmentName)();
5352
if (currentEnv !== task.environmentName) {
5332
- request.pendingDebugChunks++;
5353
+ request.pendingChunks++;
5354
// The environment changed since we last emitted any debug information for this
5355
// task. We emit an entry that just includes the environment name change.
5356
emitDebugChunk(request, task.id, {env: currentEnv});
5465
const task = pingedTasks[i];
5466
retryTask(request, task);
5467
}
5447
- if (request.destination !== null) {
5448
- flushCompletedChunks(request, request.destination);
5449
- }
5468
+ flushCompletedChunks(request);
5469
} catch (error) {
5470
logRecoverableError(request, error, null);
5471
fatalError(request, error);
5526
request.pendingChunks--;
5527
}
5528
5510
-function flushCompletedChunks(
5511
- request: Request,
5512
- destination: Destination,
5513
-): void {
5514
- beginWriting(destination);
5515
- try {
5516
- // We emit module chunks first in the stream so that
5517
- // they can be preloaded as early as possible.
5518
- const importsChunks = request.completedImportChunks;
5519
- let i = 0;
5520
- for (; i < importsChunks.length; i++) {
5521
- request.pendingChunks--;
5522
- const chunk = importsChunks[i];
5523
- const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5524
- if (!keepWriting) {
5525
- request.destination = null;
5526
- i++;
5527
- break;
5529
+function flushCompletedChunks(request: Request): void {
5530
+ if (__DEV__ && request.debugDestination !== null) {
5531
+ const debugDestination = request.debugDestination;
5532
+ beginWriting(debugDestination);
5533
+ try {
5534
+ const debugChunks = request.completedDebugChunks;
5535
+ let i = 0;
5536
+ for (; i < debugChunks.length; i++) {
5537
+ request.pendingDebugChunks--;
5538
+ const chunk = debugChunks[i];
5539
+ writeChunkAndReturn(debugDestination, chunk);
5540
}
5541
+ debugChunks.splice(0, i);
5542
+ } finally {
5543
+ completeWriting(debugDestination);
5544
}
5530
- importsChunks.splice(0, i);
5531
-
5532
- // Next comes hints.
5533
- const hintChunks = request.completedHintChunks;
5534
- i = 0;
5535
- for (; i < hintChunks.length; i++) {
5536
- const chunk = hintChunks[i];
5537
- const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5538
- if (!keepWriting) {
5539
- request.destination = null;
5540
- i++;
5541
- break;
5545
+ flushBuffered(debugDestination);
5546
+ }
5547
+ const destination = request.destination;
5548
+ if (destination !== null) {
5549
+ beginWriting(destination);
5550
+ try {
5551
+ // We emit module chunks first in the stream so that
5552
+ // they can be preloaded as early as possible.
5553
+ const importsChunks = request.completedImportChunks;
5554
+ let i = 0;
5555
+ for (; i < importsChunks.length; i++) {
5556
+ request.pendingChunks--;
5557
+ const chunk = importsChunks[i];
5558
+ const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5559
+ if (!keepWriting) {
5560
+ request.destination = null;
5561
+ i++;
5562
+ break;
5563
+ }
5564
}
5543
- }
5544
- hintChunks.splice(0, i);
5565
+ importsChunks.splice(0, i);
5566
5546
- // Debug meta data comes before the model data because it will often end up blocking the model from
5547
- // completing since the JSX will reference the debug data.
5548
- if (__DEV__) {
5549
- const debugChunks = request.completedDebugChunks;
5567
+ // Next comes hints.
5568
+ const hintChunks = request.completedHintChunks;
5569
i = 0;
5551
- for (; i < debugChunks.length; i++) {
5552
- request.pendingDebugChunks--;
5553
- const chunk = debugChunks[i];
5570
+ for (; i < hintChunks.length; i++) {
5571
+ const chunk = hintChunks[i];
5572
const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5573
if (!keepWriting) {
5574
request.destination = null;
5576
break;
5577
}
5578
}
5561
- debugChunks.splice(0, i);
5562
- }
5579
+ hintChunks.splice(0, i);
5580
+
5581
+ // Debug meta data comes before the model data because it will often end up blocking the model from
5582
+ // completing since the JSX will reference the debug data.
5583
+ if (__DEV__ && request.debugDestination === null) {
5584
+ const debugChunks = request.completedDebugChunks;
5585
+ i = 0;
5586
+ for (; i < debugChunks.length; i++) {
5587
+ request.pendingDebugChunks--;
5588
+ const chunk = debugChunks[i];
5589
+ const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5590
+ if (!keepWriting) {
5591
+ request.destination = null;
5592
+ i++;
5593
+ break;
5594
+ }
5595
+ }
5596
+ debugChunks.splice(0, i);
5597
+ }
5598
5564
- // Next comes model data.
5565
- const regularChunks = request.completedRegularChunks;
5566
- i = 0;
5567
- for (; i < regularChunks.length; i++) {
5568
- request.pendingChunks--;
5569
- const chunk = regularChunks[i];
5570
- const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5571
- if (!keepWriting) {
5572
- request.destination = null;
5573
- i++;
5574
- break;
5599
+ // Next comes model data.
5600
+ const regularChunks = request.completedRegularChunks;
5601
+ i = 0;
5602
+ for (; i < regularChunks.length; i++) {
5603
+ request.pendingChunks--;
5604
+ const chunk = regularChunks[i];
5605
+ const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5606
+ if (!keepWriting) {
5607
+ request.destination = null;
5608
+ i++;
5609
+ break;
5610
+ }
5611
}
5576
- }
5577
- regularChunks.splice(0, i);
5578
-
5579
- // Finally, errors are sent. The idea is that it's ok to delay
5580
- // any error messages and prioritize display of other parts of
5581
- // the page.
5582
- const errorChunks = request.completedErrorChunks;
5583
- i = 0;
5584
- for (; i < errorChunks.length; i++) {
5585
- request.pendingChunks--;
5586
- const chunk = errorChunks[i];
5587
- const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5588
- if (!keepWriting) {
5589
- request.destination = null;
5590
- i++;
5591
- break;
5612
+ regularChunks.splice(0, i);
5613
+
5614
+ // Finally, errors are sent. The idea is that it's ok to delay
5615
+ // any error messages and prioritize display of other parts of
5616
+ // the page.
5617
+ const errorChunks = request.completedErrorChunks;
5618
+ i = 0;
5619
+ for (; i < errorChunks.length; i++) {
5620
+ request.pendingChunks--;
5621
+ const chunk = errorChunks[i];
5622
+ const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
5623
+ if (!keepWriting) {
5624
+ request.destination = null;
5625
+ i++;
5626
+ break;
5627
+ }
5628
}
5629
+ errorChunks.splice(0, i);
5630
+ } finally {
5631
+ request.flushScheduled = false;
5632
+ completeWriting(destination);
5633
}
5594
- errorChunks.splice(0, i);
5595
- } finally {
5596
- request.flushScheduled = false;
5597
- completeWriting(destination);
5634
+ flushBuffered(destination);
5635
}
5599
- flushBuffered(destination);
5600
- if (
5601
- request.pendingChunks === 0 &&
5602
- (!__DEV__ || request.pendingDebugChunks === 0)
5603
- ) {
5636
+ if (request.pendingChunks === 0) {
5637
+ if (__DEV__) {
5638
+ const debugDestination = request.debugDestination;
5639
+ if (request.pendingDebugChunks === 0) {
5640
+ // Continue fully closing both streams.
5641
+ if (debugDestination !== null) {
5642
+ close(debugDestination);
5643
+ request.debugDestination = null;
5644
+ }
5645
+ } else {
5646
+ // We still have debug information to write.
5647
+ if (debugDestination === null) {
5648
+ // We'll continue writing on this stream so nothing closes.
5649
+ return;
5650
+ } else {
5651
+ // We'll close the main stream but keep the debug stream open.
5652
+ // TODO: If this destination is not currently flowing we'll not close it when it resumes flowing.
5653
+ // We should keep a separate status for this.
5654
+ if (request.destination !== null) {
5655
+ close(request.destination);
5656
+ request.destination = null;
5657
+ }
5658
+ return;
5659
+ }
5660
+ }
5661
+ }
5662
// We're done.
5663
if (enableTaint) {
5664
cleanupTaintQueue(request);
5670
request.cacheController.abort(abortReason);
5671
}
5672
request.status = CLOSED;
5615
- close(destination);
5616
- request.destination = null;
5673
+ if (request.destination !== null) {
5674
+ close(request.destination);
5675
+ request.destination = null;
5676
+ }
5677
+ if (__DEV__ && request.debugDestination !== null) {
5678
+ close(request.debugDestination);
5679
+ request.debugDestination = null;
5680
+ }
5681
}
5682
}
5683
5704
request.pingedTasks.length === 0 &&
5705
// If there is no destination there is nothing we can flush to. A flush will
5706
// happen when we start flowing again
5643
- request.destination !== null
5707
+ (request.destination !== null ||
5708
+ (__DEV__ && request.debugDestination !== null))
5709
) {
5710
request.flushScheduled = true;
5711
// Unlike startWork and pingTask we intetionally use scheduleWork
5712
// here even during prerenders to allow as much batching as possible
5713
scheduleWork(() => {
5714
request.flushScheduled = false;
5650
- const destination = request.destination;
5651
- if (destination) {
5652
- flushCompletedChunks(request, destination);
5653
- }
5715
+ flushCompletedChunks(request);
5716
});
5717
}
5718
}
5739
}
5740
request.destination = destination;
5741
try {
5680
- flushCompletedChunks(request, destination);
5742
+ flushCompletedChunks(request);
5743
+ } catch (error) {
5744
+ logRecoverableError(request, error, null);
5745
+ fatalError(request, error);
5746
+ }
5747
+}
5748
+
5749
+export function startFlowingDebug(
5750
+ request: Request,
5751
+ debugDestination: Destination,
5752
+): void {
5753
+ if (request.status === CLOSING) {
5754
+ request.status = CLOSED;
5755
+ closeWithError(debugDestination, request.fatalError);
5756
+ return;
5757
+ }
5758
+ if (request.status === CLOSED) {
5759
+ return;
5760
+ }
5761
+ if (request.debugDestination !== null) {
5762
+ // We're already flowing.
5763
+ return;
5764
+ }
5765
+ request.debugDestination = debugDestination;
5766
+ try {
5767
+ flushCompletedChunks(request);
5768
} catch (error) {
5769
logRecoverableError(request, error, null);
5770
fatalError(request, error);
5780
abortedTasks.forEach(task => finishHaltedTask(task, request));
5781
const onAllReady = request.onAllReady;
5782
onAllReady();
5696
- if (request.destination !== null) {
5697
- flushCompletedChunks(request, request.destination);
5698
- }
5783
+ flushCompletedChunks(request);
5784
} catch (error) {
5785
logRecoverableError(request, error, null);
5786
fatalError(request, error);
5796
abortedTasks.forEach(task => finishAbortedTask(task, request, errorId));
5797
const onAllReady = request.onAllReady;
5798
onAllReady();
5714
- if (request.destination !== null) {
5715
- flushCompletedChunks(request, request.destination);
5716
- }
5799
+ flushCompletedChunks(request);
5800
} catch (error) {
5801
logRecoverableError(request, error, null);
5802
fatalError(request, error);
5863
} else {
5864
const onAllReady = request.onAllReady;
5865
onAllReady();
5783
- if (request.destination !== null) {
5784
- flushCompletedChunks(request, request.destination);
5785
- }
5866
+ flushCompletedChunks(request);
5867
}
5868
} catch (error) {
5869
logRecoverableError(request, error, null);