/** * 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 {ReactNodeList, ReactCustomFormAction} from 'shared/ReactTypes'; import type { PrefetchDNSOptions, PreconnectOptions, PreloadOptions, PreloadModuleOptions, PreinitOptions, PreinitModuleOptions, } from 'react-dom/src/shared/ReactDOMTypes'; import { checkHtmlStringCoercion, checkCSSPropertyStringCoercion, checkAttributeStringCoercion, } from 'shared/CheckStringCoercion'; import {Children} from 'react'; import { enableFilterEmptyStringAttributesDOM, enableCustomElementPropertySupport, enableFloat, enableFormActions, enableFizzExternalRuntime, } from 'shared/ReactFeatureFlags'; import type { Destination, Chunk, PrecomputedChunk, } from 'react-server/src/ReactServerStreamConfig'; import type {FormStatus} from '../shared/ReactDOMFormActions'; import { writeChunk, writeChunkAndReturn, stringToChunk, stringToPrecomputedChunk, clonePrecomputedChunk, } from 'react-server/src/ReactServerStreamConfig'; import { resolveRequest, getResumableState, flushResources, } from 'react-server/src/ReactFizzServer'; import isAttributeNameSafe from '../shared/isAttributeNameSafe'; import isUnitlessNumber from '../shared/isUnitlessNumber'; import getAttributeAlias from '../shared/getAttributeAlias'; import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes'; import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook'; import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook'; import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook'; import warnValidStyle from '../shared/warnValidStyle'; import escapeTextForBrowser from './escapeTextForBrowser'; import hyphenateStyleName from '../shared/hyphenateStyleName'; import hasOwnProperty from 'shared/hasOwnProperty'; import sanitizeURL from '../shared/sanitizeURL'; import isArray from 'shared/isArray'; import { clientRenderBoundary as clientRenderFunction, completeBoundary as completeBoundaryFunction, completeBoundaryWithStyles as styleInsertionFunction, completeSegment as completeSegmentFunction, formReplaying as formReplayingRuntime, } from './fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings'; import { getValueDescriptorExpectingObjectForWarning, getValueDescriptorExpectingEnumForWarning, } from '../shared/ReactDOMResourceValidation'; import {NotPending} from '../shared/ReactDOMFormActions'; import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; const ReactDOMServerDispatcher = { prefetchDNS, preconnect, preload, preloadModule, preinit, preinitModule, }; export function prepareHostDispatcher() { ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher; } // Used to distinguish these contexts from ones used in other renderers. // E.g. this can be used to distinguish legacy renderers from this modern one. export const isPrimaryRenderer = true; export type StreamingFormat = 0 | 1; const ScriptStreamingFormat: StreamingFormat = 0; const DataStreamingFormat: StreamingFormat = 1; export type InstructionState = number; const NothingSent /* */ = 0b00000; const SentCompleteSegmentFunction /* */ = 0b00001; const SentCompleteBoundaryFunction /* */ = 0b00010; const SentClientRenderFunction /* */ = 0b00100; const SentStyleInsertionFunction /* */ = 0b01000; const SentFormReplayingRuntime /* */ = 0b10000; // Per request, global state that is not contextual to the rendering subtree. // This cannot be resumed and therefore should only contain things that are // temporary working state or are never used in the prerender pass. export type RenderState = { // These can be recreated from resumable state. placeholderPrefix: PrecomputedChunk, segmentPrefix: PrecomputedChunk, boundaryPrefix: string, // inline script streaming format, unused if using external runtime / data startInlineScript: PrecomputedChunk, // the preamble must always flush before resuming, so all these chunks must // be null or empty when resuming. // preamble chunks htmlChunks: null | Array, headChunks: null | Array, // Hoistable chunks charsetChunks: Array, preconnectChunks: Array, preloadChunks: Array, hoistableChunks: Array, // Module-global-like reference for current boundary resources boundaryResources: ?BoundaryResources, // Module-global-like reference for flushing/hoisting state of style resources // We need to track whether the current request has flushed any style resources // without sending an instruction to hoist them. we do that here stylesToHoist: boolean, // We allow the legacy renderer to extend this object. ... }; // Per response, global state that is not contextual to the rendering subtree. // This is resumable and therefore should be serializable. export type ResumableState = { // external runtime script chunks externalRuntimeScript: null | ExternalRuntimeScript, // TODO: Move to a serializable format bootstrapChunks: Array, // TODO: Move to a serializable format. idPrefix: string, nextSuspenseID: number, streamingFormat: StreamingFormat, // state for script streaming format, unused if using external runtime / data instructions: InstructionState, // postamble state hasBody: boolean, hasHtml: boolean, // Resources // Request local cache preloadsMap: Map, preconnectsMap: Map, stylesMap: Map, scriptsMap: Map, // Flushing queues for Resource dependencies preconnects: Set, fontPreloads: Set, highImagePreloads: Set, // usedImagePreloads: Set, precedences: Map>, stylePrecedences: Map, bootstrapScripts: Set, scripts: Set, bulkPreloads: Set, }; const dataElementQuotedEnd = stringToPrecomputedChunk('">'); const startInlineScript = stringToPrecomputedChunk(''); const startScriptSrc = stringToPrecomputedChunk(''); /** * This escaping function is designed to work with bootstrapScriptContent only. * because we know we are escaping the entire script. We can avoid for instance * escaping html comment string sequences that are valid javascript as well because * if there are no sebsequent '); const completeSegmentData1 = stringToPrecomputedChunk( '