diff --git a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js index b06196c452a6..37ee8feae309 100644 --- a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js +++ b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js @@ -7,11 +7,16 @@ * @flow */ -import type {Destination} from 'react-server/src/ReactServerStreamConfig'; +import type { + Destination, + Chunk, + PrecomputedChunk, +} from 'react-server/src/ReactServerStreamConfig'; import { writeChunk, - convertStringToBuffer, + stringToChunk, + stringToPrecomputedChunk, } from 'react-server/src/ReactServerStreamConfig'; import escapeTextForBrowser from './escapeTextForBrowser'; @@ -55,43 +60,43 @@ function encodeHTMLTextNode(text: string): string { } export function pushTextInstance( - target: Array, + target: Array, text: string, ): void { - target.push(convertStringToBuffer(encodeHTMLTextNode(text))); + target.push(stringToChunk(encodeHTMLTextNode(text))); } -const startTag1 = convertStringToBuffer('<'); -const startTag2 = convertStringToBuffer('>'); +const startTag1 = stringToPrecomputedChunk('<'); +const startTag2 = stringToPrecomputedChunk('>'); export function pushStartInstance( - target: Array, + target: Array, type: string, props: Object, ): void { // TODO: Figure out if it's self closing and everything else. - target.push(startTag1, convertStringToBuffer(type), startTag2); + target.push(startTag1, stringToChunk(type), startTag2); } -const endTag1 = convertStringToBuffer(''); +const endTag1 = stringToPrecomputedChunk(''); export function pushEndInstance( - target: Array, + target: Array, type: string, props: Object, ): void { // TODO: Figure out if it was self closing. - target.push(endTag1, convertStringToBuffer(type), endTag2); + target.push(endTag1, stringToChunk(type), endTag2); } // Structural Nodes // A placeholder is a node inside a hidden partial tree that can be filled in later, but before // display. It's never visible to users. -const placeholder1 = convertStringToBuffer(''); +const placeholder1 = stringToPrecomputedChunk(''); export function writePlaceholder( destination: Destination, id: number, @@ -101,16 +106,18 @@ export function writePlaceholder( writeChunk(destination, placeholder1); // TODO: Use the identifierPrefix option to make the prefix configurable. writeChunk(destination, placeholder2); - const formattedID = convertStringToBuffer(id.toString(16)); + const formattedID = stringToChunk(id.toString(16)); writeChunk(destination, formattedID); return writeChunk(destination, placeholder3); } // Suspense boundaries are encoded as comments. -const startCompletedSuspenseBoundary = convertStringToBuffer(''); -const startPendingSuspenseBoundary = convertStringToBuffer(''); -const startClientRenderedSuspenseBoundary = convertStringToBuffer(''); -const endSuspenseBoundary = convertStringToBuffer(''); +const startCompletedSuspenseBoundary = stringToPrecomputedChunk(''); +const startPendingSuspenseBoundary = stringToPrecomputedChunk(''); +const startClientRenderedSuspenseBoundary = stringToPrecomputedChunk( + '', +); +const endSuspenseBoundary = stringToPrecomputedChunk(''); export function writeStartCompletedSuspenseBoundary( destination: Destination, @@ -134,10 +141,10 @@ export function writeEndSuspenseBoundary(destination: Destination): boolean { return writeChunk(destination, endSuspenseBoundary); } -const startSegment = convertStringToBuffer(''); +const startSegment = stringToPrecomputedChunk(''); export function writeStartSegment( destination: Destination, id: number, @@ -146,7 +153,7 @@ export function writeStartSegment( writeChunk(destination, startSegment); // TODO: Use the identifierPrefix option to make the prefix configurable. writeChunk(destination, startSegment2); - const formattedID = convertStringToBuffer(id.toString(16)); + const formattedID = stringToChunk(id.toString(16)); writeChunk(destination, formattedID); return writeChunk(destination, startSegment3); } @@ -276,12 +283,14 @@ const completeBoundaryFunction = const clientRenderFunction = 'function $RX(b){if(b=document.getElementById(b)){do b=b.previousSibling;while(8!==b.nodeType||"$?"!==b.data);b.data="$!";b._reactRetry&&b._reactRetry()}}'; -const completeSegmentScript1Full = convertStringToBuffer( +const completeSegmentScript1Full = stringToPrecomputedChunk( ''); +const completeSegmentScript1Partial = stringToPrecomputedChunk( + ''); export function writeCompletedSegmentInstruction( destination: Destination, @@ -297,19 +306,21 @@ export function writeCompletedSegmentInstruction( writeChunk(destination, completeSegmentScript1Partial); } // TODO: Use the identifierPrefix option to make the prefix configurable. - const formattedID = convertStringToBuffer(contentSegmentID.toString(16)); + const formattedID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, formattedID); writeChunk(destination, completeSegmentScript2); writeChunk(destination, formattedID); return writeChunk(destination, completeSegmentScript3); } -const completeBoundaryScript1Full = convertStringToBuffer( +const completeBoundaryScript1Full = stringToPrecomputedChunk( ''); +const completeBoundaryScript1Partial = stringToPrecomputedChunk( + ''); export function writeCompletedBoundaryInstruction( destination: Destination, @@ -330,23 +341,21 @@ export function writeCompletedBoundaryInstruction( boundaryID.id !== null, 'An ID must have been assigned before we can complete the boundary.', ); - const formattedBoundaryID = convertStringToBuffer( + const formattedBoundaryID = stringToChunk( encodeHTMLIDAttribute(boundaryID.id), ); - const formattedContentID = convertStringToBuffer( - contentSegmentID.toString(16), - ); + const formattedContentID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, formattedBoundaryID); writeChunk(destination, completeBoundaryScript2); writeChunk(destination, formattedContentID); return writeChunk(destination, completeBoundaryScript3); } -const clientRenderScript1Full = convertStringToBuffer( +const clientRenderScript1Full = stringToPrecomputedChunk( ''); +const clientRenderScript1Partial = stringToPrecomputedChunk(''); export function writeClientRenderBoundaryInstruction( destination: Destination, @@ -365,7 +374,7 @@ export function writeClientRenderBoundaryInstruction( boundaryID.id !== null, 'An ID must have been assigned before we can complete the boundary.', ); - const formattedBoundaryID = convertStringToBuffer( + const formattedBoundaryID = stringToPrecomputedChunk( encodeHTMLIDAttribute(boundaryID.id), ); writeChunk(destination, formattedBoundaryID); diff --git a/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js b/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js index f4e58a03c7ef..d82488ab171c 100644 --- a/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js +++ b/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js @@ -7,11 +7,12 @@ * @flow */ -import type {Destination} from 'react-server/src/ReactServerStreamConfig'; +import type {Destination, Chunk, PrecomputedChunk} from 'react-server/src/ReactServerStreamConfig'; import { writeChunk, - convertStringToBuffer, + stringToChunk, + stringToPrecomputedChunk, } from 'react-server/src/ReactServerStreamConfig'; import invariant from 'shared/invariant'; @@ -71,10 +72,10 @@ export function createSuspenseBoundaryID( return responseState.nextSuspenseID++; } -const RAW_TEXT = convertStringToBuffer('RCTRawText'); +const RAW_TEXT = stringToPrecomputedChunk('RCTRawText'); export function pushTextInstance( - target: Array, + target: Array, text: string, ): void { target.push( @@ -87,20 +88,20 @@ export function pushTextInstance( } export function pushStartInstance( - target: Array, + target: Array, type: string, props: Object, ): void { target.push( INSTANCE, - convertStringToBuffer(type), + stringToChunk(type), END, // Null terminated type string // TODO: props ); } export function pushEndInstance( - target: Array, + target: Array, type: string, props: Object, ): void { diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 411e6094164c..7614ba3e8dfb 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -27,15 +27,18 @@ const ReactNoopFlightServer = ReactFlightServer({ callback(); }, beginWriting(destination: Destination): void {}, - writeChunk(destination: Destination, buffer: Uint8Array): void { - destination.push(Buffer.from((buffer: any)).toString('utf8')); + writeChunk(destination: Destination, chunk: string): void { + destination.push(chunk); }, completeWriting(destination: Destination): void {}, close(destination: Destination): void {}, closeWithError(destination: Destination, error: mixed): void {}, flushBuffered(destination: Destination): void {}, - convertStringToBuffer(content: string): Uint8Array { - return Buffer.from(content, 'utf8'); + stringToChunk(content: string): string { + return content; + }, + stringToPrecomputedChunk(content: string): string { + return content; }, isModuleReference(reference: Object): boolean { return reference.$$typeof === Symbol.for('react.module.reference'); diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 4f5879b86bff..4731e965dab2 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -8,7 +8,11 @@ */ import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; -import type {Destination} from './ReactServerStreamConfig'; +import type { + Destination, + Chunk, + PrecomputedChunk, +} from './ReactServerStreamConfig'; import type {ReactNodeList} from 'shared/ReactTypes'; import type { SuspenseBoundaryID, @@ -78,7 +82,7 @@ type Segment = { parentFlushed: boolean, // typically a segment will be flushed by its parent, except if its parent was already flushed id: number, // starts as 0 and is lazily assigned if the parent flushes early +index: number, // the index within the parent's chunks or 0 at the root - +chunks: Array, + +chunks: Array, +children: Array, // If this segment represents a fallback, this is the content that will replace that fallback. +boundary: null | SuspenseBoundary, diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index 12241493e57f..b9a57c16eb27 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -66,11 +66,11 @@ ByteSize import type {Request, ReactModel} from 'react-server/src/ReactFlightServer'; -import {convertStringToBuffer} from './ReactServerStreamConfig'; +import {stringToChunk} from './ReactServerStreamConfig'; -export type {Destination} from './ReactServerStreamConfig'; +import type {Chunk} from './ReactServerStreamConfig'; -export type Chunk = Uint8Array; +export type {Destination, Chunk} from './ReactServerStreamConfig'; const stringify = JSON.stringify; @@ -86,7 +86,7 @@ export function processErrorChunk( ): Chunk { const errorInfo = {message, stack}; const row = serializeRowHeader('E', id) + stringify(errorInfo) + '\n'; - return convertStringToBuffer(row); + return stringToChunk(row); } export function processModelChunk( @@ -96,7 +96,7 @@ export function processModelChunk( ): Chunk { const json = stringify(model, request.toJSON); const row = serializeRowHeader('J', id) + json + '\n'; - return convertStringToBuffer(row); + return stringToChunk(row); } export function processModuleChunk( @@ -106,7 +106,7 @@ export function processModuleChunk( ): Chunk { const json = stringify(moduleMetaData); const row = serializeRowHeader('M', id) + json + '\n'; - return convertStringToBuffer(row); + return stringToChunk(row); } export function processSymbolChunk( @@ -116,7 +116,7 @@ export function processSymbolChunk( ): Chunk { const json = stringify(name); const row = serializeRowHeader('S', id) + json + '\n'; - return convertStringToBuffer(row); + return stringToChunk(row); } export { diff --git a/packages/react-server/src/ReactServerStreamConfigBrowser.js b/packages/react-server/src/ReactServerStreamConfigBrowser.js index efcef78509cb..3714be430276 100644 --- a/packages/react-server/src/ReactServerStreamConfigBrowser.js +++ b/packages/react-server/src/ReactServerStreamConfigBrowser.js @@ -9,6 +9,9 @@ export type Destination = ReadableStreamController; +export type PrecomputedChunk = Uint8Array; +export type Chunk = Uint8Array; + export function scheduleWork(callback: () => void) { callback(); } @@ -22,9 +25,9 @@ export function beginWriting(destination: Destination) {} export function writeChunk( destination: Destination, - buffer: Uint8Array, + chunk: PrecomputedChunk | Chunk, ): boolean { - destination.enqueue(buffer); + destination.enqueue(chunk); return destination.desiredSize > 0; } @@ -36,7 +39,11 @@ export function close(destination: Destination) { const textEncoder = new TextEncoder(); -export function convertStringToBuffer(content: string): Uint8Array { +export function stringToChunk(content: string): Chunk { + return textEncoder.encode(content); +} + +export function stringToPrecomputedChunk(content: string): PrecomputedChunk { return textEncoder.encode(content); } diff --git a/packages/react-server/src/ReactServerStreamConfigNode.js b/packages/react-server/src/ReactServerStreamConfigNode.js index 00e3364fc93a..4329554b7f05 100644 --- a/packages/react-server/src/ReactServerStreamConfigNode.js +++ b/packages/react-server/src/ReactServerStreamConfigNode.js @@ -18,6 +18,9 @@ type MightBeFlushable = { export type Destination = Writable & MightBeFlushable; +export type PrecomputedChunk = Uint8Array; +export type Chunk = string; + export function scheduleWork(callback: () => void) { setImmediate(callback); } @@ -44,9 +47,9 @@ export function beginWriting(destination: Destination) { export function writeChunk( destination: Destination, - buffer: Uint8Array, + chunk: Chunk | PrecomputedChunk, ): boolean { - const nodeBuffer = ((buffer: any): Buffer); // close enough + const nodeBuffer = ((chunk: any): Buffer | string); // close enough return destination.write(nodeBuffer); } @@ -61,7 +64,11 @@ export function close(destination: Destination) { destination.end(); } -export function convertStringToBuffer(content: string): Uint8Array { +export function stringToChunk(content: string): Chunk { + return content; +} + +export function stringToPrecomputedChunk(content: string): PrecomputedChunk { return Buffer.from(content, 'utf8'); } diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.custom.js b/packages/react-server/src/forks/ReactServerStreamConfig.custom.js index 276659596737..e832f19430a2 100644 --- a/packages/react-server/src/forks/ReactServerStreamConfig.custom.js +++ b/packages/react-server/src/forks/ReactServerStreamConfig.custom.js @@ -26,6 +26,9 @@ declare var $$$hostConfig: any; export opaque type Destination = mixed; // eslint-disable-line no-undef +export opaque type PrecomputedChunk = mixed; // eslint-disable-line no-undef +export opaque type Chunk = mixed; // eslint-disable-line no-undef + export const scheduleWork = $$$hostConfig.scheduleWork; export const beginWriting = $$$hostConfig.beginWriting; export const writeChunk = $$$hostConfig.writeChunk; @@ -33,4 +36,5 @@ export const completeWriting = $$$hostConfig.completeWriting; export const flushBuffered = $$$hostConfig.flushBuffered; export const close = $$$hostConfig.close; export const closeWithError = $$$hostConfig.closeWithError; -export const convertStringToBuffer = $$$hostConfig.convertStringToBuffer; +export const stringToChunk = $$$hostConfig.stringToChunk; +export const stringToPrecomputedChunk = $$$hostConfig.stringToPrecomputedChunk;