Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit dbe3363

Browse files
authoredJun 14, 2021
[Fizz] Implement Legacy renderToString and renderToNodeStream on top of Fizz (#21276)
* Wire up DOM legacy build * Hack to filter extra comments for testing purposes * Use string concat in renderToString I think this might be faster. We could probably use a combination of this technique in the stream too to lower the overhead. * Error if we can't complete the root synchronously Maybe this should always error but in the async forms we can just delay the stream until it resolves so it does have some useful semantics. In the synchronous form it's never useful though. I'm mostly adding the error because we're testing this behavior for renderToString specifically. * Gate memory leak tests of internals These tests don't translate as is to the new implementation and have been ported to the Fizz tests separately. * Enable Fizz legacy mode in stable * Add wrapper around the ServerFormatConfig for legacy mode This ensures that we can inject custom overrides without negatively affecting the new implementation. This adds another field for static mark up for example. * Wrap pushTextInstance to avoid emitting comments for text in static markup * Don't emit static mark up for completed suspense boundaries Completed and client rendered boundaries are only marked for the client to take over. Pending boundaries are still supported in case you stream non-hydratable mark up. * Wire up generateStaticMarkup to static API entry points * Mark as renderer for stable This shouldn't affect the FB one ideally but it's done with the same build so let's hope this works.
1 parent 101ea9f commit dbe3363

25 files changed

+680
-40
lines changed
 
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export * from 'react-client/src/ReactFlightClientHostConfigBrowser';
11+
export * from 'react-client/src/ReactFlightClientHostConfigStream';
12+
export * from 'react-server-dom-webpack/src/ReactFlightClientWebpackBundlerConfig';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export {
11+
renderToString,
12+
renderToStaticMarkup,
13+
renderToNodeStream,
14+
renderToStaticNodeStream,
15+
version,
16+
} from './src/server/ReactDOMServerBrowser';

‎packages/react-dom/server.browser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ export {
1313
renderToNodeStream,
1414
renderToStaticNodeStream,
1515
version,
16-
} from './src/server/ReactDOMServerBrowser';
16+
} from './src/server/ReactDOMLegacyServerBrowser';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export {
11+
renderToString,
12+
renderToStaticMarkup,
13+
renderToNodeStream,
14+
renderToStaticNodeStream,
15+
version,
16+
} from './src/server/ReactDOMServerBrowser';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
// For some reason Flow doesn't like export * in this file. I don't know why.
11+
export {
12+
renderToString,
13+
renderToStaticMarkup,
14+
renderToNodeStream,
15+
renderToStaticNodeStream,
16+
version,
17+
} from './src/server/ReactDOMServerNode';

‎packages/react-dom/server.node.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ export {
1414
renderToNodeStream,
1515
renderToStaticNodeStream,
1616
version,
17-
} from './src/server/ReactDOMServerNode';
17+
} from './src/server/ReactDOMLegacyServerNode';

‎packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ describe('ReactDOMServerIntegration', () => {
487487
});
488488

