import type {ReactNodeList} from 'shared/ReactTypes';
import type {LazyComponent} from 'react/src/ReactLazy';
import type {ErrorInfo} from 'react-server/src/ReactFizzServer';
import ReactVersion from 'shared/ReactVersion';
import ReactSharedInternalsServer from 'react-server/src/ReactSharedInternalsServer';
import ReactSharedInternalsClient from 'shared/ReactSharedInternals';
import {
createRequest as createFlightRequest,
startWork as startFlightWork,
startFlowing as startFlightFlowing,
abort as abortFlight,
} from 'react-server/src/ReactFlightServer';
import {
createResponse as createFlightResponse,
getRoot as getFlightRoot,
processStringChunk as processFlightStringChunk,
close as closeFlight,
} from 'react-client/src/ReactFlightClient';
import {
createRequest as createFizzRequest,
startWork as startFizzWork,
startFlowing as startFizzFlowing,
abort as abortFizz,
} from 'react-server/src/ReactFizzServer';
import {
createResumableState,
createRenderState,
createRootFormatContext,
} from './ReactFizzConfigMarkup';
type ReactMarkupNodeList =
| React$Element<React$ComponentType<any>>
| LazyComponent<ReactMarkupNodeList, any>
| React$Element<string>
| string
| boolean
| number
| symbol
| null
| void
| bigint
| $AsyncIterable<ReactMarkupNodeList, ReactMarkupNodeList, void>
| $AsyncIterator<ReactMarkupNodeList, ReactMarkupNodeList, void>
| Iterable<ReactMarkupNodeList>
| Iterator<ReactMarkupNodeList>
| Array<ReactMarkupNodeList>
| Promise<ReactMarkupNodeList>;
type MarkupOptions = {
identifierPrefix?: string,
signal?: AbortSignal,
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
};
function noServerCallOrFormAction() {
throw new Error(
'renderToHTML should not have emitted Server References. This is a bug in React.',
);
}
export function experimental_renderToHTML(
children: ReactMarkupNodeList,
options?: MarkupOptions,
): Promise<string> {
return new Promise((resolve, reject) => {
const flightDestination = {
push(chunk: string | null): boolean {
if (chunk !== null) {
processFlightStringChunk(flightResponse, chunk);
} else {
closeFlight(flightResponse);
}
return true;
},
destroy(error: mixed): void {
abortFizz(fizzRequest, error);
reject(error);
},
};
let buffer = '';
const fizzDestination = {
push(chunk) {
if (chunk !== null) {
buffer += chunk;
} else {
resolve(buffer);
}
return true;
},
destroy(error) {
abortFlight(flightRequest, error);
reject(error);
},
};
let stashErrorIdx = 1;
const stashedErrors: Map<string, mixed> = new Map();
function handleFlightError(error: mixed): string {
const id = '' + stashErrorIdx++;
stashedErrors.set(id, error);
return id;
}
function handleError(error: mixed, errorInfo: ErrorInfo) {
if (typeof error === 'object' && error !== null) {
const id = error.digest;
if (typeof id === 'string' && stashedErrors.has(id)) {
error = stashedErrors.get(id);
}
}
reject(error);
const onError = options && options.onError;
if (onError) {
if (__DEV__) {
const prevGetCurrentStackImpl =
ReactSharedInternalsServer.getCurrentStack;
ReactSharedInternalsServer.getCurrentStack =
ReactSharedInternalsClient.getCurrentStack;
try {
onError(error, errorInfo);
} finally {
ReactSharedInternalsServer.getCurrentStack =
prevGetCurrentStackImpl;
}
} else {
onError(error, errorInfo);
}
}
}
const flightRequest = createFlightRequest(
children,
null,
handleFlightError,
options ? options.identifierPrefix : undefined,
undefined,
undefined,
'Markup',
undefined,
false,
);
const flightResponse = createFlightResponse(
null,
null,
null,
noServerCallOrFormAction,
noServerCallOrFormAction,
undefined,
undefined,
undefined,
false,
);
const resumableState = createResumableState(
options ? options.identifierPrefix : undefined,
undefined,
);
const root = getFlightRoot<ReactNodeList>(flightResponse);
const fizzRequest = createFizzRequest(
root,
resumableState,
createRenderState(
resumableState,
undefined,
undefined,
undefined,
undefined,
undefined,
),
createRootFormatContext(),
Infinity,
handleError,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abortFlight(flightRequest, (signal: any).reason);
abortFizz(fizzRequest, (signal: any).reason);
} else {
const listener = () => {
abortFlight(flightRequest, (signal: any).reason);
abortFizz(fizzRequest, (signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
startFlightWork(flightRequest);
startFlightFlowing(flightRequest, flightDestination);
startFizzWork(fizzRequest);
startFizzFlowing(fizzRequest, fizzDestination);
});
}
export {ReactVersion as version};