Skip to content

Commit c99bb3f

Browse files
committed
Update on "[compiler] Add types for WeakMap, WeakSet, and reanimated shared values"
Building on mofeiz's recent work to type constructors. Also, types for reanimated values which are useful in the next PR. [ghstack-poisoned]
2 parents 6f45e9d + a4565f6 commit c99bb3f

21 files changed

+491
-411
lines changed

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,15 @@ const ACTIVITY_END_DATA = '/&';
238238
const SUSPENSE_START_DATA = '$';
239239
const SUSPENSE_END_DATA = '/$';
240240
const SUSPENSE_PENDING_START_DATA = '$?';
241+
const SUSPENSE_QUEUED_START_DATA = '$~';
241242
const SUSPENSE_FALLBACK_START_DATA = '$!';
242243
const PREAMBLE_CONTRIBUTION_HTML = 'html';
243244
const PREAMBLE_CONTRIBUTION_BODY = 'body';
244245
const PREAMBLE_CONTRIBUTION_HEAD = 'head';
245246
const FORM_STATE_IS_MATCHING = 'F!';
246247
const FORM_STATE_IS_NOT_MATCHING = 'F';
247248

248-
const DOCUMENT_READY_STATE_COMPLETE = 'complete';
249+
const DOCUMENT_READY_STATE_LOADING = 'loading';
249250

250251
const STYLE = 'style';
251252