489489
// Regression test for https://github.com/facebook/react/issues/14705
490+
// @gate !experimental && www
490491
it('does not pollute later renders when stream destroyed', () => {
491492
const LoggedInUser = React.createContext('default');
492493

@@ -529,6 +530,7 @@ describe('ReactDOMServerIntegration', () => {
529530
});
530531

531532
// Regression test for https://github.com/facebook/react/issues/14705
533+
// @gate !experimental && www
532534
it('frees context value reference when stream destroyed', () => {
533535
const LoggedInUser = React.createContext('default');
534536

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import ReactVersion from 'shared/ReactVersion';
11+
import invariant from 'shared/invariant';
12+
13+
import type {ReactNodeList} from 'shared/ReactTypes';
14+
15+
import {
16+
createRequest,
17+
startWork,
18+
startFlowing,
19+
abort,
20+
} from 'react-server/src/ReactFizzServer';
21+
22+
import {
23+
createResponseState,
24+
createRootFormatContext,
25+
} from './ReactDOMServerLegacyFormatConfig';
26+
27+
type ServerOptions = {
28+
identifierPrefix?: string,
29+
};
30+
31+
function onError() {
32+
// Non-fatal errors are ignored.
33+
}
34+
35+
function renderToStringImpl(
36+
children: ReactNodeList,
37+
options: void | ServerOptions,
38+
generateStaticMarkup: boolean,
39+
): string {
40+
let didFatal = false;
41+
let fatalError = null;
42+
let result = '';
43+
const destination = {
44+
push(chunk) {
45+
if (chunk !== null) {
46+
result += chunk;
47+
}
48+
return true;
49+
},
50+
destroy(error) {
51+
didFatal = true;
52+
fatalError = error;
53+
},
54+
};
55+
56+
let readyToStream = false;
57+
function onReadyToStream() {
58+
readyToStream = true;
59+
}
60+
const request = createRequest(
61+
children,
62+
destination,
63+
createResponseState(
64+
generateStaticMarkup,
65+
options ? options.identifierPrefix : undefined,
66+
),
67+
createRootFormatContext(undefined),
68+
Infinity,
69+
onError,
70+
undefined,
71+
onReadyToStream,
72+
);
73+
startWork(request);
74+
// If anything suspended and is still pending, we'll abort it before writing.
75+
// That way we write only client-rendered boundaries from the start.
76+
abort(request);
77+
startFlowing(request);
78+
if (didFatal) {
79+
throw fatalError;
80+
}
81+
invariant(
82+
readyToStream,
83+
'A React component suspended while rendering, but no fallback UI was specified.\n' +
84+
'\n' +
85+
'Add a <Suspense fallback=...> component higher in the tree to ' +
86+
'provide a loading indicator or placeholder to display.',
87+
);
88+
return result;
89+
}
90+
91+
function renderToString(
92+
children: ReactNodeList,
93+
options?: ServerOptions,
94+
): string {
95+
return renderToStringImpl(children, options, false);
96+
}
97+
98+
function renderToStaticMarkup(
99+
children: ReactNodeList,
100+
options?: ServerOptions,
101+
): string {
102+
return renderToStringImpl(children, options, true);
103+
}
104+
105+
function renderToNodeStream() {
106+
invariant(
107+
false,
108+
'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
109+
'in the browser. Use ReactDOMServer.renderToString() instead.',
110+
);
111+
}
112+
113+
function renderToStaticNodeStream() {
114+
invariant(
115+
false,
116+
'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
117+
'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
118+
);
119+
}
120+
121+
export {
122+
renderToString,
123+
renderToStaticMarkup,
124+
renderToNodeStream,
125+
renderToStaticNodeStream,
126+
ReactVersion as version,
127+
};
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import type {ReactNodeList} from 'shared/ReactTypes';
11+
12+
import type {Request} from 'react-server/src/ReactFizzServer';
13+
14+
import {
15+
createRequest,
16+
startWork,
17+
startFlowing,
18+
abort,
19+
} from 'react-server/src/ReactFizzServer';
20+
21+
import {
22+
createResponseState,
23+
createRootFormatContext,
24+
} from './ReactDOMServerLegacyFormatConfig';
25+
26+
import {
27+
version,
28+
renderToString,
29+
renderToStaticMarkup,
30+
} from './ReactDOMLegacyServerBrowser';
31+
32+
import {Readable} from 'stream';
33+
34+
type ServerOptions = {
35+
identifierPrefix?: string,
36+
};
37+
38+
class ReactMarkupReadableStream extends Readable {
39+
request: Request;
40+
startedFlowing: boolean;
41+
constructor() {
42+
// Calls the stream.Readable(options) constructor. Consider exposing built-in
43+
// features like highWaterMark in the future.
44+
super({});
45+
this.request = (null: any);
46+
this.startedFlowing = false;
47+
}
48+
49+
_destroy(err, callback) {
50+
abort(this.request);
51+
// $FlowFixMe: The type definition for the callback should allow undefined and null.
52+
callback(err);
53+
}
54+
55+
_read(size) {
56+
if (this.startedFlowing) {
57+
startFlowing(this.request);
58+
}
59+
}
60+
}
61+
62+
function onError() {
63+
// Non-fatal errors are ignored.
64+
}
65+
66+
function renderToNodeStreamImpl(
67+
children: ReactNodeList,
68+
options: void | ServerOptions,
69+
generateStaticMarkup: boolean,
70+
): Readable {
71+
function onCompleteAll() {
72+
// We wait until everything has loaded before starting to write.
73+
// That way we only end up with fully resolved HTML even if we suspend.
74+
destination.startedFlowing = true;
75+
startFlowing(request);
76+
}
77+
const destination = new ReactMarkupReadableStream();
78+
const request = createRequest(
79+
children,
80+
destination,
81+
createResponseState(false, options ? options.identifierPrefix : undefined),
82+
createRootFormatContext(undefined),
83+
Infinity,
84+
onError,
85+
onCompleteAll,
86+
undefined,
87+
);
88+
destination.request = request;
89+
startWork(request);
90+
return destination;
91+
}
92+
93+
function renderToNodeStream(
94+
children: ReactNodeList,
95+
options?: ServerOptions,
96+
): Readable {
97+
return renderToNodeStreamImpl(children, options, false);
98+
}
99+
100+
function renderToStaticNodeStream(
101+
children: ReactNodeList,
102+
options?: ServerOptions,
103+
): Readable {
104+
return renderToNodeStreamImpl(children, options, true);
105+
}
106+
107+
export {
108+
renderToString,
109+
renderToStaticMarkup,
110+
renderToNodeStream,
111+
renderToStaticNodeStream,
112+
version,
113+
};
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export type Destination = {
11+
push(chunk: string | null): boolean,
12+
destroy(error: Error): mixed,
13+
...
14+
};
15+
16+
export type PrecomputedChunk = string;
17+
export type Chunk = string;
18+
19+
export function scheduleWork(callback: () => void) {
20+
callback();
21+
}
22+
23+
export function flushBuffered(destination: Destination) {}
24+
25+
export function beginWriting(destination: Destination) {}
26+
27+
let prevWasCommentSegmenter = false;
28+
export function writeChunk(
29+
destination: Destination,
30+
chunk: Chunk | PrecomputedChunk,
31+
): boolean {
32+
if (prevWasCommentSegmenter) {
33+
prevWasCommentSegmenter = false;
34+
if (chunk[0] !== '<') {
35+
destination.push('<!-- -->');
36+
}
37+
}
38+
if (chunk === '<!-- -->') {
39+
prevWasCommentSegmenter = true;
40+
return true;
41+
}
42+
return destination.push(chunk);
43+
}
44+
45+
export function completeWriting(destination: Destination) {}
46+
47+
export function close(destination: Destination) {
48+
destination.push(null);
49+
}
50+
51+
export function stringToChunk(content: string): Chunk {
52+
return content;
53+
}
54+
55+
export function stringToPrecomputedChunk(content: string): PrecomputedChunk {
56+
return content;
57+
}
58+
59+
export function closeWithError(destination: Destination, error: mixed): void {
60+
// $FlowFixMe: This is an Error object or the destination accepts other types.
61+
destination.destroy(error);
62+
}

‎packages/react-dom/src/server/ReactDOMServerFormatConfig.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
writeChunk,
2424
stringToChunk,
2525
stringToPrecomputedChunk,
26-
isPrimaryStreamConfig,
2726
} from 'react-server/src/ReactServerStreamConfig';
2827

2928
import {
@@ -51,7 +50,7 @@ import isArray from 'shared/isArray';
5150

5251
// Used to distinguish these contexts from ones used in other renderers.
5352
// E.g. this can be used to distinguish legacy renderers from this modern one.
54-
export const isPrimaryRenderer = isPrimaryStreamConfig;
53+
export const isPrimaryRenderer = true;
5554

5655
// Per response, global state that is not contextual to the rendering subtree.
5756
export type ResponseState = {
@@ -63,18 +62,20 @@ export type ResponseState = {
6362
nextOpaqueID: number,
6463
sentCompleteSegmentFunction: boolean,
6564
sentCompleteBoundaryFunction: boolean,
66-
sentClientRenderFunction: boolean,
65+
sentClientRenderFunction: boolean, // We allow the legacy renderer to extend this object.
66+
...
6767
};
6868

6969
// Allows us to keep track of what we've already written so we can refer back to it.
7070
export function createResponseState(
71-
identifierPrefix: string = '',
71+
identifierPrefix: string | void,
7272
): ResponseState {
73+
const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
7374
return {
74-
placeholderPrefix: stringToPrecomputedChunk(identifierPrefix + 'P:'),
75-
segmentPrefix: stringToPrecomputedChunk(identifierPrefix + 'S:'),
76-
boundaryPrefix: identifierPrefix + 'B:',
77-
opaqueIdentifierPrefix: identifierPrefix + 'R:',
75+
placeholderPrefix: stringToPrecomputedChunk(idPrefix + 'P:'),
76+
segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
77+
boundaryPrefix: idPrefix + 'B:',
78+
opaqueIdentifierPrefix: idPrefix + 'R:',
7879
nextSuspenseID: 0,
7980
nextOpaqueID: 0,
8081
sentCompleteSegmentFunction: false,
@@ -1459,23 +1460,41 @@ const endSuspenseBoundary = stringToPrecomputedChunk('<!--/$-->');
14591460

14601461
export function writeStartCompletedSuspenseBoundary(
14611462
destination: Destination,
1463+
responseState: ResponseState,
14621464
id: SuspenseBoundaryID,
14631465
): boolean {
14641466
return writeChunk(destination, startCompletedSuspenseBoundary);
14651467
}
14661468
export function writeStartPendingSuspenseBoundary(
14671469
destination: Destination,
1470+
responseState: ResponseState,
14681471
id: SuspenseBoundaryID,
14691472
): boolean {
14701473
return writeChunk(destination, startPendingSuspenseBoundary);
14711474
}
14721475
export function writeStartClientRenderedSuspenseBoundary(
14731476
destination: Destination,
1477+
responseState: ResponseState,
14741478
id: SuspenseBoundaryID,
14751479
): boolean {
14761480
return writeChunk(destination, startClientRenderedSuspenseBoundary);
14771481
}
1478-
export function writeEndSuspenseBoundary(destination: Destination): boolean {
1482+
export function writeEndCompletedSuspenseBoundary(
1483+
destination: Destination,
1484+
responseState: ResponseState,
1485+
): boolean {
1486+
return writeChunk(destination, endSuspenseBoundary);
1487+
}
1488+
export function writeEndPendingSuspenseBoundary(
1489+
destination: Destination,
1490+
responseState: ResponseState,
1491+
): boolean {
1492+
return writeChunk(destination, endSuspenseBoundary);
1493+
}
1494+
export function writeEndClientRenderedSuspenseBoundary(
1495+
destination: Destination,
1496+
responseState: ResponseState,
1497+
): boolean {
14791498
return writeChunk(destination, endSuspenseBoundary);
14801499
}
14811500

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import type {SuspenseBoundaryID} from './ReactDOMServerFormatConfig';
11+
12+
import {
13+
createResponseState as createResponseStateImpl,
14+
pushTextInstance as pushTextInstanceImpl,
15+
writeStartCompletedSuspenseBoundary as writeStartCompletedSuspenseBoundaryImpl,
16+
writeStartClientRenderedSuspenseBoundary as writeStartClientRenderedSuspenseBoundaryImpl,
17+
writeEndCompletedSuspenseBoundary as writeEndCompletedSuspenseBoundaryImpl,
18+
writeEndClientRenderedSuspenseBoundary as writeEndClientRenderedSuspenseBoundaryImpl,
19+
} from './ReactDOMServerFormatConfig';
20+
21+
import type {
22+
Destination,
23+
Chunk,
24+
PrecomputedChunk,
25+
} from 'react-server/src/ReactServerStreamConfig';
26+
27+
export const isPrimaryRenderer = false;
28+
29+
export type ResponseState = {
30+
// Keep this in sync with ReactDOMServerFormatConfig
31+
placeholderPrefix: PrecomputedChunk,
32+
segmentPrefix: PrecomputedChunk,
33+
boundaryPrefix: string,
34+
opaqueIdentifierPrefix: string,
35+
nextSuspenseID: number,
36+
nextOpaqueID: number,
37+
sentCompleteSegmentFunction: boolean,
38+
sentCompleteBoundaryFunction: boolean,
39+
sentClientRenderFunction: boolean,
40+
// This is an extra field for the legacy renderer
41+
generateStaticMarkup: boolean,
42+
};
43+
44+
export function createResponseState(
45+
generateStaticMarkup: boolean,
46+
identifierPrefix: string | void,
47+
): ResponseState {
48+
const responseState = createResponseStateImpl(identifierPrefix);
49+
return {
50+
// Keep this in sync with ReactDOMServerFormatConfig
51+
placeholderPrefix: responseState.placeholderPrefix,
52+
segmentPrefix: responseState.segmentPrefix,
53+
boundaryPrefix: responseState.boundaryPrefix,
54+
opaqueIdentifierPrefix: responseState.opaqueIdentifierPrefix,
55+
nextSuspenseID: responseState.nextSuspenseID,
56+
nextOpaqueID: responseState.nextOpaqueID,
57+
sentCompleteSegmentFunction: responseState.sentCompleteSegmentFunction,
58+
sentCompleteBoundaryFunction: responseState.sentCompleteBoundaryFunction,
59+
sentClientRenderFunction: responseState.sentClientRenderFunction,
60+
// This is an extra field for the legacy renderer
61+
generateStaticMarkup,
62+
};
63+
}
64+
65+
export type {
66+
FormatContext,
67+
SuspenseBoundaryID,
68+
OpaqueIDType,
69+
} from './ReactDOMServerFormatConfig';
70+
71+
export {
72+
createRootFormatContext,
73+
getChildFormatContext,
74+
createSuspenseBoundaryID,
75+
makeServerID,
76+
pushEmpty,
77+
pushStartInstance,
78+
pushEndInstance,
79+
writeStartSegment,
80+
writeEndSegment,
81+
writeCompletedSegmentInstruction,
82+
writeCompletedBoundaryInstruction,
83+
writeClientRenderBoundaryInstruction,
84+
writeStartPendingSuspenseBoundary,
85+
writeEndPendingSuspenseBoundary,
86+
writePlaceholder,
87+
} from './ReactDOMServerFormatConfig';
88+
89+
import {stringToChunk} from 'react-server/src/ReactServerStreamConfig';
90+
91+
import escapeTextForBrowser from './escapeTextForBrowser';
92+
93+
export function pushTextInstance(
94+
target: Array<Chunk | PrecomputedChunk>,
95+
text: string,
96+
responseState: ResponseState,
97+
assignID: null | SuspenseBoundaryID,
98+
): void {
99+
if (responseState.generateStaticMarkup) {
100+
target.push(stringToChunk(escapeTextForBrowser(text)));
101+
} else {
102+
pushTextInstanceImpl(target, text, responseState, assignID);
103+
}
104+
}
105+
106+
export function writeStartCompletedSuspenseBoundary(
107+
destination: Destination,
108+
responseState: ResponseState,
109+
id: SuspenseBoundaryID,
110+
): boolean {
111+
if (responseState.generateStaticMarkup) {
112+
// A completed boundary is done and doesn't need a representation in the HTML
113+
// if we're not going to be hydrating it.
114+
return true;
115+
}
116+
return writeStartCompletedSuspenseBoundaryImpl(
117+
destination,
118+
responseState,
119+
id,
120+
);
121+
}
122+
export function writeStartClientRenderedSuspenseBoundary(
123+
destination: Destination,
124+
responseState: ResponseState,
125+
id: SuspenseBoundaryID,
126+
): boolean {
127+
if (responseState.generateStaticMarkup) {
128+
// A client rendered boundary is done and doesn't need a representation in the HTML
129+
// since we'll never hydrate it. This is arguably an error in static generation.
130+
return true;
131+
}
132+
return writeStartClientRenderedSuspenseBoundaryImpl(
133+
destination,
134+
responseState,
135+
id,
136+
);
137+
}
138+
export function writeEndCompletedSuspenseBoundary(
139+
destination: Destination,
140+
responseState: ResponseState,
141+
): boolean {
142+
if (responseState.generateStaticMarkup) {
143+
return true;
144+
}
145+
return writeEndCompletedSuspenseBoundaryImpl(destination, responseState);
146+
}
147+
export function writeEndClientRenderedSuspenseBoundary(
148+
destination: Destination,
149+
responseState: ResponseState,
150+
): boolean {
151+
if (responseState.generateStaticMarkup) {
152+
return true;
153+
}
154+
return writeEndClientRenderedSuspenseBoundaryImpl(destination, responseState);
155+
}

‎packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,26 +203,44 @@ export function writePlaceholder(
203203
// Suspense boundaries are encoded as comments.
204204
export function writeStartCompletedSuspenseBoundary(
205205
destination: Destination,
206+
responseState: ResponseState,
206207
id: SuspenseBoundaryID,
207208
): boolean {
208209
writeChunk(destination, SUSPENSE_COMPLETE);
209210
return writeChunk(destination, formatID(id));
210211
}
211212
export function writeStartPendingSuspenseBoundary(
212213
destination: Destination,
214+
responseState: ResponseState,
213215
id: SuspenseBoundaryID,
214216
): boolean {
215217
writeChunk(destination, SUSPENSE_PENDING);
216218
return writeChunk(destination, formatID(id));
217219
}
218220
export function writeStartClientRenderedSuspenseBoundary(
219221
destination: Destination,
222+
responseState: ResponseState,
220223
id: SuspenseBoundaryID,
221224
): boolean {
222225
writeChunk(destination, SUSPENSE_CLIENT_RENDER);
223226
return writeChunk(destination, formatID(id));
224227
}
225-
export function writeEndSuspenseBoundary(destination: Destination): boolean {
228+
export function writeEndCompletedSuspenseBoundary(
229+
destination: Destination,
230+
responseState: ResponseState,
231+
): boolean {
232+
return writeChunk(destination, END);
233+
}
234+
export function writeEndPendingSuspenseBoundary(
235+
destination: Destination,
236+
responseState: ResponseState,
237+
): boolean {
238+
return writeChunk(destination, END);
239+
}
240+
export function writeEndClientRenderedSuspenseBoundary(
241+
destination: Destination,
242+
responseState: ResponseState,
243+
): boolean {
226244
return writeChunk(destination, END);
227245
}
228246

‎packages/react-noop-renderer/src/ReactNoopServer.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ const ReactNoopServer = ReactFizzServer({
138138

139139
writeStartCompletedSuspenseBoundary(
140140
destination: Destination,
141+
responseState: ResponseState,
141142
suspenseInstance: SuspenseInstance,
142143
): boolean {
143144
suspenseInstance.state = 'complete';
@@ -147,6 +148,7 @@ const ReactNoopServer = ReactFizzServer({
147148
},
148149
writeStartPendingSuspenseBoundary(
149150
destination: Destination,
151+
responseState: ResponseState,
150152
suspenseInstance: SuspenseInstance,
151153
): boolean {
152154
suspenseInstance.state = 'pending';
@@ -156,14 +158,21 @@ const ReactNoopServer = ReactFizzServer({
156158
},
157159
writeStartClientRenderedSuspenseBoundary(
158160
destination: Destination,
161+
responseState: ResponseState,
159162
suspenseInstance: SuspenseInstance,
160163
): boolean {
161164
suspenseInstance.state = 'client-render';
162165
const parent = destination.stack[destination.stack.length - 1];
163166
parent.children.push(suspenseInstance);
164167
destination.stack.push(suspenseInstance);
165168
},
166-
writeEndSuspenseBoundary(destination: Destination): boolean {
169+
writeEndCompletedSuspenseBoundary(destination: Destination): boolean {
170+
destination.stack.pop();
171+
},
172+
writeEndPendingSuspenseBoundary(destination: Destination): boolean {
173+
destination.stack.pop();
174+
},
175+
writeEndClientRenderedSuspenseBoundary(destination: Destination): boolean {
167176
destination.stack.pop();
168177
},
169178

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export * from 'react-dom/src/client/ReactDOMHostConfig';

‎packages/react-server-dom-relay/src/ReactServerStreamConfigFB.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ export type Destination = {
1717
export type PrecomputedChunk = string;
1818
export type Chunk = string;
1919

20-
export const isPrimaryStreamConfig = true;
21-
2220
export function scheduleWork(callback: () => void) {
2321
// We don't schedule work in this model, and instead expect performWork to always be called repeatedly.
2422
}

‎packages/react-server/src/ReactFizzServer.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ import {
4040
writeStartCompletedSuspenseBoundary,
4141
writeStartPendingSuspenseBoundary,
4242
writeStartClientRenderedSuspenseBoundary,
43-
writeEndSuspenseBoundary,
43+
writeEndCompletedSuspenseBoundary,
44+
writeEndPendingSuspenseBoundary,
45+
writeEndClientRenderedSuspenseBoundary,
4446
writeStartSegment,
4547
writeEndSegment,
4648
writeClientRenderBoundaryInstruction,
@@ -1609,12 +1611,19 @@ function flushSegment(
16091611
// Emit a client rendered suspense boundary wrapper.
16101612
// We never queue the inner boundary so we'll never emit its content or partial segments.
16111613

1612-
writeStartClientRenderedSuspenseBoundary(destination, boundary.id);
1614+
writeStartClientRenderedSuspenseBoundary(
1615+
destination,
1616+
request.responseState,
1617+
boundary.id,
1618+
);
16131619

16141620
// Flush the fallback.
16151621
flushSubtree(request, destination, segment);
16161622

1617-
return writeEndSuspenseBoundary(destination);
1623+
return writeEndClientRenderedSuspenseBoundary(
1624+
destination,
1625+
request.responseState,
1626+
);
16181627
} else if (boundary.pendingTasks > 0) {
16191628
// This boundary is still loading. Emit a pending suspense boundary wrapper.
16201629

@@ -1625,12 +1634,16 @@ function flushSegment(
16251634
request.partialBoundaries.push(boundary);
16261635
}
16271636

1628-
writeStartPendingSuspenseBoundary(destination, boundary.id);
1637+
writeStartPendingSuspenseBoundary(
1638+
destination,
1639+
request.responseState,
1640+
boundary.id,
1641+
);
16291642

16301643
// Flush the fallback.
16311644
flushSubtree(request, destination, segment);
16321645

1633-
return writeEndSuspenseBoundary(destination);
1646+
return writeEndPendingSuspenseBoundary(destination, request.responseState);
16341647
} else if (boundary.byteSize > request.progressiveChunkSize) {
16351648
// This boundary is large and will be emitted separately so that we can progressively show
16361649
// other content. We add it to the queue during the flush because we have to ensure that
@@ -1643,16 +1656,24 @@ function flushSegment(
16431656

16441657
request.completedBoundaries.push(boundary);
16451658
// Emit a pending rendered suspense boundary wrapper.
1646-
writeStartPendingSuspenseBoundary(destination, boundary.id);
1659+
writeStartPendingSuspenseBoundary(
1660+
destination,
1661+
request.responseState,
1662+
boundary.id,
1663+
);
16471664

16481665
// Flush the fallback.
16491666
flushSubtree(request, destination, segment);
16501667

1651-
return writeEndSuspenseBoundary(destination);
1668+
return writeEndPendingSuspenseBoundary(destination, request.responseState);
16521669
} else {
16531670
// We can inline this boundary's content as a complete boundary.
16541671

1655-
writeStartCompletedSuspenseBoundary(destination, boundary.id);
1672+
writeStartCompletedSuspenseBoundary(
1673+
destination,
1674+
request.responseState,
1675+
boundary.id,
1676+
);
16561677

16571678
const completedSegments = boundary.completedSegments;
16581679
invariant(
@@ -1662,7 +1683,10 @@ function flushSegment(
16621683
const contentSegment = completedSegments[0];
16631684
flushSegment(request, destination, contentSegment);
16641685

1665-
return writeEndSuspenseBoundary(destination);
1686+
return writeEndCompletedSuspenseBoundary(
1687+
destination,
1688+
request.responseState,
1689+
);
16661690
}
16671691
}
16681692

‎packages/react-server/src/ReactServerStreamConfigBrowser.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ export type Destination = ReadableStreamController;
1212
export type PrecomputedChunk = Uint8Array;
1313
export type Chunk = Uint8Array;
1414

15-
export const isPrimaryStreamConfig = true;
16-
1715
export function scheduleWork(callback: () => void) {
1816
callback();
1917
}

‎packages/react-server/src/ReactServerStreamConfigNode.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export type Destination = Writable & MightBeFlushable;
1919
export type PrecomputedChunk = Uint8Array;
2020
export type Chunk = string;
2121

22-
export const isPrimaryStreamConfig = true;
23-
2422
export function scheduleWork(callback: () => void) {
2523
setImmediate(callback);
2624
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export * from '../ReactFlightServerConfigStream';
11+
export * from 'react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig';

‎packages/react-server/src/forks/ReactServerFormatConfig.custom.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ export const writeStartPendingSuspenseBoundary =
4646
$$$hostConfig.writeStartPendingSuspenseBoundary;
4747
export const writeStartClientRenderedSuspenseBoundary =
4848
$$$hostConfig.writeStartClientRenderedSuspenseBoundary;
49-
export const writeEndSuspenseBoundary = $$$hostConfig.writeEndSuspenseBoundary;
49+
export const writeEndCompletedSuspenseBoundary =
50+
$$$hostConfig.writeEndCompletedSuspenseBoundary;
51+
export const writeEndPendingSuspenseBoundary =
52+
$$$hostConfig.writeEndPendingSuspenseBoundary;
53+
export const writeEndClientRenderedSuspenseBoundary =
54+
$$$hostConfig.writeEndClientRenderedSuspenseBoundary;
5055
export const writeStartSegment = $$$hostConfig.writeStartSegment;
5156
export const writeEndSegment = $$$hostConfig.writeEndSegment;
5257
export const writeCompletedSegmentInstruction =
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export * from 'react-dom/src/server/ReactDOMServerLegacyFormatConfig';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
export * from 'react-dom/src/server/ReactDOMLegacyServerStreamConfig';

‎scripts/rollup/bundles.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,9 @@ const moduleTypes = {
5656
RENDERER_UTILS: 'RENDERER_UTILS',
5757
// Standalone reconciler for third-party renderers.
5858
RECONCILER: 'RECONCILER',
59-
// Non-Fiber implementations like SSR and Shallow renderers.
60-
NON_FIBER_RENDERER: 'NON_FIBER_RENDERER',
6159
};
6260

63-
const {
64-
ISOMORPHIC,
65-
RENDERER,
66-
RENDERER_UTILS,
67-
RECONCILER,
68-
NON_FIBER_RENDERER,
69-
} = moduleTypes;
61+
const {ISOMORPHIC, RENDERER, RENDERER_UTILS, RECONCILER} = moduleTypes;
7062

7163
const bundles = [
7264
/******* Isomorphic *******/
@@ -241,7 +233,7 @@ const bundles = [
241233
bundleTypes: __EXPERIMENTAL__
242234
? [UMD_DEV, UMD_PROD, NODE_DEV, NODE_PROD]
243235
: [UMD_DEV, UMD_PROD, NODE_DEV, NODE_PROD, FB_WWW_DEV, FB_WWW_PROD],
244-
moduleType: NON_FIBER_RENDERER,
236+
moduleType: RENDERER,
245237
entry: 'react-dom/server.browser',
246238
global: 'ReactDOMServer',
247239
externals: ['react'],
@@ -254,7 +246,7 @@ const bundles = [
254246
},
255247
{
256248
bundleTypes: [NODE_DEV, NODE_PROD],
257-
moduleType: NON_FIBER_RENDERER,
249+
moduleType: RENDERER,
258250
entry: 'react-dom/server.node',
259251
externals: ['react', 'stream'],
260252
babel: opts =>

‎scripts/shared/inlinedHostConfigs.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module.exports = [
2020
'react-dom',
2121
'react-dom/unstable-fizz',
2222
'react-dom/unstable-fizz.node',
23+
'react-dom/server.node.stable',
2324
'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/unstable-fizz.node
2425
'react-server-dom-webpack',
2526
'react-server-dom-webpack/writer',
@@ -44,6 +45,7 @@ module.exports = [
4445
'react-dom',
4546
'react-dom/testing',
4647
'react-dom/unstable-fizz.browser',
48+
'react-dom/server.browser.stable',
4749
'react-dom/src/server/ReactDOMFizzServerBrowser.js', // react-dom/unstable-fizz.browser
4850
'react-server-dom-webpack',
4951
'react-server-dom-webpack/writer.browser.server',
@@ -53,6 +55,22 @@ module.exports = [
5355
isFlowTyped: true,
5456
isServerSupported: true,
5557
},
58+
{
59+
shortName: 'dom-legacy',
60+
entryPoints: ['react-dom/server.browser', 'react-dom/server.node'],
61+
paths: [
62+
'react-dom',
63+
'react-dom/server',
64+
'react-dom/server.browser',
65+
'react-dom/server.node',
66+
'react-server-dom-webpack',
67+
'react-dom/src/server/ReactDOMLegacyServerBrowser.js', // react-dom/server.browser
68+
'react-dom/src/server/ReactDOMLegacyServerNode.js', // react-dom/server.node
69+
'react-client/src/ReactFlightClientStream.js', // We can only type check this in streaming configurations.
70+
],
71+
isFlowTyped: true,
72+
isServerSupported: true,
73+
},
5674
{
5775
shortName: 'art',
5876
entryPoints: ['react-art'],

0 commit comments

Comments
 (0)
Please sign in to comment.