mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Uses `&` for Activity as opposed to `$` for Suspense. This will be used to delimitate which nodes we can skip hydrating. This isn't used on the client yet. It's just a noop on the client because it's just an unknown comment. This just adds the SSR parts.
300 lines
8.5 KiB
JavaScript
300 lines
8.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 {
|
|
RenderState as BaseRenderState,
|
|
ResumableState,
|
|
StyleQueue,
|
|
Resource,
|
|
HeadersDescriptor,
|
|
PreambleState,
|
|
} from './ReactFizzConfigDOM';
|
|
|
|
import {
|
|
createRenderState as createRenderStateImpl,
|
|
pushTextInstance as pushTextInstanceImpl,
|
|
pushSegmentFinale as pushSegmentFinaleImpl,
|
|
pushStartActivityBoundary as pushStartActivityBoundaryImpl,
|
|
pushEndActivityBoundary as pushEndActivityBoundaryImpl,
|
|
writeStartCompletedSuspenseBoundary as writeStartCompletedSuspenseBoundaryImpl,
|
|
writeStartClientRenderedSuspenseBoundary as writeStartClientRenderedSuspenseBoundaryImpl,
|
|
writeEndCompletedSuspenseBoundary as writeEndCompletedSuspenseBoundaryImpl,
|
|
writeEndClientRenderedSuspenseBoundary as writeEndClientRenderedSuspenseBoundaryImpl,
|
|
} from './ReactFizzConfigDOM';
|
|
|
|
import type {
|
|
Destination,
|
|
Chunk,
|
|
PrecomputedChunk,
|
|
} from 'react-server/src/ReactServerStreamConfig';
|
|
|
|
import type {FormStatus} from '../shared/ReactDOMFormActions';
|
|
|
|
import {NotPending} from '../shared/ReactDOMFormActions';
|
|
|
|
export const isPrimaryRenderer = false;
|
|
|
|
export type RenderState = {
|
|
// Keep this in sync with ReactFizzConfigDOM
|
|
placeholderPrefix: PrecomputedChunk,
|
|
segmentPrefix: PrecomputedChunk,
|
|
boundaryPrefix: PrecomputedChunk,
|
|
startInlineScript: PrecomputedChunk,
|
|
preamble: PreambleState,
|
|
externalRuntimeScript: null | any,
|
|
bootstrapChunks: Array<Chunk | PrecomputedChunk>,
|
|
importMapChunks: Array<Chunk | PrecomputedChunk>,
|
|
onHeaders: void | ((headers: HeadersDescriptor) => void),
|
|
headers: null | {
|
|
preconnects: string,
|
|
fontPreloads: string,
|
|
highImagePreloads: string,
|
|
remainingCapacity: number,
|
|
},
|
|
resets: BaseRenderState['resets'],
|
|
charsetChunks: Array<Chunk | PrecomputedChunk>,
|
|
viewportChunks: Array<Chunk | PrecomputedChunk>,
|
|
hoistableChunks: Array<Chunk | PrecomputedChunk>,
|
|
preconnects: Set<Resource>,
|
|
fontPreloads: Set<Resource>,
|
|
highImagePreloads: Set<Resource>,
|
|
// usedImagePreloads: Set<Resource>,
|
|
styles: Map<string, StyleQueue>,
|
|
bootstrapScripts: Set<Resource>,
|
|
scripts: Set<Resource>,
|
|
bulkPreloads: Set<Resource>,
|
|
preloads: {
|
|
images: Map<string, Resource>,
|
|
stylesheets: Map<string, Resource>,
|
|
scripts: Map<string, Resource>,
|
|
moduleScripts: Map<string, Resource>,
|
|
},
|
|
stylesToHoist: boolean,
|
|
// This is an extra field for the legacy renderer
|
|
generateStaticMarkup: boolean,
|
|
};
|
|
|
|
export function createRenderState(
|
|
resumableState: ResumableState,
|
|
generateStaticMarkup: boolean,
|
|
): RenderState {
|
|
const renderState = createRenderStateImpl(
|
|
resumableState,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
);
|
|
return {
|
|
// Keep this in sync with ReactFizzConfigDOM
|
|
placeholderPrefix: renderState.placeholderPrefix,
|
|
segmentPrefix: renderState.segmentPrefix,
|
|
boundaryPrefix: renderState.boundaryPrefix,
|
|
startInlineScript: renderState.startInlineScript,
|
|
preamble: renderState.preamble,
|
|
externalRuntimeScript: renderState.externalRuntimeScript,
|
|
bootstrapChunks: renderState.bootstrapChunks,
|
|
importMapChunks: renderState.importMapChunks,
|
|
onHeaders: renderState.onHeaders,
|
|
headers: renderState.headers,
|
|
resets: renderState.resets,
|
|
charsetChunks: renderState.charsetChunks,
|
|
viewportChunks: renderState.viewportChunks,
|
|
hoistableChunks: renderState.hoistableChunks,
|
|
preconnects: renderState.preconnects,
|
|
fontPreloads: renderState.fontPreloads,
|
|
highImagePreloads: renderState.highImagePreloads,
|
|
// usedImagePreloads: renderState.usedImagePreloads,
|
|
styles: renderState.styles,
|
|
bootstrapScripts: renderState.bootstrapScripts,
|
|
scripts: renderState.scripts,
|
|
bulkPreloads: renderState.bulkPreloads,
|
|
preloads: renderState.preloads,
|
|
stylesToHoist: renderState.stylesToHoist,
|
|
|
|
// This is an extra field for the legacy renderer
|
|
generateStaticMarkup,
|
|
};
|
|
}
|
|
|
|
import {
|
|
stringToChunk,
|
|
stringToPrecomputedChunk,
|
|
} from 'react-server/src/ReactServerStreamConfig';
|
|
|
|
// this chunk is empty on purpose because we do not want to emit the DOCTYPE in legacy mode
|
|
export const doctypeChunk: PrecomputedChunk = stringToPrecomputedChunk('');
|
|
|
|
export type {
|
|
ResumableState,
|
|
HoistableState,
|
|
PreambleState,
|
|
FormatContext,
|
|
} from './ReactFizzConfigDOM';
|
|
|
|
export {
|
|
getChildFormatContext,
|
|
makeId,
|
|
pushStartInstance,
|
|
pushEndInstance,
|
|
pushFormStateMarkerIsMatching,
|
|
pushFormStateMarkerIsNotMatching,
|
|
writeStartSegment,
|
|
writeEndSegment,
|
|
writeCompletedSegmentInstruction,
|
|
writeCompletedBoundaryInstruction,
|
|
writeClientRenderBoundaryInstruction,
|
|
writeStartPendingSuspenseBoundary,
|
|
writeEndPendingSuspenseBoundary,
|
|
writeHoistablesForBoundary,
|
|
writePlaceholder,
|
|
writeCompletedRoot,
|
|
createRootFormatContext,
|
|
createResumableState,
|
|
createPreambleState,
|
|
createHoistableState,
|
|
writePreambleStart,
|
|
writePreambleEnd,
|
|
writeHoistables,
|
|
writePostamble,
|
|
hoistHoistables,
|
|
resetResumableState,
|
|
completeResumableState,
|
|
emitEarlyPreloads,
|
|
supportsClientAPIs,
|
|
canHavePreamble,
|
|
hoistPreambleState,
|
|
isPreambleReady,
|
|
isPreambleContext,
|
|
} from './ReactFizzConfigDOM';
|
|
|
|
import escapeTextForBrowser from './escapeTextForBrowser';
|
|
|
|
export function pushTextInstance(
|
|
target: Array<Chunk | PrecomputedChunk>,
|
|
text: string,
|
|
renderState: RenderState,
|
|
textEmbedded: boolean,
|
|
): boolean {
|
|
if (renderState.generateStaticMarkup) {
|
|
target.push(stringToChunk(escapeTextForBrowser(text)));
|
|
return false;
|
|
} else {
|
|
return pushTextInstanceImpl(target, text, renderState, textEmbedded);
|
|
}
|
|
}
|
|
|
|
export function pushSegmentFinale(
|
|
target: Array<Chunk | PrecomputedChunk>,
|
|
renderState: RenderState,
|
|
lastPushedText: boolean,
|
|
textEmbedded: boolean,
|
|
): void {
|
|
if (renderState.generateStaticMarkup) {
|
|
return;
|
|
} else {
|
|
return pushSegmentFinaleImpl(
|
|
target,
|
|
renderState,
|
|
lastPushedText,
|
|
textEmbedded,
|
|
);
|
|
}
|
|
}
|
|
|
|
export function pushStartActivityBoundary(
|
|
target: Array<Chunk | PrecomputedChunk>,
|
|
renderState: RenderState,
|
|
): void {
|
|
if (renderState.generateStaticMarkup) {
|
|
// A completed boundary is done and doesn't need a representation in the HTML
|
|
// if we're not going to be hydrating it.
|
|
return;
|
|
}
|
|
pushStartActivityBoundaryImpl(target, renderState);
|
|
}
|
|
|
|
export function pushEndActivityBoundary(
|
|
target: Array<Chunk | PrecomputedChunk>,
|
|
renderState: RenderState,
|
|
preambleState: null | PreambleState,
|
|
): void {
|
|
if (renderState.generateStaticMarkup) {
|
|
return;
|
|
}
|
|
pushEndActivityBoundaryImpl(target, renderState, preambleState);
|
|
}
|
|
|
|
export function writeStartCompletedSuspenseBoundary(
|
|
destination: Destination,
|
|
renderState: RenderState,
|
|
): boolean {
|
|
if (renderState.generateStaticMarkup) {
|
|
// A completed boundary is done and doesn't need a representation in the HTML
|
|
// if we're not going to be hydrating it.
|
|
return true;
|
|
}
|
|
return writeStartCompletedSuspenseBoundaryImpl(destination, renderState);
|
|
}
|
|
export function writeStartClientRenderedSuspenseBoundary(
|
|
destination: Destination,
|
|
renderState: RenderState,
|
|
// flushing these error arguments are not currently supported in this legacy streaming format.
|
|
errorDigest: ?string,
|
|
errorMessage: ?string,
|
|
errorStack: ?string,
|
|
errorComponentStack: ?string,
|
|
): boolean {
|
|
if (renderState.generateStaticMarkup) {
|
|
// A client rendered boundary is done and doesn't need a representation in the HTML
|
|
// since we'll never hydrate it. This is arguably an error in static generation.
|
|
return true;
|
|
}
|
|
return writeStartClientRenderedSuspenseBoundaryImpl(
|
|
destination,
|
|
renderState,
|
|
errorDigest,
|
|
errorMessage,
|
|
errorStack,
|
|
errorComponentStack,
|
|
);
|
|
}
|
|
export function writeEndCompletedSuspenseBoundary(
|
|
destination: Destination,
|
|
renderState: RenderState,
|
|
preambleState: null | PreambleState,
|
|
): boolean {
|
|
if (renderState.generateStaticMarkup) {
|
|
return true;
|
|
}
|
|
return writeEndCompletedSuspenseBoundaryImpl(
|
|
destination,
|
|
renderState,
|
|
preambleState,
|
|
);
|
|
}
|
|
export function writeEndClientRenderedSuspenseBoundary(
|
|
destination: Destination,
|
|
renderState: RenderState,
|
|
preambleState: null | PreambleState,
|
|
): boolean {
|
|
if (renderState.generateStaticMarkup) {
|
|
return true;
|
|
}
|
|
return writeEndClientRenderedSuspenseBoundaryImpl(
|
|
destination,
|
|
renderState,
|
|
preambleState,
|
|
);
|
|
}
|
|
|
|
export type TransitionStatus = FormStatus;
|
|
export const NotPendingTransition: TransitionStatus = NotPending;
|