* Add format context * Let the Work node hold all working state for the recursive loop Stacks are nice and all but there's a cost to maintaining each frame both in terms of stack size usage and writing to it. * Move current format context into work * Synchronously render children of a Suspense boundary We don't have to spawn work and snapshot the context. Instead we can try to render the boundary immediately in case it works. * Lazily create the fallback work Instead of eagerly create the fallback work and then immediately abort it. We can just avoid creating it if we finish synchronously.
76 lines
1.8 KiB
JavaScript
76 lines
1.8 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {ReactNodeList} from 'shared/ReactTypes';
|
|
import type {Writable} from 'stream';
|
|
|
|
import {
|
|
createRequest,
|
|
startWork,
|
|
startFlowing,
|
|
abort,
|
|
} from 'react-server/src/ReactFizzServer';
|
|
|
|
import {
|
|
createResponseState,
|
|
createRootFormatContext,
|
|
} from './ReactDOMServerFormatConfig';
|
|
|
|
function createDrainHandler(destination, request) {
|
|
return () => startFlowing(request);
|
|
}
|
|
|
|
type Options = {
|
|
identifierPrefix?: string,
|
|
progressiveChunkSize?: number,
|
|
onReadyToStream?: () => void,
|
|
onCompleteAll?: () => void,
|
|
onError?: (error: mixed) => void,
|
|
};
|
|
|
|
type Controls = {
|
|
// Cancel any pending I/O and put anything remaining into
|
|
// client rendered mode.
|
|
abort(): void,
|
|
};
|
|
|
|
function pipeToNodeWritable(
|
|
children: ReactNodeList,
|
|
destination: Writable,
|
|
options?: Options,
|
|
): Controls {
|
|
const request = createRequest(
|
|
children,
|
|
destination,
|
|
createResponseState(options ? options.identifierPrefix : undefined),
|
|
createRootFormatContext(), // We call this here in case we need options to initialize it.
|
|
options ? options.progressiveChunkSize : undefined,
|
|
options ? options.onError : undefined,
|
|
options ? options.onCompleteAll : undefined,
|
|
options ? options.onReadyToStream : undefined,
|
|
);
|
|
let hasStartedFlowing = false;
|
|
startWork(request);
|
|
return {
|
|
startWriting() {
|
|
if (hasStartedFlowing) {
|
|
return;
|
|
}
|
|
hasStartedFlowing = true;
|
|
startFlowing(request);
|
|
destination.on('drain', createDrainHandler(destination, request));
|
|
},
|
|
abort() {
|
|
abort(request);
|
|
},
|
|
};
|
|
}
|
|
|
|
export {pipeToNodeWritable};
|