mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
fdc8c81e07
We already did this for Server References on the Client so this brings us parity with that. This gives us some more flexibility with changing the runtime implementation without having to affect the loaders. We can also do more in the runtime such as adding `.bind()` support to Server References. I also moved the CommonJS Proxy creation into the runtime helper from the register so that it can be handled in one place. This lets us remove the forks from Next.js since the loaders can be simplified there to just use these helpers. This PR doesn't change the protocol or shape of the objects. They're still specific to each bundler but ideally we should probably move this to shared helpers that can be used by multiple bundler implementations.
99 lines
2.5 KiB
JavaScript
99 lines
2.5 KiB
JavaScript
/**
|
|
* 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 {ReactClientValue} from 'react-server/src/ReactFlightServer';
|
|
import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes';
|
|
import type {ClientManifest} from './ReactFlightServerConfigWebpackBundler';
|
|
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
|
|
|
|
import {
|
|
createRequest,
|
|
startWork,
|
|
startFlowing,
|
|
abort,
|
|
} from 'react-server/src/ReactFlightServer';
|
|
|
|
import {
|
|
createResponse,
|
|
close,
|
|
getRoot,
|
|
} from 'react-server/src/ReactFlightReplyServer';
|
|
|
|
import {decodeAction} from 'react-server/src/ReactFlightActionServer';
|
|
|
|
export {
|
|
registerServerReference,
|
|
registerClientReference,
|
|
createClientModuleProxy,
|
|
} from './ReactFlightWebpackReferences';
|
|
|
|
type Options = {
|
|
identifierPrefix?: string,
|
|
signal?: AbortSignal,
|
|
context?: Array<[string, ServerContextJSONValue]>,
|
|
onError?: (error: mixed) => void,
|
|
};
|
|
|
|
function renderToReadableStream(
|
|
model: ReactClientValue,
|
|
webpackMap: ClientManifest,
|
|
options?: Options,
|
|
): ReadableStream {
|
|
const request = createRequest(
|
|
model,
|
|
webpackMap,
|
|
options ? options.onError : undefined,
|
|
options ? options.context : undefined,
|
|
options ? options.identifierPrefix : undefined,
|
|
);
|
|
if (options && options.signal) {
|
|
const signal = options.signal;
|
|
if (signal.aborted) {
|
|
abort(request, (signal: any).reason);
|
|
} else {
|
|
const listener = () => {
|
|
abort(request, (signal: any).reason);
|
|
signal.removeEventListener('abort', listener);
|
|
};
|
|
signal.addEventListener('abort', listener);
|
|
}
|
|
}
|
|
const stream = new ReadableStream(
|
|
{
|
|
type: 'bytes',
|
|
start: (controller): ?Promise<void> => {
|
|
startWork(request);
|
|
},
|
|
pull: (controller): ?Promise<void> => {
|
|
startFlowing(request, controller);
|
|
},
|
|
cancel: (reason): ?Promise<void> => {},
|
|
},
|
|
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
|
{highWaterMark: 0},
|
|
);
|
|
return stream;
|
|
}
|
|
|
|
function decodeReply<T>(
|
|
body: string | FormData,
|
|
webpackMap: ServerManifest,
|
|
): Thenable<T> {
|
|
if (typeof body === 'string') {
|
|
const form = new FormData();
|
|
form.append('0', body);
|
|
body = form;
|
|
}
|
|
const response = createResponse(webpackMap, '', body);
|
|
close(response);
|
|
return getRoot(response);
|
|
}
|
|
|
|
export {renderToReadableStream, decodeReply, decodeAction};
|