import type {
Destination,
Chunk,
PrecomputedChunk,
} from './ReactServerStreamConfig';
import type {
ReactNodeList,
ReactContext,
ReactProviderType,
OffscreenMode,
Wakeable,
} from 'shared/ReactTypes';
import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';
import type {
SuspenseBoundaryID,
ResponseState,
FormatContext,
Resources,
BoundaryResources,
} from './ReactServerFormatConfig';
import type {ContextSnapshot} from './ReactFizzNewContext';
import type {ComponentStackNode} from './ReactFizzComponentStack';
import type {TreeContext} from './ReactFizzTreeContext';
import type {ThenableState} from './ReactFizzThenable';
import {
scheduleWork,
beginWriting,
writeChunk,
writeChunkAndReturn,
completeWriting,
flushBuffered,
close,
closeWithError,
} from './ReactServerStreamConfig';
import {
writeCompletedRoot,
writePlaceholder,
writeStartCompletedSuspenseBoundary,
writeStartPendingSuspenseBoundary,
writeStartClientRenderedSuspenseBoundary,
writeEndCompletedSuspenseBoundary,
writeEndPendingSuspenseBoundary,
writeEndClientRenderedSuspenseBoundary,
writeStartSegment,
writeEndSegment,
writeClientRenderBoundaryInstruction,
writeCompletedBoundaryInstruction,
writeCompletedSegmentInstruction,
pushTextInstance,
pushStartInstance,
pushEndInstance,
pushStartCompletedSuspenseBoundary,
pushEndCompletedSuspenseBoundary,
pushSegmentFinale,
UNINITIALIZED_SUSPENSE_BOUNDARY_ID,
assignSuspenseBoundaryID,
getChildFormatContext,
writeInitialResources,
writeImmediateResources,
hoistResources,
hoistResourcesToRoot,
prepareToRender,
cleanupAfterRender,
setCurrentlyRenderingBoundaryResourcesTarget,
createResources,
createBoundaryResources,
} from './ReactServerFormatConfig';
import {
constructClassInstance,
mountClassInstance,
} from './ReactFizzClassComponent';
import {
getMaskedContext,
processChildContext,
emptyContextObject,
} from './ReactFizzContext';
import {
readContext,
rootContextSnapshot,
switchContext,
getActiveContext,
pushProvider,
popProvider,
} from './ReactFizzNewContext';
import {
prepareToUseHooks,
finishHooks,
checkDidRenderIdHook,
resetHooksState,
HooksDispatcher,
currentResponseState,
setCurrentResponseState,
getThenableStateAfterSuspending,
} from './ReactFizzHooks';
import {DefaultCacheDispatcher} from './ReactFizzCache';
import {getStackByComponentStackNode} from './ReactFizzComponentStack';
import {emptyTreeContext, pushTreeContext} from './ReactFizzTreeContext';
import {
getIteratorFn,
REACT_ELEMENT_TYPE,
REACT_PORTAL_TYPE,
REACT_LAZY_TYPE,
REACT_SUSPENSE_TYPE,
REACT_LEGACY_HIDDEN_TYPE,
REACT_DEBUG_TRACING_MODE_TYPE,
REACT_STRICT_MODE_TYPE,
REACT_PROFILER_TYPE,
REACT_SUSPENSE_LIST_TYPE,
REACT_FRAGMENT_TYPE,
REACT_FORWARD_REF_TYPE,
REACT_MEMO_TYPE,
REACT_PROVIDER_TYPE,
REACT_CONTEXT_TYPE,
REACT_SCOPE_TYPE,
REACT_OFFSCREEN_TYPE,
} from 'shared/ReactSymbols';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {
disableLegacyContext,
disableModulePatternComponents,
enableScopeAPI,
enableSuspenseAvoidThisFallbackFizz,
enableFloat,
enableCache,
} from 'shared/ReactFeatureFlags';
import assign from 'shared/assign';
import getComponentNameFromType from 'shared/getComponentNameFromType';
import isArray from 'shared/isArray';
import {SuspenseException, getSuspendedThenable} from './ReactFizzThenable';
const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
const ReactCurrentCache = ReactSharedInternals.ReactCurrentCache;
const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
type LegacyContext = {
[key: string]: any,
};
type SuspenseBoundary = {
id: SuspenseBoundaryID,
rootSegmentID: number,
errorDigest: ?string,
errorMessage?: string,
errorComponentStack?: string,
forceClientRender: boolean,
parentFlushed: boolean,
pendingTasks: number,
completedSegments: Array<Segment>,
byteSize: number,
fallbackAbortableTasks: Set<Task>,
resources: BoundaryResources,
};
export type Task = {
node: ReactNodeList,
ping: () => void,
blockedBoundary: Root | SuspenseBoundary,
blockedSegment: Segment,
abortSet: Set<Task>,
legacyContext: LegacyContext,
context: ContextSnapshot,
treeContext: TreeContext,
componentStack: null | ComponentStackNode,
thenableState: null | ThenableState,
};
const PENDING = 0;
const COMPLETED = 1;
const FLUSHED = 2;
const ABORTED = 3;
const ERRORED = 4;
type Root = null;
type Segment = {
status: 0 | 1 | 2 | 3 | 4,
parentFlushed: boolean,
id: number,
+index: number,
+chunks: Array<Chunk | PrecomputedChunk>,
+children: Array<Segment>,
formatContext: FormatContext,
+boundary: null | SuspenseBoundary,
lastPushedText: boolean,
textEmbedded: boolean,
};
const OPEN = 0;
const CLOSING = 1;
const CLOSED = 2;
export opaque type Request = {
destination: null | Destination,
+responseState: ResponseState,
+progressiveChunkSize: number,
status: 0 | 1 | 2,
fatalError: mixed,
nextSegmentId: number,
allPendingTasks: number,
pendingRootTasks: number,
resources: Resources,
completedRootSegment: null | Segment,
abortableTasks: Set<Task>,
pingedTasks: Array<Task>,
clientRenderedBoundaries: Array<SuspenseBoundary>,
completedBoundaries: Array<SuspenseBoundary>,
partialBoundaries: Array<SuspenseBoundary>,
+preamble: Array<Chunk | PrecomputedChunk>,
+postamble: Array<Chunk | PrecomputedChunk>,
onError: (error: mixed) => ?string,
onAllReady: () => void,
onShellReady: () => void,
onShellError: (error: mixed) => void,
onFatalError: (error: mixed) => void,
};
const DEFAULT_PROGRESSIVE_CHUNK_SIZE = 12800;
function defaultErrorHandler(error: mixed) {
console['error'](error);
return null;
}
function noop(): void {}
export function createRequest(
children: ReactNodeList,
responseState: ResponseState,
rootFormatContext: FormatContext,
progressiveChunkSize: void | number,
onError: void | ((error: mixed) => ?string),
onAllReady: void | (() => void),
onShellReady: void | (() => void),
onShellError: void | ((error: mixed) => void),
onFatalError: void | ((error: mixed) => void),
): Request {
const pingedTasks = [];
const abortSet: Set<Task> = new Set();
const resources: Resources = createResources();
const request = {
destination: null,
responseState,
progressiveChunkSize:
progressiveChunkSize === undefined
? DEFAULT_PROGRESSIVE_CHUNK_SIZE
: progressiveChunkSize,
status: OPEN,
fatalError: null,
nextSegmentId: 0,
allPendingTasks: 0,
pendingRootTasks: 0,
resources,
completedRootSegment: null,
abortableTasks: abortSet,
pingedTasks: pingedTasks,
clientRenderedBoundaries: ([]: Array<SuspenseBoundary>),
completedBoundaries: ([]: Array<SuspenseBoundary>),
partialBoundaries: ([]: Array<SuspenseBoundary>),
preamble: ([]: Array<Chunk | PrecomputedChunk>),
postamble: ([]: Array<Chunk | PrecomputedChunk>),
onError: onError === undefined ? defaultErrorHandler : onError,
onAllReady: onAllReady === undefined ? noop : onAllReady,
onShellReady: onShellReady === undefined ? noop : onShellReady,
onShellError: onShellError === undefined ? noop : onShellError,
onFatalError: onFatalError === undefined ? noop : onFatalError,
};
const rootSegment = createPendingSegment(
request,
0,
null,
rootFormatContext,
false,
false,
);
rootSegment.parentFlushed = true;
const rootTask = createTask(
request,
null,
children,
null,
rootSegment,
abortSet,
emptyContextObject,
rootContextSnapshot,
emptyTreeContext,
);
pingedTasks.push(rootTask);
return request;
}
function pingTask(request: Request, task: Task): void {
const pingedTasks = request.pingedTasks;
pingedTasks.push(task);
if (pingedTasks.length === 1) {
scheduleWork(() => performWork(request));
}
}
function createSuspenseBoundary(
request: Request,
fallbackAbortableTasks: Set<Task>,
): SuspenseBoundary {
return {
id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID,
rootSegmentID: -1,
parentFlushed: false,
pendingTasks: 0,
forceClientRender: false,
completedSegments: [],
byteSize: 0,
fallbackAbortableTasks,
errorDigest: null,
resources: createBoundaryResources(),
};
}
function createTask(
request: Request,
thenableState: ThenableState | null,
node: ReactNodeList,
blockedBoundary: Root | SuspenseBoundary,
blockedSegment: Segment,
abortSet: Set<Task>,
legacyContext: LegacyContext,
context: ContextSnapshot,
treeContext: TreeContext,
): Task {
request.allPendingTasks++;
if (blockedBoundary === null) {
request.pendingRootTasks++;
} else {
blockedBoundary.pendingTasks++;
}
const task: Task = ({
node,
ping: () => pingTask(request, task),
blockedBoundary,
blockedSegment,
abortSet,
legacyContext,
context,
treeContext,
thenableState,
}: any);
if (__DEV__) {
task.componentStack = null;
}
abortSet.add(task);
return task;
}
function createPendingSegment(
request: Request,
index: number,
boundary: null | SuspenseBoundary,
formatContext: FormatContext,
lastPushedText: boolean,
textEmbedded: boolean,
): Segment {
return {
status: PENDING,
id: -1,
index,
parentFlushed: false,
chunks: [],
children: [],
formatContext,
boundary,
lastPushedText,
textEmbedded,
};
}
let currentTaskInDEV: null | Task = null;
function getCurrentStackInDEV(): string {
if (__DEV__) {
if (currentTaskInDEV === null || currentTaskInDEV.componentStack === null) {
return '';
}
return getStackByComponentStackNode(currentTaskInDEV.componentStack);
}
return '';
}
function pushBuiltInComponentStackInDEV(task: Task, type: string): void {
if (__DEV__) {
task.componentStack = {
tag: 0,
parent: task.componentStack,
type,
};
}
}
function pushFunctionComponentStackInDEV(task: Task, type: Function): void {
if (__DEV__) {
task.componentStack = {
tag: 1,
parent: task.componentStack,
type,
};
}
}
function pushClassComponentStackInDEV(task: Task, type: Function): void {
if (__DEV__) {
task.componentStack = {
tag: 2,
parent: task.componentStack,
type,
};
}
}
function popComponentStackInDEV(task: Task): void {
if (__DEV__) {
if (task.componentStack === null) {
console.error(
'Unexpectedly popped too many stack frames. This is a bug in React.',
);
} else {
task.componentStack = task.componentStack.parent;
}
}
}
let lastBoundaryErrorComponentStackDev: ?string = null;
function captureBoundaryErrorDetailsDev(
boundary: SuspenseBoundary,
error: mixed,
) {
if (__DEV__) {
let errorMessage;
if (typeof error === 'string') {
errorMessage = error;
} else if (error && typeof error.message === 'string') {
errorMessage = error.message;
} else {
errorMessage = String(error);
}
const errorComponentStack =
lastBoundaryErrorComponentStackDev || getCurrentStackInDEV();
lastBoundaryErrorComponentStackDev = null;
boundary.errorMessage = errorMessage;
boundary.errorComponentStack = errorComponentStack;
}
}
function logRecoverableError(request: Request, error: any): ?string {
const errorDigest = request.onError(error);
if (errorDigest != null && typeof errorDigest !== 'string') {
throw new Error(
`onError returned something with a type other than "string". onError should return a string and may return null or undefined but must not return anything else. It received something of type "${typeof errorDigest}" instead`,
);
}
return errorDigest;
}
function fatalError(request: Request, error: mixed): void {
const onShellError = request.onShellError;
onShellError(error);
const onFatalError = request.onFatalError;
onFatalError(error);
if (request.destination !== null) {
request.status = CLOSED;
closeWithError(request.destination, error);
} else {
request.status = CLOSING;
request.fatalError = error;
}
}
function renderSuspenseBoundary(
request: Request,
task: Task,
props: Object,
): void {
pushBuiltInComponentStackInDEV(task, 'Suspense');
const parentBoundary = task.blockedBoundary;
const parentSegment = task.blockedSegment;
const fallback: ReactNodeList = props.fallback;
const content: ReactNodeList = props.children;
const fallbackAbortSet: Set<Task> = new Set();
const newBoundary = createSuspenseBoundary(request, fallbackAbortSet);
const insertionIndex = parentSegment.chunks.length;
const boundarySegment = createPendingSegment(
request,
insertionIndex,
newBoundary,
parentSegment.formatContext,
false,
false,
);
parentSegment.children.push(boundarySegment);
parentSegment.lastPushedText = false;
const contentRootSegment = createPendingSegment(
request,
0,
null,
parentSegment.formatContext,
false,
false,
);
contentRootSegment.parentFlushed = true;
task.blockedBoundary = newBoundary;
task.blockedSegment = contentRootSegment;
if (enableFloat) {
setCurrentlyRenderingBoundaryResourcesTarget(
request.resources,
newBoundary.resources,
);
}
try {
renderNode(request, task, content);
pushSegmentFinale(
contentRootSegment.chunks,
request.responseState,
contentRootSegment.lastPushedText,
contentRootSegment.textEmbedded,
);
contentRootSegment.status = COMPLETED;
if (enableFloat) {
if (newBoundary.pendingTasks === 0) {
hoistCompletedBoundaryResources(request, newBoundary);
}
}
queueCompletedSegment(newBoundary, contentRootSegment);
if (newBoundary.pendingTasks === 0) {
popComponentStackInDEV(task);
return;
}
} catch (error) {
contentRootSegment.status = ERRORED;
newBoundary.forceClientRender = true;
newBoundary.errorDigest = logRecoverableError(request, error);
if (__DEV__) {
captureBoundaryErrorDetailsDev(newBoundary, error);
}
} finally {
if (enableFloat) {
setCurrentlyRenderingBoundaryResourcesTarget(
request.resources,
parentBoundary ? parentBoundary.resources : null,
);
}
task.blockedBoundary = parentBoundary;
task.blockedSegment = parentSegment;
}
const suspendedFallbackTask = createTask(
request,
null,
fallback,
parentBoundary,
boundarySegment,
fallbackAbortSet,
task.legacyContext,
task.context,
task.treeContext,
);
if (__DEV__) {
suspendedFallbackTask.componentStack = task.componentStack;
}
request.pingedTasks.push(suspendedFallbackTask);
popComponentStackInDEV(task);
}
function hoistCompletedBoundaryResources(
request: Request,
completedBoundary: SuspenseBoundary,
): void {
if (request.completedRootSegment !== null || request.pendingRootTasks > 0) {
hoistResourcesToRoot(request.resources, completedBoundary.resources);
}
}
function renderBackupSuspenseBoundary(
request: Request,
task: Task,
props: Object,
) {
pushBuiltInComponentStackInDEV(task, 'Suspense');
const content = props.children;
const segment = task.blockedSegment;
pushStartCompletedSuspenseBoundary(segment.chunks);
renderNode(request, task, content);
pushEndCompletedSuspenseBoundary(segment.chunks);
popComponentStackInDEV(task);
}
function renderHostElement(
request: Request,
task: Task,
type: string,
props: Object,
): void {
pushBuiltInComponentStackInDEV(task, type);
const segment = task.blockedSegment;
const children = pushStartInstance(
segment.chunks,
request.preamble,
type,
props,
request.responseState,
segment.formatContext,
segment.lastPushedText,
);
segment.lastPushedText = false;
const prevContext = segment.formatContext;
segment.formatContext = getChildFormatContext(prevContext, type, props);
renderNode(request, task, children);
segment.formatContext = prevContext;
pushEndInstance(segment.chunks, request.postamble, type, props);
segment.lastPushedText = false;
popComponentStackInDEV(task);
}
function shouldConstruct(Component: any) {
return Component.prototype && Component.prototype.isReactComponent;
}
function renderWithHooks<Props, SecondArg>(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
): any {
const componentIdentity = {};
prepareToUseHooks(task, componentIdentity, prevThenableState);
const result = Component(props, secondArg);
return finishHooks(Component, props, result, secondArg);
}
function finishClassComponent(
request: Request,
task: Task,
instance: any,
Component: any,
props: any,
): ReactNodeList {
const nextChildren = instance.render();
if (__DEV__) {
if (instance.props !== props) {
if (!didWarnAboutReassigningProps) {
console.error(
'It looks like %s is reassigning its own `this.props` while rendering. ' +
'This is not supported and can lead to confusing bugs.',
getComponentNameFromType(Component) || 'a component',
);
}
didWarnAboutReassigningProps = true;
}
}
if (!disableLegacyContext) {
const childContextTypes = Component.childContextTypes;
if (childContextTypes !== null && childContextTypes !== undefined) {
const previousContext = task.legacyContext;
const mergedContext = processChildContext(
instance,
Component,
previousContext,
childContextTypes,
);
task.legacyContext = mergedContext;
renderNodeDestructive(request, task, null, nextChildren);
task.legacyContext = previousContext;
return;
}
}
renderNodeDestructive(request, task, null, nextChildren);
}
function renderClassComponent(
request: Request,
task: Task,
Component: any,
props: any,
): void {
pushClassComponentStackInDEV(task, Component);
const maskedContext = !disableLegacyContext
? getMaskedContext(Component, task.legacyContext)
: undefined;
const instance = constructClassInstance(Component, props, maskedContext);
mountClassInstance(instance, Component, props, maskedContext);
finishClassComponent(request, task, instance, Component, props);
popComponentStackInDEV(task);
}
const didWarnAboutBadClass: {[string]: boolean} = {};
const didWarnAboutModulePatternComponent: {[string]: boolean} = {};
const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
let didWarnAboutReassigningProps = false;
const didWarnAboutDefaultPropsOnFunctionComponent: {[string]: boolean} = {};
let didWarnAboutGenerators = false;
let didWarnAboutMaps = false;
let hasWarnedAboutUsingContextAsConsumer = false;
function renderIndeterminateComponent(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
Component: any,
props: any,
): void {
let legacyContext;
if (!disableLegacyContext) {
legacyContext = getMaskedContext(Component, task.legacyContext);
}
pushFunctionComponentStackInDEV(task, Component);
if (__DEV__) {
if (
Component.prototype &&
typeof Component.prototype.render === 'function'
) {
const componentName = getComponentNameFromType(Component) || 'Unknown';
if (!didWarnAboutBadClass[componentName]) {
console.error(
"The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
'This is likely to cause errors. Change %s to extend React.Component instead.',
componentName,
componentName,
);
didWarnAboutBadClass[componentName] = true;
}
}
}
const value = renderWithHooks(
request,
task,
prevThenableState,
Component,
props,
legacyContext,
);
const hasId = checkDidRenderIdHook();
if (__DEV__) {
if (
typeof value === 'object' &&
value !== null &&
typeof value.render === 'function' &&
value.$$typeof === undefined
) {
const componentName = getComponentNameFromType(Component) || 'Unknown';
if (!didWarnAboutModulePatternComponent[componentName]) {
console.error(
'The <%s /> component appears to be a function component that returns a class instance. ' +
'Change %s to a class that extends React.Component instead. ' +
"If you can't use a class try assigning the prototype on the function as a workaround. " +
"`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
'cannot be called with `new` by React.',
componentName,
componentName,
componentName,
);
didWarnAboutModulePatternComponent[componentName] = true;
}
}
}
if (
!disableModulePatternComponents &&
typeof value === 'object' &&
value !== null &&
typeof value.render === 'function' &&
value.$$typeof === undefined
) {
if (__DEV__) {
const componentName = getComponentNameFromType(Component) || 'Unknown';
if (!didWarnAboutModulePatternComponent[componentName]) {
console.error(
'The <%s /> component appears to be a function component that returns a class instance. ' +
'Change %s to a class that extends React.Component instead. ' +
"If you can't use a class try assigning the prototype on the function as a workaround. " +
"`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
'cannot be called with `new` by React.',
componentName,
componentName,
componentName,
);
didWarnAboutModulePatternComponent[componentName] = true;
}
}
mountClassInstance(value, Component, props, legacyContext);
finishClassComponent(request, task, value, Component, props);
} else {
if (__DEV__) {
if (disableLegacyContext && Component.contextTypes) {
console.error(
'%s uses the legacy contextTypes API which is no longer supported. ' +
'Use React.createContext() with React.useContext() instead.',
getComponentNameFromType(Component) || 'Unknown',
);
}
}
if (__DEV__) {
validateFunctionComponentInDev(Component);
}
if (hasId) {
const prevTreeContext = task.treeContext;
const totalChildren = 1;
const index = 0;
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
try {
renderNodeDestructive(request, task, null, value);
} finally {
task.treeContext = prevTreeContext;
}
} else {
renderNodeDestructive(request, task, null, value);
}
}
popComponentStackInDEV(task);
}
function validateFunctionComponentInDev(Component: any): void {
if (__DEV__) {
if (Component) {
if (Component.childContextTypes) {
console.error(
'%s(...): childContextTypes cannot be defined on a function component.',
Component.displayName || Component.name || 'Component',
);
}
}
if (Component.defaultProps !== undefined) {
const componentName = getComponentNameFromType(Component) || 'Unknown';
if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
console.error(
'%s: Support for defaultProps will be removed from function components ' +
'in a future major release. Use JavaScript default parameters instead.',
componentName,
);
didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
}
}
if (typeof Component.getDerivedStateFromProps === 'function') {
const componentName = getComponentNameFromType(Component) || 'Unknown';
if (!didWarnAboutGetDerivedStateOnFunctionComponent[componentName]) {
console.error(
'%s: Function components do not support getDerivedStateFromProps.',
componentName,
);
didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true;
}
}
if (
typeof Component.contextType === 'object' &&
Component.contextType !== null
) {
const componentName = getComponentNameFromType(Component) || 'Unknown';
if (!didWarnAboutContextTypeOnFunctionComponent[componentName]) {
console.error(
'%s: Function components do not support contextType.',
componentName,
);
didWarnAboutContextTypeOnFunctionComponent[componentName] = true;
}
}
}
}
function resolveDefaultProps(Component: any, baseProps: Object): Object {
if (Component && Component.defaultProps) {
const props = assign({}, baseProps);
const defaultProps = Component.defaultProps;
for (const propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
return props;
}
return baseProps;
}
function renderForwardRef(
request: Request,
task: Task,
prevThenableState: null | ThenableState,
type: any,
props: Object,
ref: any,
): void {
pushFunctionComponentStackInDEV(task, type.render);
const children = renderWithHooks(
request,
task,
prevThenableState,
type.render,
props,
ref,
);
const hasId = checkDidRenderIdHook();
if (hasId) {
const prevTreeContext = task.treeContext;
const totalChildren = 1;
const index = 0;
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
try {
renderNodeDestructive(request, task, null, children);
} finally {
task.treeContext = prevTreeContext;
}
} else {
renderNodeDestructive(request, task, null, children);
}
popComponentStackInDEV(task);
}
function renderMemo(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
type: any,
props: Object,
ref: any,
): void {
const innerType = type.type;
const resolvedProps = resolveDefaultProps(innerType, props);
renderElement(
request,
task,
prevThenableState,
innerType,
resolvedProps,
ref,
);
}
function renderContextConsumer(
request: Request,
task: Task,
context: ReactContext<any>,
props: Object,
): void {
if (__DEV__) {
if ((context: any)._context === undefined) {
if (context !== context.Consumer) {
if (!hasWarnedAboutUsingContextAsConsumer) {
hasWarnedAboutUsingContextAsConsumer = true;
console.error(
'Rendering <Context> directly is not supported and will be removed in ' +
'a future major release. Did you mean to render <Context.Consumer> instead?',
);
}
}
} else {
context = (context: any)._context;
}
}
const render = props.children;
if (__DEV__) {
if (typeof render !== 'function') {
console.error(
'A context consumer was rendered with multiple children, or a child ' +
"that isn't a function. A context consumer expects a single child " +
'that is a function. If you did pass a function, make sure there ' +
'is no trailing or leading whitespace around it.',
);
}
}
const newValue = readContext(context);
const newChildren = render(newValue);
renderNodeDestructive(request, task, null, newChildren);
}
function renderContextProvider(
request: Request,
task: Task,
type: ReactProviderType<any>,
props: Object,
): void {
const context = type._context;
const value = props.value;
const children = props.children;
let prevSnapshot;
if (__DEV__) {
prevSnapshot = task.context;
}
task.context = pushProvider(context, value);
renderNodeDestructive(request, task, null, children);
task.context = popProvider(context);
if (__DEV__) {
if (prevSnapshot !== task.context) {
console.error(
'Popping the context provider did not return back to the original snapshot. This is a bug in React.',
);
}
}
}
function renderLazyComponent(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
lazyComponent: LazyComponentType<any, any>,
props: Object,
ref: any,
): void {
pushBuiltInComponentStackInDEV(task, 'Lazy');
const payload = lazyComponent._payload;
const init = lazyComponent._init;
const Component = init(payload);
const resolvedProps = resolveDefaultProps(Component, props);
renderElement(
request,
task,
prevThenableState,
Component,
resolvedProps,
ref,
);
popComponentStackInDEV(task);
}
function renderOffscreen(request: Request, task: Task, props: Object): void {
const mode: ?OffscreenMode = (props.mode: any);
if (mode === 'hidden') {
} else {
renderNodeDestructive(request, task, null, props.children);
}
}
function renderElement(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
type: any,
props: Object,
ref: any,
): void {
if (typeof type === 'function') {
if (shouldConstruct(type)) {
renderClassComponent(request, task, type, props);
return;
} else {
renderIndeterminateComponent(
request,
task,
prevThenableState,
type,
props,
);
return;
}
}
if (typeof type === 'string') {
renderHostElement(request, task, type, props);
return;
}
switch (type) {
case REACT_LEGACY_HIDDEN_TYPE:
case REACT_DEBUG_TRACING_MODE_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_PROFILER_TYPE:
case REACT_FRAGMENT_TYPE: {
renderNodeDestructive(request, task, null, props.children);
return;
}
case REACT_OFFSCREEN_TYPE: {
renderOffscreen(request, task, props);
return;
}
case REACT_SUSPENSE_LIST_TYPE: {
pushBuiltInComponentStackInDEV(task, 'SuspenseList');
renderNodeDestructive(request, task, null, props.children);
popComponentStackInDEV(task);
return;
}
case REACT_SCOPE_TYPE: {
if (enableScopeAPI) {
renderNodeDestructive(request, task, null, props.children);
return;
}
throw new Error('ReactDOMServer does not yet support scope components.');
}
case REACT_SUSPENSE_TYPE: {
if (
enableSuspenseAvoidThisFallbackFizz &&
props.unstable_avoidThisFallback === true
) {
renderBackupSuspenseBoundary(request, task, props);
} else {
renderSuspenseBoundary(request, task, props);
}
return;
}
}
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE: {
renderForwardRef(request, task, prevThenableState, type, props, ref);
return;
}
case REACT_MEMO_TYPE: {
renderMemo(request, task, prevThenableState, type, props, ref);
return;
}
case REACT_PROVIDER_TYPE: {
renderContextProvider(request, task, type, props);
return;
}
case REACT_CONTEXT_TYPE: {
renderContextConsumer(request, task, type, props);
return;
}
case REACT_LAZY_TYPE: {
renderLazyComponent(request, task, prevThenableState, type, props);
return;
}
}
}
let info = '';
if (__DEV__) {
if (
type === undefined ||
(typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0)
) {
info +=
' You likely forgot to export your component from the file ' +
"it's defined in, or you might have mixed up default and " +
'named imports.';
}
}
throw new Error(
'Element type is invalid: expected a string (for built-in ' +
'components) or a class/function (for composite components) ' +
`but got: ${type == null ? type : typeof type}.${info}`,
);
}
function validateIterable(iterable, iteratorFn: Function): void {
if (__DEV__) {
if (
typeof Symbol === 'function' &&
iterable[Symbol.toStringTag] === 'Generator'
) {
if (!didWarnAboutGenerators) {
console.error(
'Using Generators as children is unsupported and will likely yield ' +
'unexpected results because enumerating a generator mutates it. ' +
'You may convert it to an array with `Array.from()` or the ' +
'`[...spread]` operator before rendering. Keep in mind ' +
'you might need to polyfill these features for older browsers.',
);
}
didWarnAboutGenerators = true;
}
if ((iterable: any).entries === iteratorFn) {
if (!didWarnAboutMaps) {
console.error(
'Using Maps as children is not supported. ' +
'Use an array of keyed ReactElements instead.',
);
}
didWarnAboutMaps = true;
}
}
}
function renderNodeDestructive(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
node: ReactNodeList,
): void {
if (__DEV__) {
try {
return renderNodeDestructiveImpl(request, task, prevThenableState, node);
} catch (x) {
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
} else {
lastBoundaryErrorComponentStackDev =
lastBoundaryErrorComponentStackDev !== null
? lastBoundaryErrorComponentStackDev
: getCurrentStackInDEV();
}
throw x;
}
} else {
return renderNodeDestructiveImpl(request, task, prevThenableState, node);
}
}
function renderNodeDestructiveImpl(
request: Request,
task: Task,
prevThenableState: ThenableState | null,
node: ReactNodeList,
): void {
task.node = node;
if (typeof node === 'object' && node !== null) {
switch ((node: any).$$typeof) {
case REACT_ELEMENT_TYPE: {
const element: React$Element<any> = (node: any);
const type = element.type;
const props = element.props;
const ref = element.ref;
renderElement(request, task, prevThenableState, type, props, ref);
return;
}
case REACT_PORTAL_TYPE:
throw new Error(
'Portals are not currently supported by the server renderer. ' +
'Render them conditionally so that they only appear on the client render.',
);
case REACT_LAZY_TYPE: {
const lazyNode: LazyComponentType<any, any> = (node: any);
const payload = lazyNode._payload;
const init = lazyNode._init;
let resolvedNode;
if (__DEV__) {
try {
resolvedNode = init(payload);
} catch (x) {
if (
typeof x === 'object' &&
x !== null &&
typeof x.then === 'function'
) {
pushBuiltInComponentStackInDEV(task, 'Lazy');
}
throw x;
}
} else {
resolvedNode = init(payload);
}
renderNodeDestructive(request, task, null, resolvedNode);
return;
}
}
if (isArray(node)) {
renderChildrenArray(request, task, node);
return;
}
const iteratorFn = getIteratorFn(node);
if (iteratorFn) {
if (__DEV__) {
validateIterable(node, iteratorFn);
}
const iterator = iteratorFn.call(node);
if (iterator) {
let step = iterator.next();
if (!step.done) {
const children = [];
do {
children.push(step.value);
step = iterator.next();
} while (!step.done);
renderChildrenArray(request, task, children);
return;
}
return;
}
}
const childString = Object.prototype.toString.call(node);
throw new Error(
`Objects are not valid as a React child (found: ${
childString === '[object Object]'
? 'object with keys {' + Object.keys(node).join(', ') + '}'
: childString
}). ` +
'If you meant to render a collection of children, use an array ' +
'instead.',
);
}
if (typeof node === 'string') {
const segment = task.blockedSegment;
segment.lastPushedText = pushTextInstance(
task.blockedSegment.chunks,
node,
request.responseState,
segment.lastPushedText,
);
return;
}
if (typeof node === 'number') {
const segment = task.blockedSegment;
segment.lastPushedText = pushTextInstance(
task.blockedSegment.chunks,
'' + node,
request.responseState,
segment.lastPushedText,
);
return;
}
if (__DEV__) {
if (typeof node === 'function') {
console.error(
'Functions are not valid as a React child. This may happen if ' +
'you return a Component instead of <Component /> from render. ' +
'Or maybe you meant to call this function rather than return it.',
);
}
}
}
function renderChildrenArray(
request: Request,
task: Task,
children: Array<any>,
) {
const totalChildren = children.length;
for (let i = 0; i < totalChildren; i++) {
const prevTreeContext = task.treeContext;
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i);
try {
renderNode(request, task, children[i]);
} finally {
task.treeContext = prevTreeContext;
}
}
}
function spawnNewSuspendedTask(
request: Request,
task: Task,
thenableState: ThenableState | null,
x: Wakeable,
): void {
const segment = task.blockedSegment;
const insertionIndex = segment.chunks.length;
const newSegment = createPendingSegment(
request,
insertionIndex,
null,
segment.formatContext,
segment.lastPushedText,
true,
);
segment.children.push(newSegment);
segment.lastPushedText = false;
const newTask = createTask(
request,
thenableState,
task.node,
task.blockedBoundary,
newSegment,
task.abortSet,
task.legacyContext,
task.context,
task.treeContext,
);
if (__DEV__) {
if (task.componentStack !== null) {
newTask.componentStack = task.componentStack.parent;
}
}
const ping = newTask.ping;
x.then(ping, ping);
}
function renderNode(request: Request, task: Task, node: ReactNodeList): void {
const previousFormatContext = task.blockedSegment.formatContext;
const previousLegacyContext = task.legacyContext;
const previousContext = task.context;
let previousComponentStack = null;
if (__DEV__) {
previousComponentStack = task.componentStack;
}
try {
return renderNodeDestructive(request, task, null, node);
} catch (thrownValue) {
resetHooksState();
const x =
thrownValue === SuspenseException
?
getSuspendedThenable()
: thrownValue;
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
const wakeable: Wakeable = (x: any);
const thenableState = getThenableStateAfterSuspending();
spawnNewSuspendedTask(request, task, thenableState, wakeable);
task.blockedSegment.formatContext = previousFormatContext;
task.legacyContext = previousLegacyContext;
task.context = previousContext;
switchContext(previousContext);
if (__DEV__) {
task.componentStack = previousComponentStack;
}
return;
} else {
task.blockedSegment.formatContext = previousFormatContext;
task.legacyContext = previousLegacyContext;
task.context = previousContext;
switchContext(previousContext);
if (__DEV__) {
task.componentStack = previousComponentStack;
}
throw x;
}
}
}
function erroredTask(
request: Request,
boundary: Root | SuspenseBoundary,
segment: Segment,
error: mixed,
) {
const errorDigest = logRecoverableError(request, error);
if (boundary === null) {
fatalError(request, error);
} else {
boundary.pendingTasks--;
if (!boundary.forceClientRender) {
boundary.forceClientRender = true;
boundary.errorDigest = errorDigest;
if (__DEV__) {
captureBoundaryErrorDetailsDev(boundary, error);
}
if (boundary.parentFlushed) {
request.clientRenderedBoundaries.push(boundary);
}
}
}
request.allPendingTasks--;
if (request.allPendingTasks === 0) {
const onAllReady = request.onAllReady;
onAllReady();
}
}
function abortTaskSoft(this: Request, task: Task): void {
const request: Request = this;
const boundary = task.blockedBoundary;
const segment = task.blockedSegment;
segment.status = ABORTED;
finishedTask(request, boundary, segment);
}
function abortTask(task: Task, request: Request, error: mixed): void {
const boundary = task.blockedBoundary;
const segment = task.blockedSegment;
segment.status = ABORTED;
if (boundary === null) {
request.allPendingTasks--;
if (request.status !== CLOSING && request.status !== CLOSED) {
logRecoverableError(request, error);
fatalError(request, error);
}
} else {
boundary.pendingTasks--;
if (!boundary.forceClientRender) {
boundary.forceClientRender = true;
boundary.errorDigest = request.onError(error);
if (__DEV__) {
const errorPrefix =
'The server did not finish this Suspense boundary: ';
let errorMessage;
if (error && typeof error.message === 'string') {
errorMessage = errorPrefix + error.message;
} else {
errorMessage = errorPrefix + String(error);
}
const previousTaskInDev = currentTaskInDEV;
currentTaskInDEV = task;
try {
captureBoundaryErrorDetailsDev(boundary, errorMessage);
} finally {
currentTaskInDEV = previousTaskInDev;
}
}
if (boundary.parentFlushed) {
request.clientRenderedBoundaries.push(boundary);
}
}
boundary.fallbackAbortableTasks.forEach(fallbackTask =>
abortTask(fallbackTask, request, error),
);
boundary.fallbackAbortableTasks.clear();
request.allPendingTasks--;
if (request.allPendingTasks === 0) {
const onAllReady = request.onAllReady;
onAllReady();
}
}
}
function queueCompletedSegment(
boundary: SuspenseBoundary,
segment: Segment,
): void {
if (
segment.chunks.length === 0 &&
segment.children.length === 1 &&
segment.children[0].boundary === null
) {
const childSegment = segment.children[0];
childSegment.id = segment.id;
childSegment.parentFlushed = true;
if (childSegment.status === COMPLETED) {
queueCompletedSegment(boundary, childSegment);
}
} else {
const completedSegments = boundary.completedSegments;
completedSegments.push(segment);
}
}
function finishedTask(
request: Request,
boundary: Root | SuspenseBoundary,
segment: Segment,
) {
if (boundary === null) {
if (segment.parentFlushed) {
if (request.completedRootSegment !== null) {
throw new Error(
'There can only be one root segment. This is a bug in React.',
);
}
request.completedRootSegment = segment;
}
request.pendingRootTasks--;
if (request.pendingRootTasks === 0) {
request.onShellError = noop;
const onShellReady = request.onShellReady;
onShellReady();
}
} else {
boundary.pendingTasks--;
if (boundary.forceClientRender) {
} else if (boundary.pendingTasks === 0) {
if (segment.parentFlushed) {
if (segment.status === COMPLETED) {
queueCompletedSegment(boundary, segment);
}
}
if (enableFloat) {
hoistCompletedBoundaryResources(request, boundary);
}
if (boundary.parentFlushed) {
request.completedBoundaries.push(boundary);
}
boundary.fallbackAbortableTasks.forEach(abortTaskSoft, request);
boundary.fallbackAbortableTasks.clear();
} else {
if (segment.parentFlushed) {
if (segment.status === COMPLETED) {
queueCompletedSegment(boundary, segment);
const completedSegments = boundary.completedSegments;
if (completedSegments.length === 1) {
if (boundary.parentFlushed) {
request.partialBoundaries.push(boundary);
}
}
}
}
}
}
request.allPendingTasks--;
if (request.allPendingTasks === 0) {
const onAllReady = request.onAllReady;
onAllReady();
}
}
function retryTask(request: Request, task: Task): void {
if (enableFloat) {
const blockedBoundary = task.blockedBoundary;
setCurrentlyRenderingBoundaryResourcesTarget(
request.resources,
blockedBoundary ? blockedBoundary.resources : null,
);
}
const segment = task.blockedSegment;
if (segment.status !== PENDING) {
return;
}
switchContext(task.context);
let prevTaskInDEV = null;
if (__DEV__) {
prevTaskInDEV = currentTaskInDEV;
currentTaskInDEV = task;
}
try {
const prevThenableState = task.thenableState;
task.thenableState = null;
renderNodeDestructive(request, task, prevThenableState, task.node);
pushSegmentFinale(
segment.chunks,
request.responseState,
segment.lastPushedText,
segment.textEmbedded,
);
task.abortSet.delete(task);
segment.status = COMPLETED;
finishedTask(request, task.blockedBoundary, segment);
} catch (thrownValue) {
resetHooksState();
const x =
thrownValue === SuspenseException
?
getSuspendedThenable()
: thrownValue;
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
const ping = task.ping;
x.then(ping, ping);
task.thenableState = getThenableStateAfterSuspending();
} else {
task.abortSet.delete(task);
segment.status = ERRORED;
erroredTask(request, task.blockedBoundary, segment, x);
}
} finally {
if (enableFloat) {
setCurrentlyRenderingBoundaryResourcesTarget(request.resources, null);
}
if (__DEV__) {
currentTaskInDEV = prevTaskInDEV;
}
}
}
export function performWork(request: Request): void {
if (request.status === CLOSED) {
return;
}
const prevContext = getActiveContext();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = HooksDispatcher;
let prevCacheDispatcher;
if (enableCache) {
prevCacheDispatcher = ReactCurrentCache.current;
ReactCurrentCache.current = DefaultCacheDispatcher;
}
const previousHostDispatcher = prepareToRender(request.resources);
let prevGetCurrentStackImpl;
if (__DEV__) {
prevGetCurrentStackImpl = ReactDebugCurrentFrame.getCurrentStack;
ReactDebugCurrentFrame.getCurrentStack = getCurrentStackInDEV;
}
const prevResponseState = currentResponseState;
setCurrentResponseState(request.responseState);
try {
const pingedTasks = request.pingedTasks;
let i;
for (i = 0; i < pingedTasks.length; i++) {
const task = pingedTasks[i];
retryTask(request, task);
}
pingedTasks.splice(0, i);
if (request.destination !== null) {
flushCompletedQueues(request, request.destination);
}
} catch (error) {
logRecoverableError(request, error);
fatalError(request, error);
} finally {
setCurrentResponseState(prevResponseState);
ReactCurrentDispatcher.current = prevDispatcher;
if (enableCache) {
ReactCurrentCache.current = prevCacheDispatcher;
}
cleanupAfterRender(previousHostDispatcher);
if (__DEV__) {
ReactDebugCurrentFrame.getCurrentStack = prevGetCurrentStackImpl;
}
if (prevDispatcher === HooksDispatcher) {
switchContext(prevContext);
}
}
}
function flushSubtree(
request: Request,
destination: Destination,
segment: Segment,
): boolean {
segment.parentFlushed = true;
switch (segment.status) {
case PENDING: {
const segmentID = (segment.id = request.nextSegmentId++);
segment.lastPushedText = false;
segment.textEmbedded = false;
return writePlaceholder(destination, request.responseState, segmentID);
}
case COMPLETED: {
segment.status = FLUSHED;
let r = true;
const chunks = segment.chunks;
let chunkIdx = 0;
const children = segment.children;
for (let childIdx = 0; childIdx < children.length; childIdx++) {
const nextChild = children[childIdx];
for (; chunkIdx < nextChild.index; chunkIdx++) {
writeChunk(destination, chunks[chunkIdx]);
}
r = flushSegment(request, destination, nextChild);
}
for (; chunkIdx < chunks.length - 1; chunkIdx++) {
writeChunk(destination, chunks[chunkIdx]);
}
if (chunkIdx < chunks.length) {
r = writeChunkAndReturn(destination, chunks[chunkIdx]);
}
return r;
}
default: {
throw new Error(
'Aborted, errored or already flushed boundaries should not be flushed again. This is a bug in React.',
);
}
}
}
function flushSegment(
request: Request,
destination: Destination,
segment: Segment,
): boolean {
const boundary = segment.boundary;
if (boundary === null) {
return flushSubtree(request, destination, segment);
}
boundary.parentFlushed = true;
if (boundary.forceClientRender) {
writeStartClientRenderedSuspenseBoundary(
destination,
request.responseState,
boundary.errorDigest,
boundary.errorMessage,
boundary.errorComponentStack,
);
flushSubtree(request, destination, segment);
return writeEndClientRenderedSuspenseBoundary(
destination,
request.responseState,
);
} else if (boundary.pendingTasks > 0) {
boundary.rootSegmentID = request.nextSegmentId++;
if (boundary.completedSegments.length > 0) {
request.partialBoundaries.push(boundary);
}
const id = (boundary.id = assignSuspenseBoundaryID(request.responseState));
writeStartPendingSuspenseBoundary(destination, request.responseState, id);
flushSubtree(request, destination, segment);
return writeEndPendingSuspenseBoundary(destination, request.responseState);
} else if (boundary.byteSize > request.progressiveChunkSize) {
boundary.rootSegmentID = request.nextSegmentId++;
request.completedBoundaries.push(boundary);
writeStartPendingSuspenseBoundary(
destination,
request.responseState,
boundary.id,
);
flushSubtree(request, destination, segment);
return writeEndPendingSuspenseBoundary(destination, request.responseState);
} else {
if (enableFloat) {
hoistResources(request.resources, boundary.resources);
}
writeStartCompletedSuspenseBoundary(destination, request.responseState);
const completedSegments = boundary.completedSegments;
if (completedSegments.length !== 1) {
throw new Error(
'A previously unvisited boundary must have exactly one root segment. This is a bug in React.',
);
}
const contentSegment = completedSegments[0];
flushSegment(request, destination, contentSegment);
return writeEndCompletedSuspenseBoundary(
destination,
request.responseState,
);
}
}
function flushInitialResources(
destination: Destination,
resources: Resources,
responseState: ResponseState,
willFlushAllSegments: boolean,
): void {
writeInitialResources(
destination,
resources,
responseState,
willFlushAllSegments,
);
}
function flushImmediateResources(
destination: Destination,
request: Request,
): void {
writeImmediateResources(
destination,
request.resources,
request.responseState,
);
}
function flushClientRenderedBoundary(
request: Request,
destination: Destination,
boundary: SuspenseBoundary,
): boolean {
return writeClientRenderBoundaryInstruction(
destination,
request.responseState,
boundary.id,
boundary.errorDigest,
boundary.errorMessage,
boundary.errorComponentStack,
);
}
function flushSegmentContainer(
request: Request,
destination: Destination,
segment: Segment,
): boolean {
writeStartSegment(
destination,
request.responseState,
segment.formatContext,
segment.id,
);
flushSegment(request, destination, segment);
return writeEndSegment(destination, segment.formatContext);
}
function flushCompletedBoundary(
request: Request,
destination: Destination,
boundary: SuspenseBoundary,
): boolean {
if (enableFloat) {
setCurrentlyRenderingBoundaryResourcesTarget(
request.resources,
boundary.resources,
);
}
const completedSegments = boundary.completedSegments;
let i = 0;
for (; i < completedSegments.length; i++) {
const segment = completedSegments[i];
flushPartiallyCompletedSegment(request, destination, boundary, segment);
}
completedSegments.length = 0;
return writeCompletedBoundaryInstruction(
destination,
request.responseState,
boundary.id,
boundary.rootSegmentID,
boundary.resources,
);
}
function flushPartialBoundary(
request: Request,
destination: Destination,
boundary: SuspenseBoundary,
): boolean {
if (enableFloat) {
setCurrentlyRenderingBoundaryResourcesTarget(
request.resources,
boundary.resources,
);
}
const completedSegments = boundary.completedSegments;
let i = 0;
for (; i < completedSegments.length; i++) {
const segment = completedSegments[i];
if (
!flushPartiallyCompletedSegment(request, destination, boundary, segment)
) {
i++;
completedSegments.splice(0, i);
return false;
}
}
completedSegments.splice(0, i);
return true;
}
function flushPartiallyCompletedSegment(
request: Request,
destination: Destination,
boundary: SuspenseBoundary,
segment: Segment,
): boolean {
if (segment.status === FLUSHED) {
return true;
}
const segmentID = segment.id;
if (segmentID === -1) {
const rootSegmentID = (segment.id = boundary.rootSegmentID);
if (rootSegmentID === -1) {
throw new Error(
'A root segment ID must have been assigned by now. This is a bug in React.',
);
}
return flushSegmentContainer(request, destination, segment);
} else {
flushSegmentContainer(request, destination, segment);
return writeCompletedSegmentInstruction(
destination,
request.responseState,
segmentID,
);
}
}
function flushCompletedQueues(
request: Request,
destination: Destination,
): void {
beginWriting(destination);
try {
let i;
const completedRootSegment = request.completedRootSegment;
if (completedRootSegment !== null) {
if (request.pendingRootTasks === 0) {
if (enableFloat) {
const preamble = request.preamble;
for (i = 0; i < preamble.length; i++) {
writeChunk(destination, preamble[i]);
}
flushInitialResources(
destination,
request.resources,
request.responseState,
request.allPendingTasks === 0,
);
}
flushSegment(request, destination, completedRootSegment);
request.completedRootSegment = null;
writeCompletedRoot(destination, request.responseState);
} else {
return;
}
} else if (enableFloat) {
flushImmediateResources(destination, request);
}
const clientRenderedBoundaries = request.clientRenderedBoundaries;
for (i = 0; i < clientRenderedBoundaries.length; i++) {
const boundary = clientRenderedBoundaries[i];
if (!flushClientRenderedBoundary(request, destination, boundary)) {
request.destination = null;
i++;
clientRenderedBoundaries.splice(0, i);
return;
}
}
clientRenderedBoundaries.splice(0, i);
const completedBoundaries = request.completedBoundaries;
for (i = 0; i < completedBoundaries.length; i++) {
const boundary = completedBoundaries[i];
if (!flushCompletedBoundary(request, destination, boundary)) {
request.destination = null;
i++;
completedBoundaries.splice(0, i);
return;
}
}
completedBoundaries.splice(0, i);
completeWriting(destination);
beginWriting(destination);
const partialBoundaries = request.partialBoundaries;
for (i = 0; i < partialBoundaries.length; i++) {
const boundary = partialBoundaries[i];
if (!flushPartialBoundary(request, destination, boundary)) {
request.destination = null;
i++;
partialBoundaries.splice(0, i);
return;
}
}
partialBoundaries.splice(0, i);
const largeBoundaries = request.completedBoundaries;
for (i = 0; i < largeBoundaries.length; i++) {
const boundary = largeBoundaries[i];
if (!flushCompletedBoundary(request, destination, boundary)) {
request.destination = null;
i++;
largeBoundaries.splice(0, i);
return;
}
}
largeBoundaries.splice(0, i);
} finally {
if (
request.allPendingTasks === 0 &&
request.pingedTasks.length === 0 &&
request.clientRenderedBoundaries.length === 0 &&
request.completedBoundaries.length === 0
) {
if (enableFloat) {
const postamble = request.postamble;
for (let i = 0; i < postamble.length; i++) {
writeChunk(destination, postamble[i]);
}
}
completeWriting(destination);
flushBuffered(destination);
if (__DEV__) {
if (request.abortableTasks.size !== 0) {
console.error(
'There was still abortable task at the root when we closed. This is a bug in React.',
);
}
}
close(destination);
} else {
completeWriting(destination);
flushBuffered(destination);
}
}
}
export function startWork(request: Request): void {
scheduleWork(() => performWork(request));
}
export function startFlowing(request: Request, destination: Destination): void {
if (request.status === CLOSING) {
request.status = CLOSED;
closeWithError(destination, request.fatalError);
return;
}
if (request.status === CLOSED) {
return;
}
if (request.destination !== null) {
return;
}
request.destination = destination;
try {
flushCompletedQueues(request, destination);
} catch (error) {
logRecoverableError(request, error);
fatalError(request, error);
}
}
export function abort(request: Request, reason: mixed): void {
try {
const abortableTasks = request.abortableTasks;
if (abortableTasks.size > 0) {
const error =
reason === undefined
? new Error('The render was aborted by the server without a reason.')
: reason;
abortableTasks.forEach(task => abortTask(task, request, error));
abortableTasks.clear();
}
if (request.destination !== null) {
flushCompletedQueues(request, request.destination);
}
} catch (error) {
logRecoverableError(request, error);
fatalError(request, error);
}
}