@@ -1084,6 +1085,7 @@ function clearHydrationBoundary(
10841085
} else if (
10851086
data === SUSPENSE_START_DATA ||
10861087
data === SUSPENSE_PENDING_START_DATA ||
1088+
data === SUSPENSE_QUEUED_START_DATA ||
10871089
data === SUSPENSE_FALLBACK_START_DATA ||
10881090
data === ACTIVITY_START_DATA
10891091
) {
@@ -1205,6 +1207,7 @@ function hideOrUnhideDehydratedBoundary(
12051207
} else if (
12061208
data === SUSPENSE_START_DATA ||
12071209
data === SUSPENSE_PENDING_START_DATA ||
1210+
data === SUSPENSE_QUEUED_START_DATA ||
12081211
data === SUSPENSE_FALLBACK_START_DATA
12091212
) {
12101213
depth++;
@@ -3140,7 +3143,10 @@ export function canHydrateSuspenseInstance(
31403143
}
31413144
31423145
export function isSuspenseInstancePending(instance: SuspenseInstance): boolean {
3143-
return instance.data === SUSPENSE_PENDING_START_DATA;
3146+
return (
3147+
instance.data === SUSPENSE_PENDING_START_DATA ||
3148+
instance.data === SUSPENSE_QUEUED_START_DATA
3149+
);
31443150
}
31453151
31463152
export function isSuspenseInstanceFallback(
@@ -3149,7 +3155,7 @@ export function isSuspenseInstanceFallback(
31493155
return (
31503156
instance.data === SUSPENSE_FALLBACK_START_DATA ||
31513157
(instance.data === SUSPENSE_PENDING_START_DATA &&
3152-
instance.ownerDocument.readyState === DOCUMENT_READY_STATE_COMPLETE)
3158+
instance.ownerDocument.readyState !== DOCUMENT_READY_STATE_LOADING)
31533159
);
31543160
}
31553161
@@ -3192,15 +3198,19 @@ export function registerSuspenseInstanceRetry(
31923198
callback: () => void,
31933199
) {
31943200
const ownerDocument = instance.ownerDocument;
3195-
if (
3201+
if (instance.data === SUSPENSE_QUEUED_START_DATA) {
3202+
// The Fizz runtime has already queued this boundary for reveal. We wait for it
3203+
// to be revealed and then retries.
3204+
instance._reactRetry = callback;
3205+
} else if (
31963206
// The Fizz runtime must have put this boundary into client render or complete
31973207
// state after the render finished but before it committed. We need to call the
31983208
// callback now rather than wait
31993209
instance.data !== SUSPENSE_PENDING_START_DATA ||
32003210
// The boundary is still in pending status but the document has finished loading
32013211
// before we could register the event handler that would have scheduled the retry
32023212
// on load so we call teh callback now.
3203-
ownerDocument.readyState === DOCUMENT_READY_STATE_COMPLETE
3213+
ownerDocument.readyState !== DOCUMENT_READY_STATE_LOADING
32043214
) {
32053215
callback();
32063216
} else {
@@ -3255,18 +3265,19 @@ function getNextHydratable(node: ?Node) {
32553265
break;
32563266
}
32573267
if (nodeType === COMMENT_NODE) {
3258-
const nodeData = (node: any).data;
3268+
const data = (node: any).data;
32593269
if (
3260-
nodeData === SUSPENSE_START_DATA ||
3261-
nodeData === SUSPENSE_FALLBACK_START_DATA ||
3262-
nodeData === SUSPENSE_PENDING_START_DATA ||
3263-
nodeData === ACTIVITY_START_DATA ||
3264-
nodeData === FORM_STATE_IS_MATCHING ||
3265-
nodeData === FORM_STATE_IS_NOT_MATCHING
3270+
data === SUSPENSE_START_DATA ||
3271+
data === SUSPENSE_FALLBACK_START_DATA ||
3272+
data === SUSPENSE_PENDING_START_DATA ||
3273+
data === SUSPENSE_QUEUED_START_DATA ||
3274+
data === ACTIVITY_START_DATA ||
3275+
data === FORM_STATE_IS_MATCHING ||
3276+
data === FORM_STATE_IS_NOT_MATCHING
32663277
) {
32673278
break;
32683279
}
3269-
if (nodeData === SUSPENSE_END_DATA || nodeData === ACTIVITY_END_DATA) {
3280+
if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {
32703281
return null;
32713282
}
32723283
}
@@ -3494,6 +3505,7 @@ function getNextHydratableInstanceAfterHydrationBoundary(
34943505
data === SUSPENSE_START_DATA ||
34953506
data === SUSPENSE_FALLBACK_START_DATA ||
34963507
data === SUSPENSE_PENDING_START_DATA ||
3508+
data === SUSPENSE_QUEUED_START_DATA ||
34973509
data === ACTIVITY_START_DATA
34983510
) {
34993511
depth++;
@@ -3535,6 +3547,7 @@ export function getParentHydrationBoundary(
35353547
data === SUSPENSE_START_DATA ||
35363548
data === SUSPENSE_FALLBACK_START_DATA ||
35373549
data === SUSPENSE_PENDING_START_DATA ||
3550+
data === SUSPENSE_QUEUED_START_DATA ||
35383551
data === ACTIVITY_START_DATA
35393552
) {
35403553
if (depth === 0) {

packages/react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@
77

88
// Imports are resolved statically by the closure compiler in release bundles
99
// and by rollup in jest unit tests
10-
import {
11-
clientRenderBoundary,
12-
completeBoundaryWithStyles,
13-
completeBoundary,
14-
completeSegment,
15-
} from './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
10+
import './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
1611

1712
if (document.body != null) {
1813
if (document.readyState === 'loading') {
@@ -82,7 +77,7 @@ function handleNode(node_: Node) {
8277
const node = (node_: HTMLElement);
8378
const dataset = node.dataset;
8479
if (dataset['rxi'] != null) {
85-
clientRenderBoundary(
80+
window['$RX'](
8681
dataset['bid'],
8782
dataset['dgst'],
8883
dataset['msg'],
@@ -92,17 +87,13 @@ function handleNode(node_: Node) {
9287
node.remove();
9388
} else if (dataset['rri'] != null) {
9489
// Convert styles here, since its type is Array<Array<string>>
95-
completeBoundaryWithStyles(
96-
dataset['bid'],
97-
dataset['sid'],
98-
JSON.parse(dataset['sty']),
99-
);
90+
window['$RR'](dataset['bid'], dataset['sid'], JSON.parse(dataset['sty']));
10091
node.remove();
10192
} else if (dataset['rci'] != null) {
102-
completeBoundary(dataset['bid'], dataset['sid']);
93+
window['$RC'](dataset['bid'], dataset['sid']);
10394
node.remove();
10495
} else if (dataset['rsi'] != null) {
105-
completeSegment(dataset['sid'], dataset['pid']);
96+
window['$RS'](dataset['sid'], dataset['pid']);
10697
node.remove();
10798
}
10899
}

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import {
8181
completeBoundaryWithStyles as styleInsertionFunction,
8282
completeSegment as completeSegmentFunction,
8383
formReplaying as formReplayingRuntime,
84+
markShellTime,
8485
} from './fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings';
8586

8687
import {getValueDescriptorExpectingObjectForWarning} from '../shared/ReactDOMResourceValidation';
@@ -120,13 +121,14 @@ const ScriptStreamingFormat: StreamingFormat = 0;
120121
const DataStreamingFormat: StreamingFormat = 1;
121122

122123
export type InstructionState = number;
123-
const NothingSent /* */ = 0b000000;
124-
const SentCompleteSegmentFunction /* */ = 0b000001;
125-
const SentCompleteBoundaryFunction /* */ = 0b000010;
126-
const SentClientRenderFunction /* */ = 0b000100;
127-
const SentStyleInsertionFunction /* */ = 0b001000;
128-
const SentFormReplayingRuntime /* */ = 0b010000;
129-
const SentCompletedShellId /* */ = 0b100000;
124+
const NothingSent /* */ = 0b0000000;
125+
const SentCompleteSegmentFunction /* */ = 0b0000001;
126+
const SentCompleteBoundaryFunction /* */ = 0b0000010;
127+
const SentClientRenderFunction /* */ = 0b0000100;
128+
const SentStyleInsertionFunction /* */ = 0b0001000;
129+
const SentFormReplayingRuntime /* */ = 0b0010000;
130+
const SentCompletedShellId /* */ = 0b0100000;
131+
const SentMarkShellTime /* */ = 0b1000000;
130132

131133
// Per request, global state that is not contextual to the rendering subtree.
132134
// This cannot be resumed and therefore should only contain things that are
@@ -4107,21 +4109,53 @@ function writeBootstrap(
41074109
return true;
41084110
}
41094111

4112+
const shellTimeRuntimeScript = stringToPrecomputedChunk(markShellTime);
4113+
4114+
function writeShellTimeInstruction(
4115+
destination: Destination,
4116+
resumableState: ResumableState,
4117+
renderState: RenderState,
4118+
): boolean {
4119+
if (
4120+
enableFizzExternalRuntime &&
4121+
resumableState.streamingFormat !== ScriptStreamingFormat
4122+
) {
4123+
// External runtime always tracks the shell time in the runtime.
4124+
return true;
4125+
}
4126+
if ((resumableState.instructions & SentMarkShellTime) !== NothingSent) {
4127+
// We already sent this instruction.
4128+
return true;
4129+
}
4130+
resumableState.instructions |= SentMarkShellTime;
4131+
writeChunk(destination, renderState.startInlineScript);
4132+
writeCompletedShellIdAttribute(destination, resumableState);
4133+
writeChunk(destination, endOfStartTag);
4134+
writeChunk(destination, shellTimeRuntimeScript);
4135+
return writeChunkAndReturn(destination, endInlineScript);
4136+
}
4137+
41104138
export function writeCompletedRoot(
41114139
destination: Destination,
41124140
resumableState: ResumableState,
41134141
renderState: RenderState,
4142+
isComplete: boolean,
41144143
): boolean {
4144+
if (!isComplete) {
4145+
// If we're not already fully complete, we might complete another boundary. If so,
4146+
// we need to track the paint time of the shell so we know how much to throttle the reveal.
4147+
writeShellTimeInstruction(destination, resumableState, renderState);
4148+
}
41154149
const preamble = renderState.preamble;
41164150
if (preamble.htmlChunks || preamble.headChunks) {
41174151
// If we rendered the whole document, then we emitted a rel="expect" that needs a
41184152
// matching target. Normally we use one of the bootstrap scripts for this but if
41194153
// there are none, then we need to emit a tag to complete the shell.
41204154
if ((resumableState.instructions & SentCompletedShellId) === NothingSent) {
4121-
const bootstrapChunks = renderState.bootstrapChunks;
4122-
bootstrapChunks.push(startChunkForTag('template'));
4123-
pushCompletedShellIdAttribute(bootstrapChunks, resumableState);
4124-
bootstrapChunks.push(endOfStartTag, endChunkForTag('template'));
4155+
writeChunk(destination, startChunkForTag('template'));
4156+
writeCompletedShellIdAttribute(destination, resumableState);
4157+
writeChunk(destination, endOfStartTag);
4158+
writeChunk(destination, endChunkForTag('template'));
41254159
}
41264160
}
41274161
return writeBootstrap(destination, renderState);
@@ -4482,14 +4516,14 @@ export function writeCompletedSegmentInstruction(
44824516
}
44834517
}
44844518

4519+
const completeBoundaryScriptFunctionOnly = stringToPrecomputedChunk(
4520+
completeBoundaryFunction,
4521+
);
44854522
const completeBoundaryScript1Full = stringToPrecomputedChunk(
44864523
completeBoundaryFunction + '$RC("',
44874524
);
44884525
const completeBoundaryScript1Partial = stringToPrecomputedChunk('$RC("');
44894526

4490-
const completeBoundaryWithStylesScript1FullBoth = stringToPrecomputedChunk(
4491-
completeBoundaryFunction + styleInsertionFunction + '$RR("',
4492-
);
44934527
const completeBoundaryWithStylesScript1FullPartial = stringToPrecomputedChunk(
44944528
styleInsertionFunction + '$RR("',
44954529
);
@@ -4531,19 +4565,27 @@ export function writeCompletedBoundaryInstruction(
45314565
writeChunk(destination, renderState.startInlineScript);
45324566
writeChunk(destination, endOfStartTag);
45334567
if (requiresStyleInsertion) {
4568+
if (
4569+
(resumableState.instructions & SentClientRenderFunction) ===
4570+
NothingSent
4571+
) {
4572+
// The completeBoundaryWithStyles function depends on the client render function.
4573+
resumableState.instructions |= SentClientRenderFunction;
4574+
writeChunk(destination, clientRenderScriptFunctionOnly);
4575+
}
45344576
if (
45354577
(resumableState.instructions & SentCompleteBoundaryFunction) ===
45364578
NothingSent
45374579
) {
4538-
resumableState.instructions |=
4539-
SentStyleInsertionFunction | SentCompleteBoundaryFunction;
4540-
writeChunk(destination, completeBoundaryWithStylesScript1FullBoth);
4541-
} else if (
4580+
// The completeBoundaryWithStyles function depends on the complete boundary function.
4581+
resumableState.instructions |= SentCompleteBoundaryFunction;
4582+
writeChunk(destination, completeBoundaryScriptFunctionOnly);
4583+
}
4584+
if (
45424585
(resumableState.instructions & SentStyleInsertionFunction) ===
45434586
NothingSent
45444587
) {
45454588
resumableState.instructions |= SentStyleInsertionFunction;
4546-
45474589
writeChunk(destination, completeBoundaryWithStylesScript1FullPartial);
45484590
} else {
45494591
writeChunk(destination, completeBoundaryWithStylesScript1Partial);
@@ -4608,6 +4650,9 @@ export function writeCompletedBoundaryInstruction(
46084650
return writeBootstrap(destination, renderState) && writeMore;
46094651
}
46104652

4653+
const clientRenderScriptFunctionOnly =
4654+
stringToPrecomputedChunk(clientRenderFunction);
4655+
46114656
const clientRenderScript1Full = stringToPrecomputedChunk(
46124657
clientRenderFunction + ';$RX("',
46134658
);
@@ -5004,6 +5049,21 @@ function writeBlockingRenderInstruction(
50045049

50055050
const completedShellIdAttributeStart = stringToPrecomputedChunk(' id="');
50065051

5052+
function writeCompletedShellIdAttribute(
5053+
destination: Destination,
5054+
resumableState: ResumableState,
5055+
): void {
5056+
if ((resumableState.instructions & SentCompletedShellId) !== NothingSent) {
5057+
return;
5058+
}
5059+
resumableState.instructions |= SentCompletedShellId;
5060+
const idPrefix = resumableState.idPrefix;
5061+
const shellId = '\u00AB' + idPrefix + 'R\u00BB';
5062+
writeChunk(destination, completedShellIdAttributeStart);
5063+
writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
5064+
writeChunk(destination, attributeEnd);
5065+
}
5066+
50075067
function pushCompletedShellIdAttribute(
50085068
target: Array<Chunk | PrecomputedChunk>,
50095069
resumableState: ResumableState,
@@ -5029,15 +5089,10 @@ export function writePreambleStart(
50295089
destination: Destination,
50305090
resumableState: ResumableState,
50315091
renderState: RenderState,
5032-
willFlushAllSegments: boolean,
50335092
skipExpect?: boolean, // Used as an override by ReactFizzConfigMarkup
50345093
): void {
50355094
// This function must be called exactly once on every request
5036-
if (
5037-
enableFizzExternalRuntime &&
5038-
!willFlushAllSegments &&
5039-
renderState.externalRuntimeScript
5040-
) {
5095+
if (enableFizzExternalRuntime && renderState.externalRuntimeScript) {
50415096
// If the root segment is incomplete due to suspended tasks
50425097
// (e.g. willFlushAllSegments = false) and we are using data
50435098
// streaming format, ensure the external runtime is sent.

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineClientRenderBoundary.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {clientRenderBoundary} from './ReactDOMFizzInstructionSetInlineSource';
1+
import {clientRenderBoundary} from './ReactDOMFizzInstructionSetShared';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import {completeBoundary} from './ReactDOMFizzInstructionSetInlineSource';
1+
import {completeBoundary} from './ReactDOMFizzInstructionSetShared';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation
5+
window['$RB'] = [];
6+
// eslint-disable-next-line dot-notation
57
window['$RC'] = completeBoundary;

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteBoundaryWithStyles.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {completeBoundaryWithStyles} from './ReactDOMFizzInstructionSetInlineSource';
1+
import {completeBoundaryWithStyles} from './ReactDOMFizzInstructionSetShared';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteSegment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {completeSegment} from './ReactDOMFizzInstructionSetInlineSource';
1+
import {completeSegment} from './ReactDOMFizzInstructionSetShared';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Track the paint time of the shell
2+
requestAnimationFrame(() => {
3+
// eslint-disable-next-line dot-notation
4+
window['$RT'] = performance.now();
5+
});

0 commit comments

Comments
 (0)