/** * Copyright (c) Meta Platforms, Inc. and 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 {Thenable, ReactCustomFormAction} from 'shared/ReactTypes.js'; import type {Response} from 'react-client/src/ReactFlightClient'; import type {Readable} from 'stream'; import { createResponse, createStreamState, getRoot, reportGlobalError, processStringChunk, processBinaryChunk, close, } from 'react-client/src/ReactFlightClient'; export * from './ReactFlightDOMClientEdge'; function findSourceMapURL(filename: string, environmentName: string) { const devServer = parcelRequire.meta.devServer; if (devServer != null) { const qs = new URLSearchParams(); qs.set('filename', filename); qs.set('env', environmentName); return devServer + '/__parcel_source_map?' + qs.toString(); } return null; } function noServerCall() { throw new Error( 'Server Functions cannot be called during initial render. ' + 'This would create a fetch waterfall. Try to use a Server Component ' + 'to pass data to Client Components instead.', ); } type EncodeFormActionCallback = ( id: any, args: Promise, ) => ReactCustomFormAction; export type Options = { nonce?: string, encodeFormAction?: EncodeFormActionCallback, replayConsoleLogs?: boolean, environmentName?: string, // For the Node.js client we only support a single-direction debug channel. debugChannel?: Readable, }; function startReadingFromStream( response: Response, stream: Readable, isSecondaryStream: boolean, ): void { const streamState = createStreamState(); stream.on('data', chunk => { if (typeof chunk === 'string') { processStringChunk(response, streamState, chunk); } else { processBinaryChunk(response, streamState, chunk); } }); stream.on('error', error => { reportGlobalError(response, error); }); stream.on('end', () => { // If we're the secondary stream, then we don't close the response until the // debug channel closes. if (!isSecondaryStream) { close(response); } }); } export function createFromNodeStream( stream: Readable, options?: Options, ): Thenable { const response: Response = createResponse( null, // bundlerConfig null, // serverReferenceConfig null, // moduleLoading noServerCall, options ? options.encodeFormAction : undefined, options && typeof options.nonce === 'string' ? options.nonce : undefined, undefined, // TODO: If encodeReply is supported, this should support temporaryReferences __DEV__ ? findSourceMapURL : undefined, __DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false __DEV__ && options && options.environmentName ? options.environmentName : undefined, ); if (__DEV__ && options && options.debugChannel) { startReadingFromStream(response, options.debugChannel, false); startReadingFromStream(response, stream, true); } else { startReadingFromStream(response, stream, false); } return getRoot(response); }