/** * Copyright (c) Facebook, Inc. and its 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} from 'shared/ReactTypes'; import { checkHtmlStringCoercion, checkCSSPropertyStringCoercion, checkAttributeStringCoercion, } from 'shared/CheckStringCoercion'; import {Children} from 'react'; import { enableFilterEmptyStringAttributesDOM, enableCustomElementPropertySupport, } from 'shared/ReactFeatureFlags'; import type { Destination, Chunk, PrecomputedChunk, } from 'react-server/src/ReactServerStreamConfig'; import { writeChunk, writeChunkAndReturn, stringToChunk, stringToPrecomputedChunk, } from 'react-server/src/ReactServerStreamConfig'; import { getPropertyInfo, isAttributeNameSafe, BOOLEAN, OVERLOADED_BOOLEAN, NUMERIC, POSITIVE_NUMERIC, } from '../shared/DOMProperty'; import {isUnitlessNumber} from '../shared/CSSProperty'; 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'; // 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; // Per response, global state that is not contextual to the rendering subtree. export type ResponseState = { bootstrapChunks: Array, startInlineScript: PrecomputedChunk, placeholderPrefix: PrecomputedChunk, segmentPrefix: PrecomputedChunk, boundaryPrefix: string, idPrefix: string, nextSuspenseID: number, sentCompleteSegmentFunction: boolean, sentCompleteBoundaryFunction: boolean, sentClientRenderFunction: boolean, // We allow the legacy renderer to extend this object. ... }; const startInlineScript = stringToPrecomputedChunk(''); const startScriptSrc = stringToPrecomputedChunk(''); // Allows us to keep track of what we've already written so we can refer back to it. export function createResponseState( identifierPrefix: string | void, nonce: string | void, bootstrapScriptContent: string | void, bootstrapScripts: Array | void, bootstrapModules: Array | void, ): ResponseState { const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix; const inlineScriptWithNonce = nonce === undefined ? startInlineScript : stringToPrecomputedChunk( ''); export function writeCompletedSegmentInstruction( destination: Destination, responseState: ResponseState, contentSegmentID: number, ): boolean { writeChunk(destination, responseState.startInlineScript); if (!responseState.sentCompleteSegmentFunction) { // The first time we write this, we'll need to include the full implementation. responseState.sentCompleteSegmentFunction = true; writeChunk(destination, completeSegmentScript1Full); } else { // Future calls can just reuse the same function. writeChunk(destination, completeSegmentScript1Partial); } writeChunk(destination, responseState.segmentPrefix); const formattedID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, formattedID); writeChunk(destination, completeSegmentScript2); writeChunk(destination, responseState.placeholderPrefix); writeChunk(destination, formattedID); return writeChunkAndReturn(destination, completeSegmentScript3); } const completeBoundaryScript1Full = stringToPrecomputedChunk( completeBoundaryFunction + ';$RC("', ); const completeBoundaryScript1Partial = stringToPrecomputedChunk('$RC("'); const completeBoundaryScript2 = stringToPrecomputedChunk('","'); const completeBoundaryScript3 = stringToPrecomputedChunk('")'); export function writeCompletedBoundaryInstruction( destination: Destination, responseState: ResponseState, boundaryID: SuspenseBoundaryID, contentSegmentID: number, ): boolean { writeChunk(destination, responseState.startInlineScript); if (!responseState.sentCompleteBoundaryFunction) { // The first time we write this, we'll need to include the full implementation. responseState.sentCompleteBoundaryFunction = true; writeChunk(destination, completeBoundaryScript1Full); } else { // Future calls can just reuse the same function. writeChunk(destination, completeBoundaryScript1Partial); } if (boundaryID === null) { throw new Error( 'An ID must have been assigned before we can complete the boundary.', ); } const formattedContentID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, boundaryID); writeChunk(destination, completeBoundaryScript2); writeChunk(destination, responseState.segmentPrefix); writeChunk(destination, formattedContentID); return writeChunkAndReturn(destination, completeBoundaryScript3); } const clientRenderScript1Full = stringToPrecomputedChunk( clientRenderFunction + ';$RX("', ); const clientRenderScript1Partial = stringToPrecomputedChunk('$RX("'); const clientRenderScript2 = stringToPrecomputedChunk('")'); export function writeClientRenderBoundaryInstruction( destination: Destination, responseState: ResponseState, boundaryID: SuspenseBoundaryID, ): boolean { writeChunk(destination, responseState.startInlineScript); if (!responseState.sentClientRenderFunction) { // The first time we write this, we'll need to include the full implementation. responseState.sentClientRenderFunction = true; writeChunk(destination, clientRenderScript1Full); } else { // Future calls can just reuse the same function. writeChunk(destination, clientRenderScript1Partial); } if (boundaryID === null) { throw new Error( 'An ID must have been assigned before we can complete the boundary.', ); } writeChunk(destination, boundaryID); return writeChunkAndReturn(destination, clientRenderScript2); }