import type {
ReactNodeList,
ReactCustomFormAction,
Thenable,
} from 'shared/ReactTypes';
import type {
CrossOriginEnum,
PreloadImplOptions,
PreloadModuleImplOptions,
PreinitStyleOptions,
PreinitScriptOptions,
PreinitModuleScriptOptions,
ImportMap,
} from 'react-dom/src/shared/ReactDOMTypes';
import {
checkHtmlStringCoercion,
checkCSSPropertyStringCoercion,
checkAttributeStringCoercion,
checkOptionStringCoercion,
} from 'shared/CheckStringCoercion';
import {Children} from 'react';
import {
enableFizzExternalRuntime,
enableSrcObject,
enableFizzBlockingRender,
enableViewTransition,
} from 'shared/ReactFeatureFlags';
import type {
Destination,
Chunk,
PrecomputedChunk,
} from 'react-server/src/ReactServerStreamConfig';
import type {FormStatus} from '../shared/ReactDOMFormActions';
import {
writeChunk,
writeChunkAndReturn,
stringToChunk,
stringToPrecomputedChunk,
readAsDataURL,
} from 'react-server/src/ReactServerStreamConfig';
import {
resolveRequest,
getResumableState,
getRenderState,
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 {getCrossOriginString} from '../shared/crossOriginStrings';
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,
completeBoundaryUpgradeToViewTransitions as upgradeToViewTransitionsInstruction,
completeBoundaryWithStyles as styleInsertionFunction,
completeSegment as completeSegmentFunction,
formReplaying as formReplayingRuntime,
markShellTime,
} from './fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings';
import {getValueDescriptorExpectingObjectForWarning} from '../shared/ReactDOMResourceValidation';
import {NotPending} from '../shared/ReactDOMFormActions';
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
const previousDispatcher =
ReactDOMSharedInternals.d;
ReactDOMSharedInternals.d = {
f : previousDispatcher.f ,
r : previousDispatcher.r ,
D : prefetchDNS,
C : preconnect,
L : preload,
m : preloadModule,
X : preinitScript,
S : preinitStyle,
M : preinitModuleScript,
};
export type HeadersDescriptor = {
Link?: string,
};
export const isPrimaryRenderer = true;
export const supportsClientAPIs = true;
export type StreamingFormat = 0 | 1;
const ScriptStreamingFormat: StreamingFormat = 0;
const DataStreamingFormat: StreamingFormat = 1;
export type InstructionState = number;
const NothingSent = 0b000000000;
const SentCompleteSegmentFunction = 0b000000001;
const SentCompleteBoundaryFunction = 0b000000010;
const SentClientRenderFunction = 0b000000100;
const SentStyleInsertionFunction = 0b000001000;
const SentFormReplayingRuntime = 0b000010000;
const SentCompletedShellId = 0b000100000;
const SentMarkShellTime = 0b001000000;
const NeedUpgradeToViewTransitions = 0b010000000;
const SentUpgradeToViewTransitions = 0b100000000;
type NonceOption =
| string
| {
script?: string,
style?: string,
};
export type RenderState = {
placeholderPrefix: PrecomputedChunk,
segmentPrefix: PrecomputedChunk,
boundaryPrefix: PrecomputedChunk,
startInlineScript: PrecomputedChunk,
startInlineStyle: PrecomputedChunk,
preamble: PreambleState,
externalRuntimeScript: null | ExternalRuntimeScript,
bootstrapChunks: Array<Chunk | PrecomputedChunk>,
importMapChunks: Array<Chunk | PrecomputedChunk>,
charsetChunks: Array<Chunk | PrecomputedChunk>,
viewportChunks: Array<Chunk | PrecomputedChunk>,
hoistableChunks: Array<Chunk | PrecomputedChunk>,
onHeaders: void | ((headers: HeadersDescriptor) => void),
headers: null | {
preconnects: string,
fontPreloads: string,
highImagePreloads: string,
remainingCapacity: number,
},
resets: {
font: {
[href: string]: Preloaded,
},
dns: {[key: string]: Exists},
connect: {
default: {[key: string]: Exists},
anonymous: {[key: string]: Exists},
credentials: {[key: string]: Exists},
},
image: {
[key: string]: Preloaded,
},
style: {
[key: string]: Exists | Preloaded | PreloadedWithCredentials,
},
},
preconnects: Set<Resource>,
fontPreloads: Set<Resource>,
highImagePreloads: 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>,
},
nonce: {
script: string | void,
style: string | void,
},
stylesToHoist: boolean,
...
};
type Exists = null;
type Preloaded = [];
type PreloadedWithCredentials = [
?CrossOriginEnum,
?string,
];
const EXISTS: Exists = null;
const PRELOAD_NO_CREDS: Preloaded = [];
if (__DEV__) {
Object.freeze(PRELOAD_NO_CREDS);
}
export type ResumableState = {
idPrefix: string,
nextFormID: number,
streamingFormat: StreamingFormat,
bootstrapScriptContent?: string | void,
bootstrapScripts?: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
bootstrapModules?: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
instructions: InstructionState,
hasBody: boolean,
hasHtml: boolean,
unknownResources: {
[asType: string]: {
[href: string]: Preloaded,
},
},
dnsResources: {[key: string]: Exists},
connectResources: {
default: {[key: string]: Exists},
anonymous: {[key: string]: Exists},
credentials: {[key: string]: Exists},
},
imageResources: {
[key: string]: Preloaded,
},
styleResources: {
[key: string]: Exists | Preloaded | PreloadedWithCredentials,
},
scriptResources: {
[key: string]: Exists | Preloaded | PreloadedWithCredentials,
},
moduleUnknownResources: {
[asType: string]: {
[href: string]: Preloaded,
},
},
moduleScriptResources: {
[key: string]: Exists | Preloaded | PreloadedWithCredentials,
},
};
let currentlyFlushingRenderState: RenderState | null = null;
const dataElementQuotedEnd = stringToPrecomputedChunk('"></template>');
const startInlineScript = stringToPrecomputedChunk('<script');
const endInlineScript = stringToPrecomputedChunk('</script>');
const startScriptSrc = stringToPrecomputedChunk('<script src="');
const startModuleSrc = stringToPrecomputedChunk('<script type="module" src="');
const scriptNonce = stringToPrecomputedChunk(' nonce="');
const scriptIntegirty = stringToPrecomputedChunk(' integrity="');
const scriptCrossOrigin = stringToPrecomputedChunk(' crossorigin="');
const endAsyncScript = stringToPrecomputedChunk(' async=""></script>');
const startInlineStyle = stringToPrecomputedChunk('<style');
function escapeEntireInlineScriptContent(scriptText: string) {
if (__DEV__) {
checkHtmlStringCoercion(scriptText);
}
return ('' + scriptText).replace(scriptRegex, scriptReplacer);
}
const scriptRegex = /(<\/|<)(s)(cript)/gi;
const scriptReplacer = (
match: string,
prefix: string,
s: string,
suffix: string,
) => `${prefix}${s === 's' ? '\\u0073' : '\\u0053'}${suffix}`;
export type BootstrapScriptDescriptor = {
src: string,
integrity?: string,
crossOrigin?: string,
};
export type ExternalRuntimeScript = {
src: string,
chunks: Array<Chunk | PrecomputedChunk>,
};
const importMapScriptStart = stringToPrecomputedChunk(
'<script type="importmap">',
);
const importMapScriptEnd = stringToPrecomputedChunk('</script>');
const DEFAULT_HEADERS_CAPACITY_IN_UTF16_CODE_UNITS = 2000;
let didWarnForNewBooleanPropsWithEmptyValue: {[string]: boolean};
if (__DEV__) {
didWarnForNewBooleanPropsWithEmptyValue = {};
}
export function createRenderState(
resumableState: ResumableState,
nonce:
| string
| {
script?: string,
style?: string,
}
| void,
externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
importMap: ImportMap | void,
onHeaders: void | ((headers: HeadersDescriptor) => void),
maxHeadersLength: void | number,
): RenderState {
const nonceScript = typeof nonce === 'string' ? nonce : nonce && nonce.script;
const inlineScriptWithNonce =
nonceScript === undefined
? startInlineScript
: stringToPrecomputedChunk(
'<script nonce="' + escapeTextForBrowser(nonceScript) + '"',
);
const nonceStyle =
typeof nonce === 'string' ? undefined : nonce && nonce.style;
const inlineStyleWithNonce =
nonceStyle === undefined
? startInlineStyle
: stringToPrecomputedChunk(
'<style nonce="' + escapeTextForBrowser(nonceStyle) + '"',
);
const idPrefix = resumableState.idPrefix;
const bootstrapChunks: Array<Chunk | PrecomputedChunk> = [];
let externalRuntimeScript: null | ExternalRuntimeScript = null;
const {bootstrapScriptContent, bootstrapScripts, bootstrapModules} =
resumableState;
if (bootstrapScriptContent !== undefined) {
bootstrapChunks.push(inlineScriptWithNonce);
pushCompletedShellIdAttribute(bootstrapChunks, resumableState);
bootstrapChunks.push(
endOfStartTag,
stringToChunk(escapeEntireInlineScriptContent(bootstrapScriptContent)),
endInlineScript,
);
}
if (enableFizzExternalRuntime) {
if (externalRuntimeConfig !== undefined) {
if (typeof externalRuntimeConfig === 'string') {
externalRuntimeScript = {
src: externalRuntimeConfig,
chunks: [],
};
pushScriptImpl(externalRuntimeScript.chunks, {
src: externalRuntimeConfig,
async: true,
integrity: undefined,
nonce: nonceScript,
});
} else {
externalRuntimeScript = {
src: externalRuntimeConfig.src,
chunks: [],
};
pushScriptImpl(externalRuntimeScript.chunks, {
src: externalRuntimeConfig.src,
async: true,
integrity: externalRuntimeConfig.integrity,
nonce: nonceScript,
});
}
}
}
const importMapChunks: Array<Chunk | PrecomputedChunk> = [];
if (importMap !== undefined) {
const map = importMap;
importMapChunks.push(importMapScriptStart);
importMapChunks.push(
stringToChunk(escapeEntireInlineScriptContent(JSON.stringify(map))),
);
importMapChunks.push(importMapScriptEnd);
}
if (__DEV__) {
if (onHeaders && typeof maxHeadersLength === 'number') {
if (maxHeadersLength <= 0) {
console.error(
'React expected a positive non-zero `maxHeadersLength` option but found %s instead. When using the `onHeaders` option you may supply an optional `maxHeadersLength` option as well however, when setting this value to zero or less no headers will be captured.',
maxHeadersLength === 0 ? 'zero' : maxHeadersLength,
);
}
}
}
const headers = onHeaders
? {
preconnects: '',
fontPreloads: '',
highImagePreloads: '',
remainingCapacity:
2 +
(typeof maxHeadersLength === 'number'
? maxHeadersLength
: DEFAULT_HEADERS_CAPACITY_IN_UTF16_CODE_UNITS),
}
: null;
const renderState: RenderState = {
placeholderPrefix: stringToPrecomputedChunk(idPrefix + 'P:'),
segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
boundaryPrefix: stringToPrecomputedChunk(idPrefix + 'B:'),
startInlineScript: inlineScriptWithNonce,
startInlineStyle: inlineStyleWithNonce,
preamble: createPreambleState(),
externalRuntimeScript: externalRuntimeScript,
bootstrapChunks: bootstrapChunks,
importMapChunks,
onHeaders,
headers,
resets: {
font: {},
dns: {},
connect: {
default: {},
anonymous: {},
credentials: {},
},
image: {},
style: {},
},
charsetChunks: [],
viewportChunks: [],
hoistableChunks: [],
preconnects: new Set(),
fontPreloads: new Set(),
highImagePreloads: new Set(),
styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
preloads: {
images: new Map(),
stylesheets: new Map(),
scripts: new Map(),
moduleScripts: new Map(),
},
nonce: {
script: nonceScript,
style: nonceStyle,
},
hoistableState: null,
stylesToHoist: false,
};
if (bootstrapScripts !== undefined) {
for (let i = 0; i < bootstrapScripts.length; i++) {
const scriptConfig = bootstrapScripts[i];
let src, crossOrigin, integrity;
const props: PreloadAsProps = ({
rel: 'preload',
as: 'script',
fetchPriority: 'low',
nonce,
}: any);
if (typeof scriptConfig === 'string') {
props.href = src = scriptConfig;
} else {
props.href = src = scriptConfig.src;
props.integrity = integrity =
typeof scriptConfig.integrity === 'string'
? scriptConfig.integrity
: undefined;
props.crossOrigin = crossOrigin =
typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null
? undefined
: scriptConfig.crossOrigin === 'use-credentials'
? 'use-credentials'
: '';
}
preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(
startScriptSrc,
stringToChunk(escapeTextForBrowser(src)),
attributeEnd,
);
if (nonceScript) {
bootstrapChunks.push(
scriptNonce,
stringToChunk(escapeTextForBrowser(nonceScript)),
attributeEnd,
);
}
if (typeof integrity === 'string') {
bootstrapChunks.push(
scriptIntegirty,
stringToChunk(escapeTextForBrowser(integrity)),
attributeEnd,
);
}
if (typeof crossOrigin === 'string') {
bootstrapChunks.push(
scriptCrossOrigin,
stringToChunk(escapeTextForBrowser(crossOrigin)),
attributeEnd,
);
}
pushCompletedShellIdAttribute(bootstrapChunks, resumableState);
bootstrapChunks.push(endAsyncScript);
}
}
if (bootstrapModules !== undefined) {
for (let i = 0; i < bootstrapModules.length; i++) {
const scriptConfig = bootstrapModules[i];
let src, crossOrigin, integrity;
const props: PreloadModuleProps = ({
rel: 'modulepreload',
fetchPriority: 'low',
nonce: nonceScript,
}: any);
if (typeof scriptConfig === 'string') {
props.href = src = scriptConfig;
} else {
props.href = src = scriptConfig.src;
props.integrity = integrity =
typeof scriptConfig.integrity === 'string'
? scriptConfig.integrity
: undefined;
props.crossOrigin = crossOrigin =
typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null
? undefined
: scriptConfig.crossOrigin === 'use-credentials'
? 'use-credentials'
: '';
}
preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(
startModuleSrc,
stringToChunk(escapeTextForBrowser(src)),
attributeEnd,
);
if (nonceScript) {
bootstrapChunks.push(
scriptNonce,
stringToChunk(escapeTextForBrowser(nonceScript)),
attributeEnd,
);
}
if (typeof integrity === 'string') {
bootstrapChunks.push(
scriptIntegirty,
stringToChunk(escapeTextForBrowser(integrity)),
attributeEnd,
);
}
if (typeof crossOrigin === 'string') {
bootstrapChunks.push(
scriptCrossOrigin,
stringToChunk(escapeTextForBrowser(crossOrigin)),
attributeEnd,
);
}
pushCompletedShellIdAttribute(bootstrapChunks, resumableState);
bootstrapChunks.push(endAsyncScript);
}
}
return renderState;
}
export function resumeRenderState(
resumableState: ResumableState,
nonce: NonceOption | void,
): RenderState {
return createRenderState(
resumableState,
nonce,
undefined,
undefined,
undefined,
undefined,
);
}
export function createResumableState(
identifierPrefix: string | void,
externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
bootstrapScriptContent: string | void,
bootstrapScripts: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
bootstrapModules: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
): ResumableState {
const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
let streamingFormat = ScriptStreamingFormat;
if (enableFizzExternalRuntime) {
if (externalRuntimeConfig !== undefined) {
streamingFormat = DataStreamingFormat;
}
}
return {
idPrefix: idPrefix,
nextFormID: 0,
streamingFormat,
bootstrapScriptContent,
bootstrapScripts,
bootstrapModules,
instructions: NothingSent,
hasBody: false,
hasHtml: false,
unknownResources: {},
dnsResources: {},
connectResources: {
default: {},
anonymous: {},
credentials: {},
},
imageResources: {},
styleResources: {},
scriptResources: {},
moduleUnknownResources: {},
moduleScriptResources: {},
};
}
export function resetResumableState(
resumableState: ResumableState,
renderState: RenderState,
): void {
resumableState.nextFormID = 0;
resumableState.hasBody = false;
resumableState.hasHtml = false;
resumableState.unknownResources = {
font: renderState.resets.font,
};
resumableState.dnsResources = renderState.resets.dns;
resumableState.connectResources = renderState.resets.connect;
resumableState.imageResources = renderState.resets.image;
resumableState.styleResources = renderState.resets.style;
resumableState.scriptResources = {};
resumableState.moduleUnknownResources = {};
resumableState.moduleScriptResources = {};
resumableState.instructions = NothingSent;
}
export function completeResumableState(resumableState: ResumableState): void {
resumableState.bootstrapScriptContent = undefined;
resumableState.bootstrapScripts = undefined;
resumableState.bootstrapModules = undefined;
}
export type PreambleState = {
htmlChunks: null | Array<Chunk | PrecomputedChunk>,
headChunks: null | Array<Chunk | PrecomputedChunk>,
bodyChunks: null | Array<Chunk | PrecomputedChunk>,
};
export function createPreambleState(): PreambleState {
return {
htmlChunks: null,
headChunks: null,
bodyChunks: null,
};
}
export const ROOT_HTML_MODE = 0;
const HTML_HTML_MODE = 1;
const HTML_MODE = 2;
const HTML_HEAD_MODE = 3;
const SVG_MODE = 4;
const MATHML_MODE = 5;
const HTML_TABLE_MODE = 6;
const HTML_TABLE_BODY_MODE = 7;
const HTML_TABLE_ROW_MODE = 8;
const HTML_COLGROUP_MODE = 9;
type InsertionMode = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
const NO_SCOPE = 0b000000;
const NOSCRIPT_SCOPE = 0b000001;
const PICTURE_SCOPE = 0b000010;
const FALLBACK_SCOPE = 0b000100;
const EXIT_SCOPE = 0b001000;
const ENTER_SCOPE = 0b010000;
const UPDATE_SCOPE = 0b100000;
const SUBTREE_SCOPE = ~(ENTER_SCOPE | EXIT_SCOPE);
type ViewTransitionContext = {
update: 'none' | 'auto' | string,
enter: 'none' | 'auto' | string,
exit: 'none' | 'auto' | string,
share: 'none' | 'auto' | string,
name: 'auto' | string,
autoName: string,
nameIdx: number,
};
export type FormatContext = {
insertionMode: InsertionMode,
selectedValue: null | string | Array<string>,
tagScope: number,
viewTransition: null | ViewTransitionContext,
};
function createFormatContext(
insertionMode: InsertionMode,
selectedValue: null | string | Array<string>,
tagScope: number,
viewTransition: null | ViewTransitionContext,
): FormatContext {
return {
insertionMode,
selectedValue,
tagScope,
viewTransition,
};
}
export function canHavePreamble(formatContext: FormatContext): boolean {
return formatContext.insertionMode < HTML_MODE;
}
export function createRootFormatContext(namespaceURI?: string): FormatContext {
const insertionMode =
namespaceURI === 'http://www.w3.org/2000/svg'
? SVG_MODE
: namespaceURI === 'http://www.w3.org/1998/Math/MathML'
? MATHML_MODE
: ROOT_HTML_MODE;
return createFormatContext(insertionMode, null, NO_SCOPE, null);
}
export function getChildFormatContext(
parentContext: FormatContext,
type: string,
props: Object,
): FormatContext {
const subtreeScope = parentContext.tagScope & SUBTREE_SCOPE;
switch (type) {
case 'noscript':
return createFormatContext(
HTML_MODE,
null,
subtreeScope | NOSCRIPT_SCOPE,
null,
);
case 'select':
return createFormatContext(
HTML_MODE,
props.value != null ? props.value : props.defaultValue,
subtreeScope,
null,
);
case 'svg':
return createFormatContext(SVG_MODE, null, subtreeScope, null);
case 'picture':
return createFormatContext(
HTML_MODE,
null,
subtreeScope | PICTURE_SCOPE,
null,
);
case 'math':
return createFormatContext(MATHML_MODE, null, subtreeScope, null);
case 'foreignObject':
return createFormatContext(HTML_MODE, null, subtreeScope, null);
case 'table':
return createFormatContext(HTML_TABLE_MODE, null, subtreeScope, null);
case 'thead':
case 'tbody':
case 'tfoot':
return createFormatContext(
HTML_TABLE_BODY_MODE,
null,
subtreeScope,
null,
);
case 'colgroup':
return createFormatContext(HTML_COLGROUP_MODE, null, subtreeScope, null);
case 'tr':
return createFormatContext(HTML_TABLE_ROW_MODE, null, subtreeScope, null);
case 'head':
if (parentContext.insertionMode < HTML_MODE) {
return createFormatContext(HTML_HEAD_MODE, null, subtreeScope, null);
}
break;
case 'html':
if (parentContext.insertionMode === ROOT_HTML_MODE) {
return createFormatContext(HTML_HTML_MODE, null, subtreeScope, null);
}
break;
}
if (parentContext.insertionMode >= HTML_TABLE_MODE) {
return createFormatContext(HTML_MODE, null, subtreeScope, null);
}
if (parentContext.insertionMode < HTML_MODE) {
return createFormatContext(HTML_MODE, null, subtreeScope, null);
}
if (enableViewTransition) {
if (parentContext.viewTransition !== null) {
return createFormatContext(
parentContext.insertionMode,
parentContext.selectedValue,
subtreeScope,
null,
);
}
}
if (parentContext.tagScope !== subtreeScope) {
return createFormatContext(
parentContext.insertionMode,
parentContext.selectedValue,
subtreeScope,
null,
);
}
return parentContext;
}
function getSuspenseViewTransition(
parentViewTransition: null | ViewTransitionContext,
): null | ViewTransitionContext {
if (parentViewTransition === null) {
return null;
}
const viewTransition: ViewTransitionContext = {
update: parentViewTransition.update,
enter: 'none',
exit: 'none',
share: parentViewTransition.update,
name: parentViewTransition.autoName,
autoName: parentViewTransition.autoName,
nameIdx: 0,
};
return viewTransition;
}
export function getSuspenseFallbackFormatContext(
resumableState: ResumableState,
parentContext: FormatContext,
): FormatContext {
if (parentContext.tagScope & UPDATE_SCOPE) {
resumableState.instructions |= NeedUpgradeToViewTransitions;
}
return createFormatContext(
parentContext.insertionMode,
parentContext.selectedValue,
parentContext.tagScope | FALLBACK_SCOPE | EXIT_SCOPE,
getSuspenseViewTransition(parentContext.viewTransition),
);
}
export function getSuspenseContentFormatContext(
resumableState: ResumableState,
parentContext: FormatContext,
): FormatContext {
return createFormatContext(
parentContext.insertionMode,
parentContext.selectedValue,
parentContext.tagScope | ENTER_SCOPE,
getSuspenseViewTransition(parentContext.viewTransition),
);
}
export function getViewTransitionFormatContext(
resumableState: ResumableState,
parentContext: FormatContext,
update: ?string,
enter: ?string,
exit: ?string,
share: ?string,
name: ?string,
autoName: string,
): FormatContext {
if (update == null) {
update = 'auto';
}
if (enter == null) {
enter = 'auto';
}
if (exit == null) {
exit = 'auto';
}
if (name == null) {
const parentViewTransition = parentContext.viewTransition;
if (parentViewTransition !== null) {
name = parentViewTransition.name;
share = parentViewTransition.share;
} else {
name = 'auto';
share = 'none';
}
} else {
if (share == null) {
share = 'auto';
}
if (parentContext.tagScope & FALLBACK_SCOPE) {
resumableState.instructions |= NeedUpgradeToViewTransitions;
}
}
if (!(parentContext.tagScope & EXIT_SCOPE)) {
exit = 'none';
} else {
resumableState.instructions |= NeedUpgradeToViewTransitions;
}
if (!(parentContext.tagScope & ENTER_SCOPE)) {
enter = 'none';
} else {
resumableState.instructions |= NeedUpgradeToViewTransitions;
}
const viewTransition: ViewTransitionContext = {
update,
enter,
exit,
share,
name,
autoName,
nameIdx: 0,
};
let subtreeScope = parentContext.tagScope & SUBTREE_SCOPE;
if (update !== 'none') {
subtreeScope |= UPDATE_SCOPE;
} else {
subtreeScope &= ~UPDATE_SCOPE;
}
return createFormatContext(
parentContext.insertionMode,
parentContext.selectedValue,
subtreeScope,
viewTransition,
);
}
export function isPreambleContext(formatContext: FormatContext): boolean {
return formatContext.insertionMode === HTML_HEAD_MODE;
}
export function makeId(
resumableState: ResumableState,
treeId: string,
localId: number,
): string {
const idPrefix = resumableState.idPrefix;
let id = '_' + idPrefix + 'R_' + treeId;
if (localId > 0) {
id += 'H' + localId.toString(32);
}
return id + '_';
}
function encodeHTMLTextNode(text: string): string {
return escapeTextForBrowser(text);
}
const textSeparator = stringToPrecomputedChunk('<!-- -->');
export function pushTextInstance(
target: Array<Chunk | PrecomputedChunk>,
text: string,
renderState: RenderState,
textEmbedded: boolean,
): boolean {
if (text === '') {
return textEmbedded;
}
if (textEmbedded) {
target.push(textSeparator);
}
target.push(stringToChunk(encodeHTMLTextNode(text)));
return true;
}
export function pushSegmentFinale(
target: Array<Chunk | PrecomputedChunk>,
renderState: RenderState,
lastPushedText: boolean,
textEmbedded: boolean,
): void {
if (lastPushedText && textEmbedded) {
target.push(textSeparator);
}
}
function pushViewTransitionAttributes(
target: Array<Chunk | PrecomputedChunk>,
formatContext: FormatContext,
): void {
if (!enableViewTransition) {
return;
}
const viewTransition = formatContext.viewTransition;
if (viewTransition === null) {
return;
}
if (viewTransition.name !== 'auto') {
pushStringAttribute(
target,
'vt-name',
viewTransition.nameIdx === 0
? viewTransition.name
: viewTransition.name + '_' + viewTransition.nameIdx,
);
viewTransition.nameIdx++;
}
pushStringAttribute(target, 'vt-update', viewTransition.update);
if (viewTransition.enter !== 'none') {
pushStringAttribute(target, 'vt-enter', viewTransition.enter);
}
if (viewTransition.exit !== 'none') {
pushStringAttribute(target, 'vt-exit', viewTransition.exit);
}
if (viewTransition.share !== 'none') {
pushStringAttribute(target, 'vt-share', viewTransition.share);
}
}
const styleNameCache: Map<string, PrecomputedChunk> = new Map();
function processStyleName(styleName: string): PrecomputedChunk {
const chunk = styleNameCache.get(styleName);
if (chunk !== undefined) {
return chunk;
}
const result = stringToPrecomputedChunk(
escapeTextForBrowser(hyphenateStyleName(styleName)),
);
styleNameCache.set(styleName, result);
return result;
}
const styleAttributeStart = stringToPrecomputedChunk(' style="');
const styleAssign = stringToPrecomputedChunk(':');
const styleSeparator = stringToPrecomputedChunk(';');
function pushStyleAttribute(
target: Array<Chunk | PrecomputedChunk>,
style: Object,
): void {
if (typeof style !== 'object') {
throw new Error(
'The `style` prop expects a mapping from style properties to values, ' +
"not a string. For example, style={{marginRight: spacing + 'em'}} when " +
'using JSX.',
);
}
let isFirst = true;
for (const styleName in style) {
if (!hasOwnProperty.call(style, styleName)) {
continue;
}
const styleValue = style[styleName];
if (
styleValue == null ||
typeof styleValue === 'boolean' ||
styleValue === ''
) {
continue;
}
let nameChunk;
let valueChunk;
const isCustomProperty = styleName.indexOf('--') === 0;
if (isCustomProperty) {
nameChunk = stringToChunk(escapeTextForBrowser(styleName));
if (__DEV__) {
checkCSSPropertyStringCoercion(styleValue, styleName);
}
valueChunk = stringToChunk(
escapeTextForBrowser(('' + styleValue).trim()),
);
} else {
if (__DEV__) {
warnValidStyle(styleName, styleValue);
}
nameChunk = processStyleName(styleName);
if (typeof styleValue === 'number') {
if (styleValue !== 0 && !isUnitlessNumber(styleName)) {
valueChunk = stringToChunk(styleValue + 'px');
} else {
valueChunk = stringToChunk('' + styleValue);
}
} else {
if (__DEV__) {
checkCSSPropertyStringCoercion(styleValue, styleName);
}
valueChunk = stringToChunk(
escapeTextForBrowser(('' + styleValue).trim()),
);
}
}
if (isFirst) {
isFirst = false;
target.push(styleAttributeStart, nameChunk, styleAssign, valueChunk);
} else {
target.push(styleSeparator, nameChunk, styleAssign, valueChunk);
}
}
if (!isFirst) {
target.push(attributeEnd);
}
}
const attributeSeparator = stringToPrecomputedChunk(' ');
const attributeAssign = stringToPrecomputedChunk('="');
const attributeEnd = stringToPrecomputedChunk('"');
const attributeEmptyString = stringToPrecomputedChunk('=""');
function pushBooleanAttribute(
target: Array<Chunk | PrecomputedChunk>,
name: string,
value: string | boolean | number | Function | Object,
): void {
if (value && typeof value !== 'function' && typeof value !== 'symbol') {
target.push(attributeSeparator, stringToChunk(name), attributeEmptyString);
}
}
function pushStringAttribute(
target: Array<Chunk | PrecomputedChunk>,
name: string,
value: string | boolean | number | Function | Object,
): void {
if (
typeof value !== 'function' &&
typeof value !== 'symbol' &&
typeof value !== 'boolean'
) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
);
}
}
function makeFormFieldPrefix(resumableState: ResumableState): string {
const id = resumableState.nextFormID++;
return resumableState.idPrefix + id;
}
const actionJavaScriptURL = stringToPrecomputedChunk(
escapeTextForBrowser(
"javascript:throw new Error('React form unexpectedly submitted.')",
),
);
const startHiddenInputChunk = stringToPrecomputedChunk('<input type="hidden"');
function pushAdditionalFormField(
this: Array<Chunk | PrecomputedChunk>,
value: string | File,
key: string,
): void {
const target: Array<Chunk | PrecomputedChunk> = this;
target.push(startHiddenInputChunk);
validateAdditionalFormField(value, key);
pushStringAttribute(target, 'name', key);
pushStringAttribute(target, 'value', value);
target.push(endOfStartTagSelfClosing);
}
function pushAdditionalFormFields(
target: Array<Chunk | PrecomputedChunk>,
formData: void | null | FormData,
) {
if (formData != null) {
formData.forEach(pushAdditionalFormField, target);
}
}
function validateAdditionalFormField(value: string | File, key: string): void {
if (typeof value !== 'string') {
throw new Error(
'File/Blob fields are not yet supported in progressive forms. ' +
'Will fallback to client hydration.',
);
}
}
function validateAdditionalFormFields(formData: void | null | FormData) {
if (formData != null) {
formData.forEach(validateAdditionalFormField);
}
return formData;
}
function getCustomFormFields(
resumableState: ResumableState,
formAction: any,
): null | ReactCustomFormAction {
const customAction = formAction.$$FORM_ACTION;
if (typeof customAction === 'function') {
const prefix = makeFormFieldPrefix(resumableState);
try {
const customFields = formAction.$$FORM_ACTION(prefix);
if (customFields) {
validateAdditionalFormFields(customFields.data);
}
return customFields;
} catch (x) {
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
throw x;
}
if (__DEV__) {
console.error(
'Failed to serialize an action for progressive enhancement:\n%s',
x,
);
}
}
}
return null;
}
function pushFormActionAttribute(
target: Array<Chunk | PrecomputedChunk>,
resumableState: ResumableState,
renderState: RenderState,
formAction: any,
formEncType: any,
formMethod: any,
formTarget: any,
name: any,
): void | null | FormData {
let formData = null;
if (typeof formAction === 'function') {
if (__DEV__) {
if (name !== null && !didWarnFormActionName) {
didWarnFormActionName = true;
console.error(
'Cannot specify a "name" prop for a button that specifies a function as a formAction. ' +
'React needs it to encode which action should be invoked. It will get overridden.',
);
}
if (
(formEncType !== null || formMethod !== null) &&
!didWarnFormActionMethod
) {
didWarnFormActionMethod = true;
console.error(
'Cannot specify a formEncType or formMethod for a button that specifies a ' +
'function as a formAction. React provides those automatically. They will get overridden.',
);
}
if (formTarget !== null && !didWarnFormActionTarget) {
didWarnFormActionTarget = true;
console.error(
'Cannot specify a formTarget for a button that specifies a function as a formAction. ' +
'The function will always be executed in the same window.',
);
}
}
const customFields = getCustomFormFields(resumableState, formAction);
if (customFields !== null) {
name = customFields.name;
formAction = customFields.action || '';
formEncType = customFields.encType;
formMethod = customFields.method;
formTarget = customFields.target;
formData = customFields.data;
} else {
target.push(
attributeSeparator,
stringToChunk('formAction'),
attributeAssign,
actionJavaScriptURL,
attributeEnd,
);
name = null;
formAction = null;
formEncType = null;
formMethod = null;
formTarget = null;
injectFormReplayingRuntime(resumableState, renderState);
}
}
if (name != null) {
pushAttribute(target, 'name', name);
}
if (formAction != null) {
pushAttribute(target, 'formAction', formAction);
}
if (formEncType != null) {
pushAttribute(target, 'formEncType', formEncType);
}
if (formMethod != null) {
pushAttribute(target, 'formMethod', formMethod);
}
if (formTarget != null) {
pushAttribute(target, 'formTarget', formTarget);
}
return formData;
}
let blobCache: null | WeakMap<Blob, Thenable<string>> = null;
function pushSrcObjectAttribute(
target: Array<Chunk | PrecomputedChunk>,
blob: Blob,
): void {
if (blobCache === null) {
blobCache = new WeakMap();
}
const suspenseCache: WeakMap<Blob, Thenable<string>> = blobCache;
let thenable = suspenseCache.get(blob);
if (thenable === undefined) {
thenable = ((readAsDataURL(blob): any): Thenable<string>);
thenable.then(
result => {
(thenable: any).status = 'fulfilled';
(thenable: any).value = result;
},
error => {
(thenable: any).status = 'rejected';
(thenable: any).reason = error;
},
);
suspenseCache.set(blob, thenable);
}
if (thenable.status === 'rejected') {
throw thenable.reason;
} else if (thenable.status !== 'fulfilled') {
throw thenable;
}
const url = thenable.value;
target.push(
attributeSeparator,
stringToChunk('src'),
attributeAssign,
stringToChunk(escapeTextForBrowser(url)),
attributeEnd,
);
}
function pushAttribute(
target: Array<Chunk | PrecomputedChunk>,
name: string,
value: string | boolean | number | Function | Object,
): void {
switch (name) {
case 'className': {
pushStringAttribute(target, 'class', value);
break;
}
case 'tabIndex': {
pushStringAttribute(target, 'tabindex', value);
break;
}
case 'dir':
case 'role':
case 'viewBox':
case 'width':
case 'height': {
pushStringAttribute(target, name, value);
break;
}
case 'style': {
pushStyleAttribute(target, value);
return;
}
case 'src': {
if (enableSrcObject && typeof value === 'object' && value !== null) {
if (typeof Blob === 'function' && value instanceof Blob) {
pushSrcObjectAttribute(target, value);
return;
}
}
}
case 'href': {
if (value === '') {
if (__DEV__) {
if (name === 'src') {
console.error(
'An empty string ("") was passed to the %s attribute. ' +
'This may cause the browser to download the whole page again over the network. ' +
'To fix this, either do not render the element at all ' +
'or pass null to %s instead of an empty string.',
name,
name,
);
} else {
console.error(
'An empty string ("") was passed to the %s attribute. ' +
'To fix this, either do not render the element at all ' +
'or pass null to %s instead of an empty string.',
name,
name,
);
}
}
return;
}
}
case 'action':
case 'formAction': {
if (
value == null ||
typeof value === 'function' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
) {
return;
}
if (__DEV__) {
checkAttributeStringCoercion(value, name);
}
const sanitizedValue = sanitizeURL('' + value);
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(sanitizedValue)),
attributeEnd,
);
return;
}
case 'defaultValue':
case 'defaultChecked':
case 'innerHTML':
case 'suppressContentEditableWarning':
case 'suppressHydrationWarning':
case 'ref':
return;
case 'autoFocus':
case 'multiple':
case 'muted': {
pushBooleanAttribute(target, name.toLowerCase(), value);
return;
}
case 'xlinkHref': {
if (
typeof value === 'function' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
) {
return;
}
if (__DEV__) {
checkAttributeStringCoercion(value, name);
}
const sanitizedValue = sanitizeURL('' + value);
target.push(
attributeSeparator,
stringToChunk('xlink:href'),
attributeAssign,
stringToChunk(escapeTextForBrowser(sanitizedValue)),
attributeEnd,
);
return;
}
case 'contentEditable':
case 'spellCheck':
case 'draggable':
case 'value':
case 'autoReverse':
case 'externalResourcesRequired':
case 'focusable':
case 'preserveAlpha': {
if (typeof value !== 'function' && typeof value !== 'symbol') {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
);
}
return;
}
case 'inert': {
if (__DEV__) {
if (value === '' && !didWarnForNewBooleanPropsWithEmptyValue[name]) {
didWarnForNewBooleanPropsWithEmptyValue[name] = true;
console.error(
'Received an empty string for a boolean attribute `%s`. ' +
'This will treat the attribute as if it were false. ' +
'Either pass `false` to silence this warning, or ' +
'pass `true` if you used an empty string in earlier versions of React to indicate this attribute is true.',
name,
);
}
}
}
case 'allowFullScreen':
case 'async':
case 'autoPlay':
case 'controls':
case 'default':
case 'defer':
case 'disabled':
case 'disablePictureInPicture':
case 'disableRemotePlayback':
case 'formNoValidate':
case 'hidden':
case 'loop':
case 'noModule':
case 'noValidate':
case 'open':
case 'playsInline':
case 'readOnly':
case 'required':
case 'reversed':
case 'scoped':
case 'seamless':
case 'itemScope': {
if (value && typeof value !== 'function' && typeof value !== 'symbol') {
target.push(
attributeSeparator,
stringToChunk(name),
attributeEmptyString,
);
}
return;
}
case 'capture':
case 'download': {
if (value === true) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeEmptyString,
);
} else if (value === false) {
} else if (typeof value !== 'function' && typeof value !== 'symbol') {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
);
}
return;
}
case 'cols':
case 'rows':
case 'size':
case 'span': {
if (
typeof value !== 'function' &&
typeof value !== 'symbol' &&
!isNaN(value) &&
(value: any) >= 1
) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
);
}
return;
}
case 'rowSpan':
case 'start': {
if (
typeof value !== 'function' &&
typeof value !== 'symbol' &&
!isNaN(value)
) {
target.push(
attributeSeparator,
stringToChunk(name),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
);
}
return;
}
case 'xlinkActuate':
pushStringAttribute(target, 'xlink:actuate', value);
return;
case 'xlinkArcrole':
pushStringAttribute(target, 'xlink:arcrole', value);
return;
case 'xlinkRole':
pushStringAttribute(target, 'xlink:role', value);
return;
case 'xlinkShow':
pushStringAttribute(target, 'xlink:show', value);
return;
case 'xlinkTitle':
pushStringAttribute(target, 'xlink:title', value);
return;
case 'xlinkType':
pushStringAttribute(target, 'xlink:type', value);
return;
case 'xmlBase':
pushStringAttribute(target, 'xml:base', value);
return;
case 'xmlLang':
pushStringAttribute(target, 'xml:lang', value);
return;
case 'xmlSpace':
pushStringAttribute(target, 'xml:space', value);
return;
default:
if (
name.length > 2 &&
(name[0] === 'o' || name[0] === 'O') &&
(name[1] === 'n' || name[1] === 'N')
) {
return;
}
const attributeName = getAttributeAlias(name);
if (isAttributeNameSafe(attributeName)) {
switch (typeof value) {
case 'function':
case 'symbol':
return;
case 'boolean': {
const prefix = attributeName.toLowerCase().slice(0, 5);
if (prefix !== 'data-' && prefix !== 'aria-') {
return;
}
}
}
target.push(
attributeSeparator,
stringToChunk(attributeName),
attributeAssign,
stringToChunk(escapeTextForBrowser(value)),
attributeEnd,
);
}
}
}
const endOfStartTag = stringToPrecomputedChunk('>');
const endOfStartTagSelfClosing = stringToPrecomputedChunk('/>');
function pushInnerHTML(
target: Array<Chunk | PrecomputedChunk>,
innerHTML: any,
children: any,
) {
if (innerHTML != null) {
if (children != null) {
throw new Error(
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
);
}
if (typeof innerHTML !== 'object' || !('__html' in innerHTML)) {
throw new Error(
'`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
'Please visit https://react.dev/link/dangerously-set-inner-html ' +
'for more information.',
);
}
const html = innerHTML.__html;
if (html !== null && html !== undefined) {
if (__DEV__) {
checkHtmlStringCoercion(html);
}
target.push(stringToChunk('' + html));
}
}
}
let didWarnDefaultInputValue = false;
let didWarnDefaultChecked = false;
let didWarnDefaultSelectValue = false;
let didWarnDefaultTextareaValue = false;
let didWarnInvalidOptionChildren = false;
let didWarnInvalidOptionInnerHTML = false;
let didWarnSelectedSetOnOption = false;
let didWarnFormActionType = false;
let didWarnFormActionName = false;
let didWarnFormActionTarget = false;
let didWarnFormActionMethod = false;
function checkSelectProp(props: any, propName: string) {
if (__DEV__) {
const value = props[propName];
if (value != null) {
const array = isArray(value);
if (props.multiple && !array) {
console.error(
'The `%s` prop supplied to <select> must be an array if ' +
'`multiple` is true.',
propName,
);
} else if (!props.multiple && array) {
console.error(
'The `%s` prop supplied to <select> must be a scalar ' +
'value if `multiple` is false.',
propName,
);
}
}
}
}
function pushStartAnchor(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag('a'));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'href':
if (propValue === '') {
pushStringAttribute(target, 'href', '');
} else {
pushAttribute(target, propKey, propValue);
}
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
target.push(stringToChunk(encodeHTMLTextNode(children)));
return null;
}
return children;
}
function pushStartObject(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag('object'));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'data': {
if (__DEV__) {
checkAttributeStringCoercion(propValue, 'data');
}
const sanitizedValue = sanitizeURL('' + propValue);
if (sanitizedValue === '') {
if (__DEV__) {
console.error(
'An empty string ("") was passed to the %s attribute. ' +
'To fix this, either do not render the element at all ' +
'or pass null to %s instead of an empty string.',
propKey,
propKey,
);
}
break;
}
target.push(
attributeSeparator,
stringToChunk('data'),
attributeAssign,
stringToChunk(escapeTextForBrowser(sanitizedValue)),
attributeEnd,
);
break;
}
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
target.push(stringToChunk(encodeHTMLTextNode(children)));
return null;
}
return children;
}
function pushStartSelect(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
formatContext: FormatContext,
): ReactNodeList {
if (__DEV__) {
checkControlledValueProps('select', props);
checkSelectProp(props, 'value');
checkSelectProp(props, 'defaultValue');
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
!didWarnDefaultSelectValue
) {
console.error(
'Select elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled select ' +
'element and remove one of these props. More info: ' +
'https://react.dev/link/controlled-components',
);
didWarnDefaultSelectValue = true;
}
}
target.push(startChunkForTag('select'));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'defaultValue':
case 'value':
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
return children;
}
function flattenOptionChildren(children: mixed): string {
let content = '';
Children.forEach((children: any), function (child) {
if (child == null) {
return;
}
content += (child: any);
if (__DEV__) {
if (
!didWarnInvalidOptionChildren &&
typeof child !== 'string' &&
typeof child !== 'number' &&
typeof child !== 'bigint'
) {
didWarnInvalidOptionChildren = true;
console.error(
'Cannot infer the option value of complex children. ' +
'Pass a `value` prop or use a plain string as children to <option>.',
);
}
}
});
return content;
}
const selectedMarkerAttribute = stringToPrecomputedChunk(' selected=""');
function pushStartOption(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
formatContext: FormatContext,
): ReactNodeList {
const selectedValue = formatContext.selectedValue;
target.push(startChunkForTag('option'));
let children = null;
let value = null;
let selected = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'selected':
selected = propValue;
if (__DEV__) {
if (!didWarnSelectedSetOnOption) {
console.error(
'Use the `defaultValue` or `value` props on <select> instead of ' +
'setting `selected` on <option>.',
);
didWarnSelectedSetOnOption = true;
}
}
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'value':
value = propValue;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
if (selectedValue != null) {
let stringValue;
if (value !== null) {
if (__DEV__) {
checkAttributeStringCoercion(value, 'value');
}
stringValue = '' + value;
} else {
if (__DEV__) {
if (innerHTML !== null) {
if (!didWarnInvalidOptionInnerHTML) {
didWarnInvalidOptionInnerHTML = true;
console.error(
'Pass a `value` prop if you set dangerouslyInnerHTML so React knows ' +
'which value should be selected.',
);
}
}
}
stringValue = flattenOptionChildren(children);
}
if (isArray(selectedValue)) {
for (let i = 0; i < selectedValue.length; i++) {
if (__DEV__) {
checkAttributeStringCoercion(selectedValue[i], 'value');
}
const v = '' + selectedValue[i];
if (v === stringValue) {
target.push(selectedMarkerAttribute);
break;
}
}
} else {
if (__DEV__) {
checkAttributeStringCoercion(selectedValue, 'select.value');
}
if ('' + selectedValue === stringValue) {
target.push(selectedMarkerAttribute);
}
}
} else if (selected) {
target.push(selectedMarkerAttribute);
}
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
return children;
}
const formReplayingRuntimeScript =
stringToPrecomputedChunk(formReplayingRuntime);
function injectFormReplayingRuntime(
resumableState: ResumableState,
renderState: RenderState,
): void {
if (
(resumableState.instructions & SentFormReplayingRuntime) === NothingSent &&
(!enableFizzExternalRuntime || !renderState.externalRuntimeScript)
) {
resumableState.instructions |= SentFormReplayingRuntime;
const preamble = renderState.preamble;
const bootstrapChunks = renderState.bootstrapChunks;
if (
(preamble.htmlChunks || preamble.headChunks) &&
bootstrapChunks.length === 0
) {
bootstrapChunks.push(renderState.startInlineScript);
pushCompletedShellIdAttribute(bootstrapChunks, resumableState);
bootstrapChunks.push(
endOfStartTag,
formReplayingRuntimeScript,
endInlineScript,
);
} else {
bootstrapChunks.unshift(
renderState.startInlineScript,
endOfStartTag,
formReplayingRuntimeScript,
endInlineScript,
);
}
}
}
const formStateMarkerIsMatching = stringToPrecomputedChunk('<!--F!-->');
const formStateMarkerIsNotMatching = stringToPrecomputedChunk('<!--F-->');
export function pushFormStateMarkerIsMatching(
target: Array<Chunk | PrecomputedChunk>,
) {
target.push(formStateMarkerIsMatching);
}
export function pushFormStateMarkerIsNotMatching(
target: Array<Chunk | PrecomputedChunk>,
) {
target.push(formStateMarkerIsNotMatching);
}
function pushStartForm(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag('form'));
let children = null;
let innerHTML = null;
let formAction = null;
let formEncType = null;
let formMethod = null;
let formTarget = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'action':
formAction = propValue;
break;
case 'encType':
formEncType = propValue;
break;
case 'method':
formMethod = propValue;
break;
case 'target':
formTarget = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
let formData = null;
let formActionName = null;
if (typeof formAction === 'function') {
if (__DEV__) {
if (
(formEncType !== null || formMethod !== null) &&
!didWarnFormActionMethod
) {
didWarnFormActionMethod = true;
console.error(
'Cannot specify a encType or method for a form that specifies a ' +
'function as the action. React provides those automatically. ' +
'They will get overridden.',
);
}
if (formTarget !== null && !didWarnFormActionTarget) {
didWarnFormActionTarget = true;
console.error(
'Cannot specify a target for a form that specifies a function as the action. ' +
'The function will always be executed in the same window.',
);
}
}
const customFields = getCustomFormFields(resumableState, formAction);
if (customFields !== null) {
formAction = customFields.action || '';
formEncType = customFields.encType;
formMethod = customFields.method;
formTarget = customFields.target;
formData = customFields.data;
formActionName = customFields.name;
} else {
target.push(
attributeSeparator,
stringToChunk('action'),
attributeAssign,
actionJavaScriptURL,
attributeEnd,
);
formAction = null;
formEncType = null;
formMethod = null;
formTarget = null;
injectFormReplayingRuntime(resumableState, renderState);
}
}
if (formAction != null) {
pushAttribute(target, 'action', formAction);
}
if (formEncType != null) {
pushAttribute(target, 'encType', formEncType);
}
if (formMethod != null) {
pushAttribute(target, 'method', formMethod);
}
if (formTarget != null) {
pushAttribute(target, 'target', formTarget);
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
if (formActionName !== null) {
target.push(startHiddenInputChunk);
pushStringAttribute(target, 'name', formActionName);
target.push(endOfStartTagSelfClosing);
pushAdditionalFormFields(target, formData);
}
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
target.push(stringToChunk(encodeHTMLTextNode(children)));
return null;
}
return children;
}
function pushInput(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
formatContext: FormatContext,
): ReactNodeList {
if (__DEV__) {
checkControlledValueProps('input', props);
}
target.push(startChunkForTag('input'));
let name = null;
let formAction = null;
let formEncType = null;
let formMethod = null;
let formTarget = null;
let value = null;
let defaultValue = null;
let checked = null;
let defaultChecked = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
case 'dangerouslySetInnerHTML':
throw new Error(
`${'input'} is a self-closing tag and must neither have \`children\` nor ` +
'use `dangerouslySetInnerHTML`.',
);
case 'name':
name = propValue;
break;
case 'formAction':
formAction = propValue;
break;
case 'formEncType':
formEncType = propValue;
break;
case 'formMethod':
formMethod = propValue;
break;
case 'formTarget':
formTarget = propValue;
break;
case 'defaultChecked':
defaultChecked = propValue;
break;
case 'defaultValue':
defaultValue = propValue;
break;
case 'checked':
checked = propValue;
break;
case 'value':
value = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
if (__DEV__) {
if (
formAction !== null &&
props.type !== 'image' &&
props.type !== 'submit' &&
!didWarnFormActionType
) {
didWarnFormActionType = true;
console.error(
'An input can only specify a formAction along with type="submit" or type="image".',
);
}
}
const formData = pushFormActionAttribute(
target,
resumableState,
renderState,
formAction,
formEncType,
formMethod,
formTarget,
name,
);
if (__DEV__) {
if (checked !== null && defaultChecked !== null && !didWarnDefaultChecked) {
console.error(
'%s contains an input of type %s with both checked and defaultChecked props. ' +
'Input elements must be either controlled or uncontrolled ' +
'(specify either the checked prop, or the defaultChecked prop, but not ' +
'both). Decide between using a controlled or uncontrolled input ' +
'element and remove one of these props. More info: ' +
'https://react.dev/link/controlled-components',
'A component',
props.type,
);
didWarnDefaultChecked = true;
}
if (value !== null && defaultValue !== null && !didWarnDefaultInputValue) {
console.error(
'%s contains an input of type %s with both value and defaultValue props. ' +
'Input elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled input ' +
'element and remove one of these props. More info: ' +
'https://react.dev/link/controlled-components',
'A component',
props.type,
);
didWarnDefaultInputValue = true;
}
}
if (checked !== null) {
pushBooleanAttribute(target, 'checked', checked);
} else if (defaultChecked !== null) {
pushBooleanAttribute(target, 'checked', defaultChecked);
}
if (value !== null) {
pushAttribute(target, 'value', value);
} else if (defaultValue !== null) {
pushAttribute(target, 'value', defaultValue);
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTagSelfClosing);
pushAdditionalFormFields(target, formData);
return null;
}
function pushStartButton(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag('button'));
let children = null;
let innerHTML = null;
let name = null;
let formAction = null;
let formEncType = null;
let formMethod = null;
let formTarget = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'name':
name = propValue;
break;
case 'formAction':
formAction = propValue;
break;
case 'formEncType':
formEncType = propValue;
break;
case 'formMethod':
formMethod = propValue;
break;
case 'formTarget':
formTarget = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
if (__DEV__) {
if (
formAction !== null &&
props.type != null &&
props.type !== 'submit' &&
!didWarnFormActionType
) {
didWarnFormActionType = true;
console.error(
'A button can only specify a formAction along with type="submit" or no type.',
);
}
}
const formData = pushFormActionAttribute(
target,
resumableState,
renderState,
formAction,
formEncType,
formMethod,
formTarget,
name,
);
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushAdditionalFormFields(target, formData);
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
target.push(stringToChunk(encodeHTMLTextNode(children)));
return null;
}
return children;
}
function pushStartTextArea(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
formatContext: FormatContext,
): ReactNodeList {
if (__DEV__) {
checkControlledValueProps('textarea', props);
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
!didWarnDefaultTextareaValue
) {
console.error(
'Textarea elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled textarea ' +
'and remove one of these props. More info: ' +
'https://react.dev/link/controlled-components',
);
didWarnDefaultTextareaValue = true;
}
}
target.push(startChunkForTag('textarea'));
let value = null;
let defaultValue = null;
let children = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'value':
value = propValue;
break;
case 'defaultValue':
defaultValue = propValue;
break;
case 'dangerouslySetInnerHTML':
throw new Error(
'`dangerouslySetInnerHTML` does not make sense on <textarea>.',
);
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
if (value === null && defaultValue !== null) {
value = defaultValue;
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
if (children != null) {
if (__DEV__) {
console.error(
'Use the `defaultValue` or `value` props instead of setting ' +
'children on <textarea>.',
);
}
if (value != null) {
throw new Error(
'If you supply `defaultValue` on a <textarea>, do not pass children.',
);
}
if (isArray(children)) {
if (children.length > 1) {
throw new Error('<textarea> can only have at most one child.');
}
if (__DEV__) {
checkHtmlStringCoercion(children[0]);
}
value = '' + children[0];
}
if (__DEV__) {
checkHtmlStringCoercion(children);
}
value = '' + children;
}
if (typeof value === 'string' && value[0] === '\n') {
target.push(leadingNewline);
}
if (value !== null) {
if (__DEV__) {
checkAttributeStringCoercion(value, 'value');
}
target.push(stringToChunk(encodeHTMLTextNode('' + value)));
}
return null;
}
function pushMeta(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
renderState: RenderState,
textEmbedded: boolean,
formatContext: FormatContext,
): null {
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
const isFallback = formatContext.tagScope & FALLBACK_SCOPE;
if (
formatContext.insertionMode === SVG_MODE ||
noscriptTagInScope ||
props.itemProp != null
) {
return pushSelfClosing(target, props, 'meta', formatContext);
} else {
if (textEmbedded) {
target.push(textSeparator);
}
if (isFallback) {
return null;
} else if (typeof props.charSet === 'string') {
return pushSelfClosing(
renderState.charsetChunks,
props,
'meta',
formatContext,
);
} else if (props.name === 'viewport') {
return pushSelfClosing(
renderState.viewportChunks,
props,
'meta',
formatContext,
);
} else {
return pushSelfClosing(
renderState.hoistableChunks,
props,
'meta',
formatContext,
);
}
}
}
function pushLink(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
hoistableState: null | HoistableState,
textEmbedded: boolean,
formatContext: FormatContext,
): null {
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
const isFallback = formatContext.tagScope & FALLBACK_SCOPE;
const rel = props.rel;
const href = props.href;
const precedence = props.precedence;
if (
formatContext.insertionMode === SVG_MODE ||
noscriptTagInScope ||
props.itemProp != null ||
typeof rel !== 'string' ||
typeof href !== 'string' ||
href === ''
) {
if (__DEV__) {
if (rel === 'stylesheet' && typeof props.precedence === 'string') {
if (typeof href !== 'string' || !href) {
console.error(
'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and expected the `href` prop to be a non-empty string but ecountered %s instead. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop ensure there is a non-empty string `href` prop as well, otherwise remove the `precedence` prop.',
getValueDescriptorExpectingObjectForWarning(href),
);
}
}
}
pushLinkImpl(target, props);
return null;
}
if (props.rel === 'stylesheet') {
const key = getResourceKey(href);
if (
typeof precedence !== 'string' ||
props.disabled != null ||
props.onLoad ||
props.onError
) {
if (__DEV__) {
if (typeof precedence === 'string') {
if (props.disabled != null) {
console.error(
'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and a `disabled` prop. The presence of the `disabled` prop indicates an intent to manage the stylesheet active state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the `disabled` prop, otherwise remove the `precedence` prop.',
);
} else if (props.onLoad || props.onError) {
const propDescription =
props.onLoad && props.onError
? '`onLoad` and `onError` props'
: props.onLoad
? '`onLoad` prop'
: '`onError` prop';
console.error(
'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and %s. The presence of loading and error handlers indicates an intent to manage the stylesheet loading state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the %s, otherwise remove the `precedence` prop.',
propDescription,
propDescription,
);
}
}
}
return pushLinkImpl(target, props);
} else {
let styleQueue = renderState.styles.get(precedence);
const hasKey = resumableState.styleResources.hasOwnProperty(key);
const resourceState = hasKey
? resumableState.styleResources[key]
: undefined;
if (resourceState !== EXISTS) {
resumableState.styleResources[key] = EXISTS;
if (!styleQueue) {
styleQueue = {
precedence: stringToChunk(escapeTextForBrowser(precedence)),
rules: ([]: Array<Chunk | PrecomputedChunk>),
hrefs: ([]: Array<Chunk | PrecomputedChunk>),
sheets: (new Map(): Map<string, StylesheetResource>),
};
renderState.styles.set(precedence, styleQueue);
}
const resource: StylesheetResource = {
state: PENDING,
props: stylesheetPropsFromRawProps(props),
};
if (resourceState) {
const preloadState: Preloaded | PreloadedWithCredentials =
resourceState;
if (preloadState.length === 2) {
adoptPreloadCredentials(resource.props, preloadState);
}
const preloadResource = renderState.preloads.stylesheets.get(key);
if (preloadResource && preloadResource.length > 0) {
preloadResource.length = 0;
} else {
resource.state = PRELOADED;
}
} else {
}
styleQueue.sheets.set(key, resource);
if (hoistableState) {
hoistableState.stylesheets.add(resource);
}
} else {
if (styleQueue) {
const resource = styleQueue.sheets.get(key);
if (resource) {
if (hoistableState) {
hoistableState.stylesheets.add(resource);
}
}
}
}
if (textEmbedded) {
target.push(textSeparator);
}
return null;
}
} else if (props.onLoad || props.onError) {
return pushLinkImpl(target, props);
} else {
if (textEmbedded) {
target.push(textSeparator);
}
if (isFallback) {
return null;
} else {
return pushLinkImpl(renderState.hoistableChunks, props);
}
}
}
function pushLinkImpl(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
): null {
target.push(startChunkForTag('link'));
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
case 'dangerouslySetInnerHTML':
throw new Error(
`${'link'} is a self-closing tag and must neither have \`children\` nor ` +
'use `dangerouslySetInnerHTML`.',
);
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
target.push(endOfStartTagSelfClosing);
return null;
}
function pushStyle(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
hoistableState: null | HoistableState,
textEmbedded: boolean,
formatContext: FormatContext,
): ReactNodeList {
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
if (__DEV__) {
if (hasOwnProperty.call(props, 'children')) {
const children = props.children;
const child = Array.isArray(children)
? children.length < 2
? children[0]
: null
: children;
if (
typeof child === 'function' ||
typeof child === 'symbol' ||
Array.isArray(child)
) {
const childType =
typeof child === 'function'
? 'a Function'
: typeof child === 'symbol'
? 'a Sybmol'
: 'an Array';
console.error(
'React expect children of <style> tags to be a string, number, or object with a `toString` method but found %s instead. ' +
'In browsers style Elements can only have `Text` Nodes as children.',
childType,
);
}
}
}
const precedence = props.precedence;
const href = props.href;
const nonce = props.nonce;
if (
formatContext.insertionMode === SVG_MODE ||
noscriptTagInScope ||
props.itemProp != null ||
typeof precedence !== 'string' ||
typeof href !== 'string' ||
href === ''
) {
return pushStyleImpl(target, props);
}
if (__DEV__) {
if (href.includes(' ')) {
console.error(
'React expected the `href` prop for a <style> tag opting into hoisting semantics using the `precedence` prop to not have any spaces but ecountered spaces instead. using spaces in this prop will cause hydration of this style to fail on the client. The href for the <style> where this ocurred is "%s".',
href,
);
}
}
const key = getResourceKey(href);
let styleQueue = renderState.styles.get(precedence);
const hasKey = resumableState.styleResources.hasOwnProperty(key);
const resourceState = hasKey ? resumableState.styleResources[key] : undefined;
if (resourceState !== EXISTS) {
resumableState.styleResources[key] = EXISTS;
if (__DEV__) {
if (resourceState) {
console.error(
'React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.',
href,
);
}
}
if (!styleQueue) {
styleQueue = {
precedence: stringToChunk(escapeTextForBrowser(precedence)),
rules: ([]: Array<Chunk | PrecomputedChunk>),
hrefs: ([]: Array<Chunk | PrecomputedChunk>),
sheets: (new Map(): Map<string, StylesheetResource>),
};
renderState.styles.set(precedence, styleQueue);
}
const nonceStyle = renderState.nonce.style;
if (!nonceStyle || nonceStyle === nonce) {
if (__DEV__) {
if (!nonceStyle && nonce) {
console.error(
'React encountered a style tag with `precedence` "%s" and `nonce` "%s". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
precedence,
nonce,
);
}
}
styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
pushStyleContents(styleQueue.rules, props);
} else if (__DEV__) {
console.error(
'React encountered a style tag with `precedence` "%s" and `nonce` "%s". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "%s" that was included with this render.',
precedence,
nonce,
nonceStyle,
);
}
}
if (styleQueue) {
if (hoistableState) {
hoistableState.styles.add(styleQueue);
}
}
if (textEmbedded) {
target.push(textSeparator);
}
}
function escapeStyleTextContent(styleText: string) {
if (__DEV__) {
checkHtmlStringCoercion(styleText);
}
return ('' + styleText).replace(styleRegex, styleReplacer);
}
const styleRegex = /(<\/|<)(s)(tyle)/gi;
const styleReplacer = (
match: string,
prefix: string,
s: string,
suffix: string,
) => `${prefix}${s === 's' ? '\\73 ' : '\\53 '}${suffix}`;
function pushStyleImpl(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
): ReactNodeList {
target.push(startChunkForTag('style'));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
target.push(endOfStartTag);
const child = Array.isArray(children)
? children.length < 2
? children[0]
: null
: children;
if (
typeof child !== 'function' &&
typeof child !== 'symbol' &&
child !== null &&
child !== undefined
) {
target.push(stringToChunk(escapeStyleTextContent(child)));
}
pushInnerHTML(target, innerHTML, children);
target.push(endChunkForTag('style'));
return null;
}
function pushStyleContents(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
): void {
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
}
}
}
const child = Array.isArray(children)
? children.length < 2
? children[0]
: null
: children;
if (
typeof child !== 'function' &&
typeof child !== 'symbol' &&
child !== null &&
child !== undefined
) {
target.push(stringToChunk(escapeStyleTextContent(child)));
}
pushInnerHTML(target, innerHTML, children);
return;
}
function pushImg(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
formatContext: FormatContext,
): null {
const pictureOrNoScriptTagInScope =
formatContext.tagScope & (PICTURE_SCOPE | NOSCRIPT_SCOPE);
const {src, srcSet} = props;
if (
props.loading !== 'lazy' &&
(src || srcSet) &&
(typeof src === 'string' || src == null) &&
(typeof srcSet === 'string' || srcSet == null) &&
props.fetchPriority !== 'low' &&
!pictureOrNoScriptTagInScope &&
!(
typeof src === 'string' &&
src[4] === ':' &&
(src[0] === 'd' || src[0] === 'D') &&
(src[1] === 'a' || src[1] === 'A') &&
(src[2] === 't' || src[2] === 'T') &&
(src[3] === 'a' || src[3] === 'A')
) &&
!(
typeof srcSet === 'string' &&
srcSet[4] === ':' &&
(srcSet[0] === 'd' || srcSet[0] === 'D') &&
(srcSet[1] === 'a' || srcSet[1] === 'A') &&
(srcSet[2] === 't' || srcSet[2] === 'T') &&
(srcSet[3] === 'a' || srcSet[3] === 'A')
)
) {
const sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
const key = getImageResourceKey(src, srcSet, sizes);
const promotablePreloads = renderState.preloads.images;
let resource = promotablePreloads.get(key);
if (resource) {
if (
props.fetchPriority === 'high' ||
renderState.highImagePreloads.size < 10
) {
promotablePreloads.delete(key);
renderState.highImagePreloads.add(resource);
}
} else if (!resumableState.imageResources.hasOwnProperty(key)) {
resumableState.imageResources[key] = PRELOAD_NO_CREDS;
const crossOrigin = getCrossOriginString(props.crossOrigin);
const headers = renderState.headers;
let header;
if (
headers &&
headers.remainingCapacity > 0 &&
typeof props.srcSet !== 'string' &&
(props.fetchPriority === 'high' ||
headers.highImagePreloads.length < 500) &&
((header = getPreloadAsHeader(src, 'image', {
imageSrcSet: props.srcSet,
imageSizes: props.sizes,
crossOrigin,
integrity: props.integrity,
nonce: props.nonce,
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.refererPolicy,
})),
(headers.remainingCapacity -= header.length + 2) >= 0)
) {
renderState.resets.image[key] = PRELOAD_NO_CREDS;
if (headers.highImagePreloads) {
headers.highImagePreloads += ', ';
}
headers.highImagePreloads += header;
} else {
resource = [];
pushLinkImpl(
resource,
({
rel: 'preload',
as: 'image',
href: srcSet ? undefined : src,
imageSrcSet: srcSet,
imageSizes: sizes,
crossOrigin: crossOrigin,
integrity: props.integrity,
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy,
}: PreloadProps),
);
if (
props.fetchPriority === 'high' ||
renderState.highImagePreloads.size < 10
) {
renderState.highImagePreloads.add(resource);
} else {
renderState.bulkPreloads.add(resource);
promotablePreloads.set(key, resource);
}
}
}
}
return pushSelfClosing(target, props, 'img', formatContext);
}
function pushSelfClosing(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
tag: string,
formatContext: FormatContext,
): null {
target.push(startChunkForTag(tag));
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
case 'dangerouslySetInnerHTML':
throw new Error(
`${tag} is a self-closing tag and must neither have \`children\` nor ` +
'use `dangerouslySetInnerHTML`.',
);
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTagSelfClosing);
return null;
}
function pushStartMenuItem(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag('menuitem'));
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
case 'dangerouslySetInnerHTML':
throw new Error(
'menuitems cannot have `children` nor `dangerouslySetInnerHTML`.',
);
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
return null;
}
function pushTitle(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
renderState: RenderState,
formatContext: FormatContext,
): ReactNodeList {
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
const isFallback = formatContext.tagScope & FALLBACK_SCOPE;
if (__DEV__) {
if (hasOwnProperty.call(props, 'children')) {
const children = props.children;
const child = Array.isArray(children)
? children.length < 2
? children[0]
: null
: children;
if (Array.isArray(children) && children.length > 1) {
console.error(
'React expects the `children` prop of <title> tags to be a string, number, bigint, or object with a novel `toString` method but found an Array with length %s instead.' +
' Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert `children` of <title> tags to a single string value' +
' which is why Arrays of length greater than 1 are not supported. When using JSX it can be common to combine text nodes and value nodes.' +
' For example: <title>hello {nameOfUser}</title>. While not immediately apparent, `children` in this case is an Array with length 2. If your `children` prop' +
' is using this form try rewriting it using a template string: <title>{`hello ${nameOfUser}`}</title>.',
children.length,
);
} else if (typeof child === 'function' || typeof child === 'symbol') {
const childType =
typeof child === 'function' ? 'a Function' : 'a Sybmol';
console.error(
'React expect children of <title> tags to be a string, number, bigint, or object with a novel `toString` method but found %s instead.' +
' Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title>' +
' tags to a single string value.',
childType,
);
} else if (child && child.toString === {}.toString) {
if (child.$$typeof != null) {
console.error(
'React expects the `children` prop of <title> tags to be a string, number, bigint, or object with a novel `toString` method but found an object that appears to be' +
' a React element which never implements a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to' +
' be able to convert children of <title> tags to a single string value which is why rendering React elements is not supported. If the `children` of <title> is' +
' a React Component try moving the <title> tag into that component. If the `children` of <title> is some HTML markup change it to be Text only to be valid HTML.',
);
} else {
console.error(
'React expects the `children` prop of <title> tags to be a string, number, bigint, or object with a novel `toString` method but found an object that does not implement' +
' a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title> tags' +
' to a single string value. Using the default `toString` method available on every object is almost certainly an error. Consider whether the `children` of this <title>' +
' is an object in error and change it to a string or number value if so. Otherwise implement a `toString` method that React can use to produce a valid <title>.',
);
}
}
}
}
if (
formatContext.insertionMode !== SVG_MODE &&
!noscriptTagInScope &&
props.itemProp == null
) {
if (isFallback) {
return null;
} else {
pushTitleImpl(renderState.hoistableChunks, props);
}
} else {
return pushTitleImpl(target, props);
}
}
function pushTitleImpl(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
): null {
target.push(startChunkForTag('title'));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
target.push(endOfStartTag);
const child = Array.isArray(children)
? children.length < 2
? children[0]
: null
: children;
if (
typeof child !== 'function' &&
typeof child !== 'symbol' &&
child !== null &&
child !== undefined
) {
target.push(stringToChunk(escapeTextForBrowser('' + child)));
}
pushInnerHTML(target, innerHTML, children);
target.push(endChunkForTag('title'));
return null;
}
const headPreambleContributionChunk = stringToPrecomputedChunk('<!--head-->');
const bodyPreambleContributionChunk = stringToPrecomputedChunk('<!--body-->');
const htmlPreambleContributionChunk = stringToPrecomputedChunk('<!--html-->');
function pushStartHead(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
renderState: RenderState,
preambleState: null | PreambleState,
formatContext: FormatContext,
): ReactNodeList {
if (formatContext.insertionMode < HTML_MODE) {
const preamble = preambleState || renderState.preamble;
if (preamble.headChunks) {
throw new Error(`The ${'`<head>`'} tag may only be rendered once.`);
}
if (preambleState !== null) {
target.push(headPreambleContributionChunk);
}
preamble.headChunks = [];
return pushStartSingletonElement(
preamble.headChunks,
props,
'head',
formatContext,
);
} else {
return pushStartGenericElement(target, props, 'head', formatContext);
}
}
function pushStartBody(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
renderState: RenderState,
preambleState: null | PreambleState,
formatContext: FormatContext,
): ReactNodeList {
if (formatContext.insertionMode < HTML_MODE) {
const preamble = preambleState || renderState.preamble;
if (preamble.bodyChunks) {
throw new Error(`The ${'`<body>`'} tag may only be rendered once.`);
}
if (preambleState !== null) {
target.push(bodyPreambleContributionChunk);
}
preamble.bodyChunks = [];
return pushStartSingletonElement(
preamble.bodyChunks,
props,
'body',
formatContext,
);
} else {
return pushStartGenericElement(target, props, 'body', formatContext);
}
}
function pushStartHtml(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
renderState: RenderState,
preambleState: null | PreambleState,
formatContext: FormatContext,
): ReactNodeList {
if (formatContext.insertionMode === ROOT_HTML_MODE) {
const preamble = preambleState || renderState.preamble;
if (preamble.htmlChunks) {
throw new Error(`The ${'`<html>`'} tag may only be rendered once.`);
}
if (preambleState !== null) {
target.push(htmlPreambleContributionChunk);
}
preamble.htmlChunks = [DOCTYPE];
return pushStartSingletonElement(
preamble.htmlChunks,
props,
'html',
formatContext,
);
} else {
return pushStartGenericElement(target, props, 'html', formatContext);
}
}
function pushScript(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
textEmbedded: boolean,
formatContext: FormatContext,
): null {
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
const asyncProp = props.async;
if (
typeof props.src !== 'string' ||
!props.src ||
!(
asyncProp &&
typeof asyncProp !== 'function' &&
typeof asyncProp !== 'symbol'
) ||
props.onLoad ||
props.onError ||
formatContext.insertionMode === SVG_MODE ||
noscriptTagInScope ||
props.itemProp != null
) {
return pushScriptImpl(target, props);
}
const src = props.src;
const key = getResourceKey(src);
let resources, preloads;
if (props.type === 'module') {
resources = resumableState.moduleScriptResources;
preloads = renderState.preloads.moduleScripts;
} else {
resources = resumableState.scriptResources;
preloads = renderState.preloads.scripts;
}
const hasKey = resources.hasOwnProperty(key);
const resourceState = hasKey ? resources[key] : undefined;
if (resourceState !== EXISTS) {
resources[key] = EXISTS;
let scriptProps = props;
if (resourceState) {
const preloadState: Preloaded | PreloadedWithCredentials = resourceState;
if (preloadState.length === 2) {
scriptProps = {...props};
adoptPreloadCredentials(scriptProps, preloadState);
}
const preloadResource = preloads.get(key);
if (preloadResource) {
preloadResource.length = 0;
}
}
const resource: Resource = [];
renderState.scripts.add(resource);
pushScriptImpl(resource, scriptProps);
}
if (textEmbedded) {
target.push(textSeparator);
}
return null;
}
function pushScriptImpl(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
): null {
target.push(startChunkForTag('script'));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
target.push(endOfStartTag);
if (__DEV__) {
if (children != null && typeof children !== 'string') {
const descriptiveStatement =
typeof children === 'number'
? 'a number for children'
: Array.isArray(children)
? 'an array for children'
: 'something unexpected for children';
console.error(
'A script element was rendered with %s. If script element has children it must be a single string.' +
' Consider using dangerouslySetInnerHTML or passing a plain string as children.',
descriptiveStatement,
);
}
}
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
target.push(stringToChunk(escapeEntireInlineScriptContent(children)));
}
target.push(endChunkForTag('script'));
return null;
}
function pushStartSingletonElement(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
tag: string,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag(tag));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
return children;
}
function pushStartGenericElement(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
tag: string,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag(tag));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
if (typeof children === 'string') {
target.push(stringToChunk(encodeHTMLTextNode(children)));
return null;
}
return children;
}
function pushStartCustomElement(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
tag: string,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag(tag));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
let propValue = props[propKey];
if (propValue == null) {
continue;
}
let attributeName = propKey;
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
case 'style':
pushStyleAttribute(target, propValue);
break;
case 'suppressContentEditableWarning':
case 'suppressHydrationWarning':
case 'ref':
break;
case 'className':
attributeName = 'class';
default:
if (
isAttributeNameSafe(propKey) &&
typeof propValue !== 'function' &&
typeof propValue !== 'symbol'
) {
if (propValue === false) {
continue;
} else if (propValue === true) {
propValue = '';
} else if (typeof propValue === 'object') {
continue;
}
target.push(
attributeSeparator,
stringToChunk(attributeName),
attributeAssign,
stringToChunk(escapeTextForBrowser(propValue)),
attributeEnd,
);
}
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
return children;
}
const leadingNewline = stringToPrecomputedChunk('\n');
function pushStartPreformattedElement(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
tag: string,
formatContext: FormatContext,
): ReactNodeList {
target.push(startChunkForTag(tag));
let children = null;
let innerHTML = null;
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'children':
children = propValue;
break;
case 'dangerouslySetInnerHTML':
innerHTML = propValue;
break;
default:
pushAttribute(target, propKey, propValue);
break;
}
}
}
pushViewTransitionAttributes(target, formatContext);
target.push(endOfStartTag);
if (innerHTML != null) {
if (children != null) {
throw new Error(
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
);
}
if (typeof innerHTML !== 'object' || !('__html' in innerHTML)) {
throw new Error(
'`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
'Please visit https://react.dev/link/dangerously-set-inner-html ' +
'for more information.',
);
}
const html = innerHTML.__html;
if (html !== null && html !== undefined) {
if (typeof html === 'string' && html.length > 0 && html[0] === '\n') {
target.push(leadingNewline, stringToChunk(html));
} else {
if (__DEV__) {
checkHtmlStringCoercion(html);
}
target.push(stringToChunk('' + html));
}
}
}
if (typeof children === 'string' && children[0] === '\n') {
target.push(leadingNewline);
}
return children;
}
const VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/;
const validatedTagCache = new Map<string, PrecomputedChunk>();
function startChunkForTag(tag: string): PrecomputedChunk {
let tagStartChunk = validatedTagCache.get(tag);
if (tagStartChunk === undefined) {
if (!VALID_TAG_REGEX.test(tag)) {
throw new Error(`Invalid tag: ${tag}`);
}
tagStartChunk = stringToPrecomputedChunk('<' + tag);
validatedTagCache.set(tag, tagStartChunk);
}
return tagStartChunk;
}
export const doctypeChunk: PrecomputedChunk =
stringToPrecomputedChunk('<!DOCTYPE html>');
import {doctypeChunk as DOCTYPE} from 'react-server/src/ReactFizzConfig';
export function pushStartInstance(
target: Array<Chunk | PrecomputedChunk>,
type: string,
props: Object,
resumableState: ResumableState,
renderState: RenderState,
preambleState: null | PreambleState,
hoistableState: null | HoistableState,
formatContext: FormatContext,
textEmbedded: boolean,
): ReactNodeList {
if (__DEV__) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, null);
if (
!props.suppressContentEditableWarning &&
props.contentEditable &&
props.children != null
) {
console.error(
'A component is `contentEditable` and contains `children` managed by ' +
'React. It is now your responsibility to guarantee that none of ' +
'those nodes are unexpectedly modified or duplicated. This is ' +
'probably not intentional.',
);
}
if (
formatContext.insertionMode !== SVG_MODE &&
formatContext.insertionMode !== MATHML_MODE
) {
if (type.indexOf('-') === -1 && type.toLowerCase() !== type) {
console.error(
'<%s /> is using incorrect casing. ' +
'Use PascalCase for React components, ' +
'or lowercase for HTML elements.',
type,
);
}
}
}
switch (type) {
case 'div':
case 'span':
case 'svg':
case 'path':
break;
case 'a':
return pushStartAnchor(target, props, formatContext);
case 'g':
case 'p':
case 'li':
break;
case 'select':
return pushStartSelect(target, props, formatContext);
case 'option':
return pushStartOption(target, props, formatContext);
case 'textarea':
return pushStartTextArea(target, props, formatContext);
case 'input':
return pushInput(
target,
props,
resumableState,
renderState,
formatContext,
);
case 'button':
return pushStartButton(
target,
props,
resumableState,
renderState,
formatContext,
);
case 'form':
return pushStartForm(
target,
props,
resumableState,
renderState,
formatContext,
);
case 'menuitem':
return pushStartMenuItem(target, props, formatContext);
case 'object':
return pushStartObject(target, props, formatContext);
case 'title':
return pushTitle(target, props, renderState, formatContext);
case 'link':
return pushLink(
target,
props,
resumableState,
renderState,
hoistableState,
textEmbedded,
formatContext,
);
case 'script':
return pushScript(
target,
props,
resumableState,
renderState,
textEmbedded,
formatContext,
);
case 'style':
return pushStyle(
target,
props,
resumableState,
renderState,
hoistableState,
textEmbedded,
formatContext,
);
case 'meta':
return pushMeta(target, props, renderState, textEmbedded, formatContext);
case 'listing':
case 'pre': {
return pushStartPreformattedElement(target, props, type, formatContext);
}
case 'img': {
return pushImg(target, props, resumableState, renderState, formatContext);
}
case 'base':
case 'area':
case 'br':
case 'col':
case 'embed':
case 'hr':
case 'keygen':
case 'param':
case 'source':
case 'track':
case 'wbr': {
return pushSelfClosing(target, props, type, formatContext);
}
case 'annotation-xml':
case 'color-profile':
case 'font-face':
case 'font-face-src':
case 'font-face-uri':
case 'font-face-format':
case 'font-face-name':
case 'missing-glyph': {
break;
}
case 'head':
return pushStartHead(
target,
props,
renderState,
preambleState,
formatContext,
);
case 'body':
return pushStartBody(
target,
props,
renderState,
preambleState,
formatContext,
);
case 'html': {
return pushStartHtml(
target,
props,
renderState,
preambleState,
formatContext,
);
}
default: {
if (type.indexOf('-') !== -1) {
return pushStartCustomElement(target, props, type, formatContext);
}
}
}
return pushStartGenericElement(target, props, type, formatContext);
}
const endTagCache = new Map<string, PrecomputedChunk>();
function endChunkForTag(tag: string): PrecomputedChunk {
let chunk = endTagCache.get(tag);
if (chunk === undefined) {
chunk = stringToPrecomputedChunk('</' + tag + '>');
endTagCache.set(tag, chunk);
}
return chunk;
}
export function pushEndInstance(
target: Array<Chunk | PrecomputedChunk>,
type: string,
props: Object,
resumableState: ResumableState,
formatContext: FormatContext,
): void {
switch (type) {
case 'title':
case 'style':
case 'script':
case 'area':
case 'base':
case 'br':
case 'col':
case 'embed':
case 'hr':
case 'img':
case 'input':
case 'keygen':
case 'link':
case 'meta':
case 'param':
case 'source':
case 'track':
case 'wbr': {
return;
}
case 'body': {
if (formatContext.insertionMode <= HTML_HTML_MODE) {
resumableState.hasBody = true;
return;
}
break;
}
case 'html':
if (formatContext.insertionMode === ROOT_HTML_MODE) {
resumableState.hasHtml = true;
return;
}
break;
case 'head':
if (formatContext.insertionMode <= HTML_HTML_MODE) {
return;
}
break;
}
target.push(endChunkForTag(type));
}
export function hoistPreambleState(
renderState: RenderState,
preambleState: PreambleState,
) {
const rootPreamble = renderState.preamble;
if (rootPreamble.htmlChunks === null && preambleState.htmlChunks) {
rootPreamble.htmlChunks = preambleState.htmlChunks;
}
if (rootPreamble.headChunks === null && preambleState.headChunks) {
rootPreamble.headChunks = preambleState.headChunks;
}
if (rootPreamble.bodyChunks === null && preambleState.bodyChunks) {
rootPreamble.bodyChunks = preambleState.bodyChunks;
}
}
export function isPreambleReady(
renderState: RenderState,
hasPendingPreambles: boolean,
): boolean {
const preamble = renderState.preamble;
return (
hasPendingPreambles === false ||
!!(preamble.headChunks && preamble.bodyChunks)
);
}
function writeBootstrap(
destination: Destination,
renderState: RenderState,
): boolean {
const bootstrapChunks = renderState.bootstrapChunks;
let i = 0;
for (; i < bootstrapChunks.length - 1; i++) {
writeChunk(destination, bootstrapChunks[i]);
}
if (i < bootstrapChunks.length) {
const lastChunk = bootstrapChunks[i];
bootstrapChunks.length = 0;
return writeChunkAndReturn(destination, lastChunk);
}
return true;
}
const shellTimeRuntimeScript = stringToPrecomputedChunk(markShellTime);
function writeShellTimeInstruction(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
): boolean {
if (
enableFizzExternalRuntime &&
resumableState.streamingFormat !== ScriptStreamingFormat
) {
return true;
}
if ((resumableState.instructions & SentMarkShellTime) !== NothingSent) {
return true;
}
resumableState.instructions |= SentMarkShellTime;
writeChunk(destination, renderState.startInlineScript);
writeCompletedShellIdAttribute(destination, resumableState);
writeChunk(destination, endOfStartTag);
writeChunk(destination, shellTimeRuntimeScript);
return writeChunkAndReturn(destination, endInlineScript);
}
export function writeCompletedRoot(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
isComplete: boolean,
): boolean {
if (!isComplete) {
writeShellTimeInstruction(destination, resumableState, renderState);
}
if (enableFizzBlockingRender) {
const preamble = renderState.preamble;
if (preamble.htmlChunks || preamble.headChunks) {
if (
(resumableState.instructions & SentCompletedShellId) ===
NothingSent
) {
writeChunk(destination, startChunkForTag('template'));
writeCompletedShellIdAttribute(destination, resumableState);
writeChunk(destination, endOfStartTag);
writeChunk(destination, endChunkForTag('template'));
}
}
}
return writeBootstrap(destination, renderState);
}
const placeholder1 = stringToPrecomputedChunk('<template id="');
const placeholder2 = stringToPrecomputedChunk('"></template>');
export function writePlaceholder(
destination: Destination,
renderState: RenderState,
id: number,
): boolean {
writeChunk(destination, placeholder1);
writeChunk(destination, renderState.placeholderPrefix);
const formattedID = stringToChunk(id.toString(16));
writeChunk(destination, formattedID);
return writeChunkAndReturn(destination, placeholder2);
}
const startActivityBoundary = stringToPrecomputedChunk('<!--&-->');
const endActivityBoundary = stringToPrecomputedChunk('<!--/&-->');
export function pushStartActivityBoundary(
target: Array<Chunk | PrecomputedChunk>,
renderState: RenderState,
): void {
target.push(startActivityBoundary);
}
export function pushEndActivityBoundary(
target: Array<Chunk | PrecomputedChunk>,
renderState: RenderState,
): void {
target.push(endActivityBoundary);
}
const startCompletedSuspenseBoundary = stringToPrecomputedChunk('<!--$-->');
const startPendingSuspenseBoundary1 = stringToPrecomputedChunk(
'<!--$?--><template id="',
);
const startPendingSuspenseBoundary2 = stringToPrecomputedChunk('"></template>');
const startClientRenderedSuspenseBoundary =
stringToPrecomputedChunk('<!--$!-->');
const endSuspenseBoundary = stringToPrecomputedChunk('<!--/$-->');
const clientRenderedSuspenseBoundaryError1 =
stringToPrecomputedChunk('<template');
const clientRenderedSuspenseBoundaryErrorAttrInterstitial =
stringToPrecomputedChunk('"');
const clientRenderedSuspenseBoundaryError1A =
stringToPrecomputedChunk(' data-dgst="');
const clientRenderedSuspenseBoundaryError1B =
stringToPrecomputedChunk(' data-msg="');
const clientRenderedSuspenseBoundaryError1C =
stringToPrecomputedChunk(' data-stck="');
const clientRenderedSuspenseBoundaryError1D =
stringToPrecomputedChunk(' data-cstck="');
const clientRenderedSuspenseBoundaryError2 =
stringToPrecomputedChunk('></template>');
export function writeStartCompletedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
): boolean {
return writeChunkAndReturn(destination, startCompletedSuspenseBoundary);
}
export function writeStartPendingSuspenseBoundary(
destination: Destination,
renderState: RenderState,
id: number,
): boolean {
writeChunk(destination, startPendingSuspenseBoundary1);
if (id === null) {
throw new Error(
'An ID must have been assigned before we can complete the boundary.',
);
}
writeChunk(destination, renderState.boundaryPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startPendingSuspenseBoundary2);
}
export function writeStartClientRenderedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
errorDigest: ?string,
errorMessage: ?string,
errorStack: ?string,
errorComponentStack: ?string,
): boolean {
let result;
result = writeChunkAndReturn(
destination,
startClientRenderedSuspenseBoundary,
);
writeChunk(destination, clientRenderedSuspenseBoundaryError1);
if (errorDigest) {
writeChunk(destination, clientRenderedSuspenseBoundaryError1A);
writeChunk(destination, stringToChunk(escapeTextForBrowser(errorDigest)));
writeChunk(
destination,
clientRenderedSuspenseBoundaryErrorAttrInterstitial,
);
}
if (__DEV__) {
if (errorMessage) {
writeChunk(destination, clientRenderedSuspenseBoundaryError1B);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(errorMessage)),
);
writeChunk(
destination,
clientRenderedSuspenseBoundaryErrorAttrInterstitial,
);
}
if (errorStack) {
writeChunk(destination, clientRenderedSuspenseBoundaryError1C);
writeChunk(destination, stringToChunk(escapeTextForBrowser(errorStack)));
writeChunk(
destination,
clientRenderedSuspenseBoundaryErrorAttrInterstitial,
);
}
if (errorComponentStack) {
writeChunk(destination, clientRenderedSuspenseBoundaryError1D);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(errorComponentStack)),
);
writeChunk(
destination,
clientRenderedSuspenseBoundaryErrorAttrInterstitial,
);
}
}
result = writeChunkAndReturn(
destination,
clientRenderedSuspenseBoundaryError2,
);
return result;
}
export function writeEndCompletedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
): boolean {
return writeChunkAndReturn(destination, endSuspenseBoundary);
}
export function writeEndPendingSuspenseBoundary(
destination: Destination,
renderState: RenderState,
): boolean {
return writeChunkAndReturn(destination, endSuspenseBoundary);
}
export function writeEndClientRenderedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
): boolean {
return writeChunkAndReturn(destination, endSuspenseBoundary);
}
const startSegmentHTML = stringToPrecomputedChunk('<div hidden id="');
const startSegmentHTML2 = stringToPrecomputedChunk('">');
const endSegmentHTML = stringToPrecomputedChunk('</div>');
const startSegmentSVG = stringToPrecomputedChunk(
'<svg aria-hidden="true" style="display:none" id="',
);
const startSegmentSVG2 = stringToPrecomputedChunk('">');
const endSegmentSVG = stringToPrecomputedChunk('</svg>');
const startSegmentMathML = stringToPrecomputedChunk(
'<math aria-hidden="true" style="display:none" id="',
);
const startSegmentMathML2 = stringToPrecomputedChunk('">');
const endSegmentMathML = stringToPrecomputedChunk('</math>');
const startSegmentTable = stringToPrecomputedChunk('<table hidden id="');
const startSegmentTable2 = stringToPrecomputedChunk('">');
const endSegmentTable = stringToPrecomputedChunk('</table>');
const startSegmentTableBody = stringToPrecomputedChunk(
'<table hidden><tbody id="',
);
const startSegmentTableBody2 = stringToPrecomputedChunk('">');
const endSegmentTableBody = stringToPrecomputedChunk('</tbody></table>');
const startSegmentTableRow = stringToPrecomputedChunk('<table hidden><tr id="');
const startSegmentTableRow2 = stringToPrecomputedChunk('">');
const endSegmentTableRow = stringToPrecomputedChunk('</tr></table>');
const startSegmentColGroup = stringToPrecomputedChunk(
'<table hidden><colgroup id="',
);
const startSegmentColGroup2 = stringToPrecomputedChunk('">');
const endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
export function writeStartSegment(
destination: Destination,
renderState: RenderState,
formatContext: FormatContext,
id: number,
): boolean {
switch (formatContext.insertionMode) {
case ROOT_HTML_MODE:
case HTML_HTML_MODE:
case HTML_HEAD_MODE:
case HTML_MODE: {
writeChunk(destination, startSegmentHTML);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentHTML2);
}
case SVG_MODE: {
writeChunk(destination, startSegmentSVG);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentSVG2);
}
case MATHML_MODE: {
writeChunk(destination, startSegmentMathML);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentMathML2);
}
case HTML_TABLE_MODE: {
writeChunk(destination, startSegmentTable);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentTable2);
}
case HTML_TABLE_BODY_MODE: {
writeChunk(destination, startSegmentTableBody);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentTableBody2);
}
case HTML_TABLE_ROW_MODE: {
writeChunk(destination, startSegmentTableRow);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentTableRow2);
}
case HTML_COLGROUP_MODE: {
writeChunk(destination, startSegmentColGroup);
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
return writeChunkAndReturn(destination, startSegmentColGroup2);
}
default: {
throw new Error('Unknown insertion mode. This is a bug in React.');
}
}
}
export function writeEndSegment(
destination: Destination,
formatContext: FormatContext,
): boolean {
switch (formatContext.insertionMode) {
case ROOT_HTML_MODE:
case HTML_HTML_MODE:
case HTML_HEAD_MODE:
case HTML_MODE: {
return writeChunkAndReturn(destination, endSegmentHTML);
}
case SVG_MODE: {
return writeChunkAndReturn(destination, endSegmentSVG);
}
case MATHML_MODE: {
return writeChunkAndReturn(destination, endSegmentMathML);
}
case HTML_TABLE_MODE: {
return writeChunkAndReturn(destination, endSegmentTable);
}
case HTML_TABLE_BODY_MODE: {
return writeChunkAndReturn(destination, endSegmentTableBody);
}
case HTML_TABLE_ROW_MODE: {
return writeChunkAndReturn(destination, endSegmentTableRow);
}
case HTML_COLGROUP_MODE: {
return writeChunkAndReturn(destination, endSegmentColGroup);
}
default: {
throw new Error('Unknown insertion mode. This is a bug in React.');
}
}
}
const completeSegmentScript1Full = stringToPrecomputedChunk(
completeSegmentFunction + '$RS("',
);
const completeSegmentScript1Partial = stringToPrecomputedChunk('$RS("');
const completeSegmentScript2 = stringToPrecomputedChunk('","');
const completeSegmentScriptEnd = stringToPrecomputedChunk('")</script>');
const completeSegmentData1 = stringToPrecomputedChunk(
'<template data-rsi="" data-sid="',
);
const completeSegmentData2 = stringToPrecomputedChunk('" data-pid="');
const completeSegmentDataEnd = dataElementQuotedEnd;
export function writeCompletedSegmentInstruction(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
contentSegmentID: number,
): boolean {
const scriptFormat =
!enableFizzExternalRuntime ||
resumableState.streamingFormat === ScriptStreamingFormat;
if (scriptFormat) {
writeChunk(destination, renderState.startInlineScript);
writeChunk(destination, endOfStartTag);
if (
(resumableState.instructions & SentCompleteSegmentFunction) ===
NothingSent
) {
resumableState.instructions |= SentCompleteSegmentFunction;
writeChunk(destination, completeSegmentScript1Full);
} else {
writeChunk(destination, completeSegmentScript1Partial);
}
} else {
writeChunk(destination, completeSegmentData1);
}
writeChunk(destination, renderState.segmentPrefix);
const formattedID = stringToChunk(contentSegmentID.toString(16));
writeChunk(destination, formattedID);
if (scriptFormat) {
writeChunk(destination, completeSegmentScript2);
} else {
writeChunk(destination, completeSegmentData2);
}
writeChunk(destination, renderState.placeholderPrefix);
writeChunk(destination, formattedID);
if (scriptFormat) {
return writeChunkAndReturn(destination, completeSegmentScriptEnd);
} else {
return writeChunkAndReturn(destination, completeSegmentDataEnd);
}
}
const completeBoundaryScriptFunctionOnly = stringToPrecomputedChunk(
completeBoundaryFunction,
);
const completeBoundaryUpgradeToViewTransitionsInstruction = stringToChunk(
upgradeToViewTransitionsInstruction,
);
const completeBoundaryScript1Partial = stringToPrecomputedChunk('$RC("');
const completeBoundaryWithStylesScript1FullPartial = stringToPrecomputedChunk(
styleInsertionFunction + '$RR("',
);
const completeBoundaryWithStylesScript1Partial =
stringToPrecomputedChunk('$RR("');
const completeBoundaryScript2 = stringToPrecomputedChunk('","');
const completeBoundaryScript3a = stringToPrecomputedChunk('",');
const completeBoundaryScript3b = stringToPrecomputedChunk('"');
const completeBoundaryScriptEnd = stringToPrecomputedChunk(')</script>');
const completeBoundaryData1 = stringToPrecomputedChunk(
'<template data-rci="" data-bid="',
);
const completeBoundaryWithStylesData1 = stringToPrecomputedChunk(
'<template data-rri="" data-bid="',
);
const completeBoundaryData2 = stringToPrecomputedChunk('" data-sid="');
const completeBoundaryData3a = stringToPrecomputedChunk('" data-sty="');
const completeBoundaryDataEnd = dataElementQuotedEnd;
export function writeCompletedBoundaryInstruction(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
id: number,
hoistableState: HoistableState,
): boolean {
const requiresStyleInsertion = renderState.stylesToHoist;
const requiresViewTransitions =
enableViewTransition &&
(resumableState.instructions & NeedUpgradeToViewTransitions) !==
NothingSent;
renderState.stylesToHoist = false;
const scriptFormat =
!enableFizzExternalRuntime ||
resumableState.streamingFormat === ScriptStreamingFormat;
if (scriptFormat) {
writeChunk(destination, renderState.startInlineScript);
writeChunk(destination, endOfStartTag);
if (requiresStyleInsertion) {
if (
(resumableState.instructions & SentClientRenderFunction) ===
NothingSent
) {
resumableState.instructions |= SentClientRenderFunction;
writeChunk(destination, clientRenderScriptFunctionOnly);
}
if (
(resumableState.instructions & SentCompleteBoundaryFunction) ===
NothingSent
) {
resumableState.instructions |= SentCompleteBoundaryFunction;
writeChunk(destination, completeBoundaryScriptFunctionOnly);
}
if (
requiresViewTransitions &&
(resumableState.instructions & SentUpgradeToViewTransitions) ===
NothingSent
) {
resumableState.instructions |= SentUpgradeToViewTransitions;
writeChunk(
destination,
completeBoundaryUpgradeToViewTransitionsInstruction,
);
}
if (
(resumableState.instructions & SentStyleInsertionFunction) ===
NothingSent
) {
resumableState.instructions |= SentStyleInsertionFunction;
writeChunk(destination, completeBoundaryWithStylesScript1FullPartial);
} else {
writeChunk(destination, completeBoundaryWithStylesScript1Partial);
}
} else {
if (
(resumableState.instructions & SentCompleteBoundaryFunction) ===
NothingSent
) {
resumableState.instructions |= SentCompleteBoundaryFunction;
writeChunk(destination, completeBoundaryScriptFunctionOnly);
}
if (
requiresViewTransitions &&
(resumableState.instructions & SentUpgradeToViewTransitions) ===
NothingSent
) {
resumableState.instructions |= SentUpgradeToViewTransitions;
writeChunk(
destination,
completeBoundaryUpgradeToViewTransitionsInstruction,
);
}
writeChunk(destination, completeBoundaryScript1Partial);
}
} else {
if (requiresStyleInsertion) {
writeChunk(destination, completeBoundaryWithStylesData1);
} else {
writeChunk(destination, completeBoundaryData1);
}
}
const idChunk = stringToChunk(id.toString(16));
writeChunk(destination, renderState.boundaryPrefix);
writeChunk(destination, idChunk);
if (scriptFormat) {
writeChunk(destination, completeBoundaryScript2);
} else {
writeChunk(destination, completeBoundaryData2);
}
writeChunk(destination, renderState.segmentPrefix);
writeChunk(destination, idChunk);
if (requiresStyleInsertion) {
if (scriptFormat) {
writeChunk(destination, completeBoundaryScript3a);
writeStyleResourceDependenciesInJS(destination, hoistableState);
} else {
writeChunk(destination, completeBoundaryData3a);
writeStyleResourceDependenciesInAttr(destination, hoistableState);
}
} else {
if (scriptFormat) {
writeChunk(destination, completeBoundaryScript3b);
}
}
let writeMore;
if (scriptFormat) {
writeMore = writeChunkAndReturn(destination, completeBoundaryScriptEnd);
} else {
writeMore = writeChunkAndReturn(destination, completeBoundaryDataEnd);
}
return writeBootstrap(destination, renderState) && writeMore;
}
const clientRenderScriptFunctionOnly =
stringToPrecomputedChunk(clientRenderFunction);
const clientRenderScript1Full = stringToPrecomputedChunk(
clientRenderFunction + ';$RX("',
);
const clientRenderScript1Partial = stringToPrecomputedChunk('$RX("');
const clientRenderScript1A = stringToPrecomputedChunk('"');
const clientRenderErrorScriptArgInterstitial = stringToPrecomputedChunk(',');
const clientRenderScriptEnd = stringToPrecomputedChunk(')</script>');
const clientRenderData1 = stringToPrecomputedChunk(
'<template data-rxi="" data-bid="',
);
const clientRenderData2 = stringToPrecomputedChunk('" data-dgst="');
const clientRenderData3 = stringToPrecomputedChunk('" data-msg="');
const clientRenderData4 = stringToPrecomputedChunk('" data-stck="');
const clientRenderData5 = stringToPrecomputedChunk('" data-cstck="');
const clientRenderDataEnd = dataElementQuotedEnd;
export function writeClientRenderBoundaryInstruction(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
id: number,
errorDigest: ?string,
errorMessage: ?string,
errorStack: ?string,
errorComponentStack: ?string,
): boolean {
const scriptFormat =
!enableFizzExternalRuntime ||
resumableState.streamingFormat === ScriptStreamingFormat;
if (scriptFormat) {
writeChunk(destination, renderState.startInlineScript);
writeChunk(destination, endOfStartTag);
if (
(resumableState.instructions & SentClientRenderFunction) ===
NothingSent
) {
resumableState.instructions |= SentClientRenderFunction;
writeChunk(destination, clientRenderScript1Full);
} else {
writeChunk(destination, clientRenderScript1Partial);
}
} else {
writeChunk(destination, clientRenderData1);
}
writeChunk(destination, renderState.boundaryPrefix);
writeChunk(destination, stringToChunk(id.toString(16)));
if (scriptFormat) {
writeChunk(destination, clientRenderScript1A);
}
if (errorDigest || errorMessage || errorStack || errorComponentStack) {
if (scriptFormat) {
writeChunk(destination, clientRenderErrorScriptArgInterstitial);
writeChunk(
destination,
stringToChunk(escapeJSStringsForInstructionScripts(errorDigest || '')),
);
} else {
writeChunk(destination, clientRenderData2);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(errorDigest || '')),
);
}
}
if (errorMessage || errorStack || errorComponentStack) {
if (scriptFormat) {
writeChunk(destination, clientRenderErrorScriptArgInterstitial);
writeChunk(
destination,
stringToChunk(escapeJSStringsForInstructionScripts(errorMessage || '')),
);
} else {
writeChunk(destination, clientRenderData3);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(errorMessage || '')),
);
}
}
if (errorStack || errorComponentStack) {
if (scriptFormat) {
writeChunk(destination, clientRenderErrorScriptArgInterstitial);
writeChunk(
destination,
stringToChunk(escapeJSStringsForInstructionScripts(errorStack || '')),
);
} else {
writeChunk(destination, clientRenderData4);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(errorStack || '')),
);
}
}
if (errorComponentStack) {
if (scriptFormat) {
writeChunk(destination, clientRenderErrorScriptArgInterstitial);
writeChunk(
destination,
stringToChunk(
escapeJSStringsForInstructionScripts(errorComponentStack),
),
);
} else {
writeChunk(destination, clientRenderData5);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(errorComponentStack)),
);
}
}
if (scriptFormat) {
return writeChunkAndReturn(destination, clientRenderScriptEnd);
} else {
return writeChunkAndReturn(destination, clientRenderDataEnd);
}
}
const regexForJSStringsInInstructionScripts = /[<\u2028\u2029]/g;
function escapeJSStringsForInstructionScripts(input: string): string {
const escaped = JSON.stringify(input);
return escaped.replace(regexForJSStringsInInstructionScripts, match => {
switch (match) {
case '<':
return '\\u003c';
case '\u2028':
return '\\u2028';
case '\u2029':
return '\\u2029';
default: {
throw new Error(
'escapeJSStringsForInstructionScripts encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React',
);
}
}
});
}
const regexForJSStringsInScripts = /[&><\u2028\u2029]/g;
function escapeJSObjectForInstructionScripts(input: Object): string {
const escaped = JSON.stringify(input);
return escaped.replace(regexForJSStringsInScripts, match => {
switch (match) {
case '&':
return '\\u0026';
case '>':
return '\\u003e';
case '<':
return '\\u003c';
case '\u2028':
return '\\u2028';
case '\u2029':
return '\\u2029';
default: {
throw new Error(
'escapeJSObjectForInstructionScripts encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React',
);
}
}
});
}
const lateStyleTagResourceOpen1 = stringToPrecomputedChunk(
' media="not all" data-precedence="',
);
const lateStyleTagResourceOpen2 = stringToPrecomputedChunk('" data-href="');
const lateStyleTagResourceOpen3 = stringToPrecomputedChunk('">');
const lateStyleTagTemplateClose = stringToPrecomputedChunk('</style>');
let currentlyRenderingBoundaryHasStylesToHoist = false;
let destinationHasCapacity = true;
function flushStyleTagsLateForBoundary(
this: Destination,
styleQueue: StyleQueue,
) {
const rules = styleQueue.rules;
const hrefs = styleQueue.hrefs;
if (__DEV__) {
if (rules.length > 0 && hrefs.length === 0) {
console.error(
'React expected to have at least one href for an a hoistable style but found none. This is a bug in React.',
);
}
}
let i = 0;
if (hrefs.length) {
writeChunk(
this,
((currentlyFlushingRenderState: any): RenderState).startInlineStyle,
);
writeChunk(this, lateStyleTagResourceOpen1);
writeChunk(this, styleQueue.precedence);
writeChunk(this, lateStyleTagResourceOpen2);
for (; i < hrefs.length - 1; i++) {
writeChunk(this, hrefs[i]);
writeChunk(this, spaceSeparator);
}
writeChunk(this, hrefs[i]);
writeChunk(this, lateStyleTagResourceOpen3);
for (i = 0; i < rules.length; i++) {
writeChunk(this, rules[i]);
}
destinationHasCapacity = writeChunkAndReturn(
this,
lateStyleTagTemplateClose,
);
currentlyRenderingBoundaryHasStylesToHoist = true;
rules.length = 0;
hrefs.length = 0;
}
}
function hasStylesToHoist(stylesheet: StylesheetResource): boolean {
if (stylesheet.state !== PREAMBLE) {
currentlyRenderingBoundaryHasStylesToHoist = true;
return true;
}
return false;
}
export function writeHoistablesForBoundary(
destination: Destination,
hoistableState: HoistableState,
renderState: RenderState,
): boolean {
currentlyRenderingBoundaryHasStylesToHoist = false;
destinationHasCapacity = true;
currentlyFlushingRenderState = renderState;
hoistableState.styles.forEach(flushStyleTagsLateForBoundary, destination);
currentlyFlushingRenderState = null;
hoistableState.stylesheets.forEach(hasStylesToHoist);
if (currentlyRenderingBoundaryHasStylesToHoist) {
renderState.stylesToHoist = true;
}
return destinationHasCapacity;
}
function flushResource(this: Destination, resource: Resource) {
for (let i = 0; i < resource.length; i++) {
writeChunk(this, resource[i]);
}
resource.length = 0;
}
const stylesheetFlushingQueue: Array<Chunk | PrecomputedChunk> = [];
function flushStyleInPreamble(
this: Destination,
stylesheet: StylesheetResource,
key: string,
map: Map<string, StylesheetResource>,
) {
pushLinkImpl(stylesheetFlushingQueue, stylesheet.props);
for (let i = 0; i < stylesheetFlushingQueue.length; i++) {
writeChunk(this, stylesheetFlushingQueue[i]);
}
stylesheetFlushingQueue.length = 0;
stylesheet.state = PREAMBLE;
}
const styleTagResourceOpen1 = stringToPrecomputedChunk(' data-precedence="');
const styleTagResourceOpen2 = stringToPrecomputedChunk('" data-href="');
const spaceSeparator = stringToPrecomputedChunk(' ');
const styleTagResourceOpen3 = stringToPrecomputedChunk('">');
const styleTagResourceClose = stringToPrecomputedChunk('</style>');
function flushStylesInPreamble(
this: Destination,
styleQueue: StyleQueue,
precedence: string,
) {
const hasStylesheets = styleQueue.sheets.size > 0;
styleQueue.sheets.forEach(flushStyleInPreamble, this);
styleQueue.sheets.clear();
const rules = styleQueue.rules;
const hrefs = styleQueue.hrefs;
if (!hasStylesheets || hrefs.length) {
writeChunk(
this,
((currentlyFlushingRenderState: any): RenderState).startInlineStyle,
);
writeChunk(this, styleTagResourceOpen1);
writeChunk(this, styleQueue.precedence);
let i = 0;
if (hrefs.length) {
writeChunk(this, styleTagResourceOpen2);
for (; i < hrefs.length - 1; i++) {
writeChunk(this, hrefs[i]);
writeChunk(this, spaceSeparator);
}
writeChunk(this, hrefs[i]);
}
writeChunk(this, styleTagResourceOpen3);
for (i = 0; i < rules.length; i++) {
writeChunk(this, rules[i]);
}
writeChunk(this, styleTagResourceClose);
rules.length = 0;
hrefs.length = 0;
}
}
function preloadLateStyle(this: Destination, stylesheet: StylesheetResource) {
if (stylesheet.state === PENDING) {
stylesheet.state = PRELOADED;
const preloadProps = preloadAsStylePropsFromProps(
stylesheet.props.href,
stylesheet.props,
);
pushLinkImpl(stylesheetFlushingQueue, preloadProps);
for (let i = 0; i < stylesheetFlushingQueue.length; i++) {
writeChunk(this, stylesheetFlushingQueue[i]);
}
stylesheetFlushingQueue.length = 0;
}
}
function preloadLateStyles(this: Destination, styleQueue: StyleQueue) {
styleQueue.sheets.forEach(preloadLateStyle, this);
styleQueue.sheets.clear();
}
const blockingRenderChunkStart = stringToPrecomputedChunk(
'<link rel="expect" href="#',
);
const blockingRenderChunkEnd = stringToPrecomputedChunk(
'" blocking="render"/>',
);
function writeBlockingRenderInstruction(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
): void {
if (enableFizzBlockingRender) {
const idPrefix = resumableState.idPrefix;
const shellId = '_' + idPrefix + 'R_';
writeChunk(destination, blockingRenderChunkStart);
writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
writeChunk(destination, blockingRenderChunkEnd);
}
}
const completedShellIdAttributeStart = stringToPrecomputedChunk(' id="');
function writeCompletedShellIdAttribute(
destination: Destination,
resumableState: ResumableState,
): void {
if ((resumableState.instructions & SentCompletedShellId) !== NothingSent) {
return;
}
resumableState.instructions |= SentCompletedShellId;
const idPrefix = resumableState.idPrefix;
const shellId = '_' + idPrefix + 'R_';
writeChunk(destination, completedShellIdAttributeStart);
writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
writeChunk(destination, attributeEnd);
}
function pushCompletedShellIdAttribute(
target: Array<Chunk | PrecomputedChunk>,
resumableState: ResumableState,
): void {
if ((resumableState.instructions & SentCompletedShellId) !== NothingSent) {
return;
}
resumableState.instructions |= SentCompletedShellId;
const idPrefix = resumableState.idPrefix;
const shellId = '_' + idPrefix + 'R_';
target.push(
completedShellIdAttributeStart,
stringToChunk(escapeTextForBrowser(shellId)),
attributeEnd,
);
}
export function writePreambleStart(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
skipBlockingShell: boolean,
): void {
if (enableFizzExternalRuntime && renderState.externalRuntimeScript) {
const {src, chunks} = renderState.externalRuntimeScript;
internalPreinitScript(resumableState, renderState, src, chunks);
}
const preamble = renderState.preamble;
const htmlChunks = preamble.htmlChunks;
const headChunks = preamble.headChunks;
let i = 0;
if (htmlChunks) {
for (i = 0; i < htmlChunks.length; i++) {
writeChunk(destination, htmlChunks[i]);
}
if (headChunks) {
for (i = 0; i < headChunks.length; i++) {
writeChunk(destination, headChunks[i]);
}
} else {
writeChunk(destination, startChunkForTag('head'));
writeChunk(destination, endOfStartTag);
}
} else if (headChunks) {
for (i = 0; i < headChunks.length; i++) {
writeChunk(destination, headChunks[i]);
}
}
const charsetChunks = renderState.charsetChunks;
for (i = 0; i < charsetChunks.length; i++) {
writeChunk(destination, charsetChunks[i]);
}
charsetChunks.length = 0;
renderState.preconnects.forEach(flushResource, destination);
renderState.preconnects.clear();
const viewportChunks = renderState.viewportChunks;
for (i = 0; i < viewportChunks.length; i++) {
writeChunk(destination, viewportChunks[i]);
}
viewportChunks.length = 0;
renderState.fontPreloads.forEach(flushResource, destination);
renderState.fontPreloads.clear();
renderState.highImagePreloads.forEach(flushResource, destination);
renderState.highImagePreloads.clear();
currentlyFlushingRenderState = renderState;
renderState.styles.forEach(flushStylesInPreamble, destination);
currentlyFlushingRenderState = null;
const importMapChunks = renderState.importMapChunks;
for (i = 0; i < importMapChunks.length; i++) {
writeChunk(destination, importMapChunks[i]);
}
importMapChunks.length = 0;
renderState.bootstrapScripts.forEach(flushResource, destination);
renderState.scripts.forEach(flushResource, destination);
renderState.scripts.clear();
renderState.bulkPreloads.forEach(flushResource, destination);
renderState.bulkPreloads.clear();
if ((htmlChunks || headChunks) && !skipBlockingShell) {
writeBlockingRenderInstruction(destination, resumableState, renderState);
} else {
resumableState.instructions |= SentCompletedShellId;
}
const hoistableChunks = renderState.hoistableChunks;
for (i = 0; i < hoistableChunks.length; i++) {
writeChunk(destination, hoistableChunks[i]);
}
hoistableChunks.length = 0;
}
export function writePreambleEnd(
destination: Destination,
renderState: RenderState,
): void {
const preamble = renderState.preamble;
const htmlChunks = preamble.htmlChunks;
const headChunks = preamble.headChunks;
if (htmlChunks || headChunks) {
writeChunk(destination, endChunkForTag('head'));
}
const bodyChunks = preamble.bodyChunks;
if (bodyChunks) {
for (let i = 0; i < bodyChunks.length; i++) {
writeChunk(destination, bodyChunks[i]);
}
}
}
export function writeHoistables(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
): void {
let i = 0;
const viewportChunks = renderState.viewportChunks;
for (i = 0; i < viewportChunks.length; i++) {
writeChunk(destination, viewportChunks[i]);
}
viewportChunks.length = 0;
renderState.preconnects.forEach(flushResource, destination);
renderState.preconnects.clear();
renderState.fontPreloads.forEach(flushResource, destination);
renderState.fontPreloads.clear();
renderState.highImagePreloads.forEach(flushResource, destination);
renderState.highImagePreloads.clear();
renderState.styles.forEach(preloadLateStyles, destination);
renderState.scripts.forEach(flushResource, destination);
renderState.scripts.clear();
renderState.bulkPreloads.forEach(flushResource, destination);
renderState.bulkPreloads.clear();
const hoistableChunks = renderState.hoistableChunks;
for (i = 0; i < hoistableChunks.length; i++) {
writeChunk(destination, hoistableChunks[i]);
}
hoistableChunks.length = 0;
}
export function writePostamble(
destination: Destination,
resumableState: ResumableState,
): void {
if (resumableState.hasBody) {
writeChunk(destination, endChunkForTag('body'));
}
if (resumableState.hasHtml) {
writeChunk(destination, endChunkForTag('html'));
}
}
const arrayFirstOpenBracket = stringToPrecomputedChunk('[');
const arraySubsequentOpenBracket = stringToPrecomputedChunk(',[');
const arrayInterstitial = stringToPrecomputedChunk(',');
const arrayCloseBracket = stringToPrecomputedChunk(']');
function writeStyleResourceDependenciesInJS(
destination: Destination,
hoistableState: HoistableState,
): void {
writeChunk(destination, arrayFirstOpenBracket);
let nextArrayOpenBrackChunk = arrayFirstOpenBracket;
hoistableState.stylesheets.forEach(resource => {
if (resource.state === PREAMBLE) {
} else if (resource.state === LATE) {
writeChunk(destination, nextArrayOpenBrackChunk);
writeStyleResourceDependencyHrefOnlyInJS(
destination,
resource.props.href,
);
writeChunk(destination, arrayCloseBracket);
nextArrayOpenBrackChunk = arraySubsequentOpenBracket;
} else {
writeChunk(destination, nextArrayOpenBrackChunk);
writeStyleResourceDependencyInJS(
destination,
resource.props.href,
resource.props['data-precedence'],
resource.props,
);
writeChunk(destination, arrayCloseBracket);
nextArrayOpenBrackChunk = arraySubsequentOpenBracket;
resource.state = LATE;
}
});
writeChunk(destination, arrayCloseBracket);
}
function writeStyleResourceDependencyHrefOnlyInJS(
destination: Destination,
href: string,
) {
if (__DEV__) {
checkAttributeStringCoercion(href, 'href');
}
const coercedHref = '' + (href: any);
writeChunk(
destination,
stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)),
);
}
function writeStyleResourceDependencyInJS(
destination: Destination,
href: mixed,
precedence: mixed,
props: Object,
) {
const coercedHref = sanitizeURL('' + (href: any));
writeChunk(
destination,
stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)),
);
if (__DEV__) {
checkAttributeStringCoercion(precedence, 'precedence');
}
const coercedPrecedence = '' + (precedence: any);
writeChunk(destination, arrayInterstitial);
writeChunk(
destination,
stringToChunk(escapeJSObjectForInstructionScripts(coercedPrecedence)),
);
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'href':
case 'rel':
case 'precedence':
case 'data-precedence': {
break;
}
case 'children':
case 'dangerouslySetInnerHTML':
throw new Error(
`${'link'} is a self-closing tag and must neither have \`children\` nor ` +
'use `dangerouslySetInnerHTML`.',
);
default:
writeStyleResourceAttributeInJS(destination, propKey, propValue);
break;
}
}
}
return null;
}
function writeStyleResourceAttributeInJS(
destination: Destination,
name: string,
value: string | boolean | number | Function | Object,
): void {
let attributeName = name.toLowerCase();
let attributeValue;
switch (typeof value) {
case 'function':
case 'symbol':
return;
}
switch (name) {
case 'innerHTML':
case 'dangerouslySetInnerHTML':
case 'suppressContentEditableWarning':
case 'suppressHydrationWarning':
case 'style':
case 'ref':
return;
case 'className': {
attributeName = 'class';
if (__DEV__) {
checkAttributeStringCoercion(value, attributeName);
}
attributeValue = '' + (value: any);
break;
}
case 'hidden': {
if (value === false) {
return;
}
attributeValue = '';
break;
}
case 'src':
case 'href': {
value = sanitizeURL(value);
if (__DEV__) {
checkAttributeStringCoercion(value, attributeName);
}
attributeValue = '' + (value: any);
break;
}
default: {
if (
name.length > 2 &&
(name[0] === 'o' || name[0] === 'O') &&
(name[1] === 'n' || name[1] === 'N')
) {
return;
}
if (!isAttributeNameSafe(name)) {
return;
}
if (__DEV__) {
checkAttributeStringCoercion(value, attributeName);
}
attributeValue = '' + (value: any);
}
}
writeChunk(destination, arrayInterstitial);
writeChunk(
destination,
stringToChunk(escapeJSObjectForInstructionScripts(attributeName)),
);
writeChunk(destination, arrayInterstitial);
writeChunk(
destination,
stringToChunk(escapeJSObjectForInstructionScripts(attributeValue)),
);
}
function writeStyleResourceDependenciesInAttr(
destination: Destination,
hoistableState: HoistableState,
): void {
writeChunk(destination, arrayFirstOpenBracket);
let nextArrayOpenBrackChunk = arrayFirstOpenBracket;
hoistableState.stylesheets.forEach(resource => {
if (resource.state === PREAMBLE) {
} else if (resource.state === LATE) {
writeChunk(destination, nextArrayOpenBrackChunk);
writeStyleResourceDependencyHrefOnlyInAttr(
destination,
resource.props.href,
);
writeChunk(destination, arrayCloseBracket);
nextArrayOpenBrackChunk = arraySubsequentOpenBracket;
} else {
writeChunk(destination, nextArrayOpenBrackChunk);
writeStyleResourceDependencyInAttr(
destination,
resource.props.href,
resource.props['data-precedence'],
resource.props,
);
writeChunk(destination, arrayCloseBracket);
nextArrayOpenBrackChunk = arraySubsequentOpenBracket;
resource.state = LATE;
}
});
writeChunk(destination, arrayCloseBracket);
}
function writeStyleResourceDependencyHrefOnlyInAttr(
destination: Destination,
href: string,
) {
if (__DEV__) {
checkAttributeStringCoercion(href, 'href');
}
const coercedHref = '' + (href: any);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(JSON.stringify(coercedHref))),
);
}
function writeStyleResourceDependencyInAttr(
destination: Destination,
href: mixed,
precedence: mixed,
props: Object,
) {
const coercedHref = sanitizeURL('' + (href: any));
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(JSON.stringify(coercedHref))),
);
if (__DEV__) {
checkAttributeStringCoercion(precedence, 'precedence');
}
const coercedPrecedence = '' + (precedence: any);
writeChunk(destination, arrayInterstitial);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(JSON.stringify(coercedPrecedence))),
);
for (const propKey in props) {
if (hasOwnProperty.call(props, propKey)) {
const propValue = props[propKey];
if (propValue == null) {
continue;
}
switch (propKey) {
case 'href':
case 'rel':
case 'precedence':
case 'data-precedence': {
break;
}
case 'children':
case 'dangerouslySetInnerHTML':
throw new Error(
`${'link'} is a self-closing tag and must neither have \`children\` nor ` +
'use `dangerouslySetInnerHTML`.',
);
default:
writeStyleResourceAttributeInAttr(destination, propKey, propValue);
break;
}
}
}
return null;
}
function writeStyleResourceAttributeInAttr(
destination: Destination,
name: string,
value: string | boolean | number | Function | Object,
): void {
let attributeName = name.toLowerCase();
let attributeValue;
switch (typeof value) {
case 'function':
case 'symbol':
return;
}
switch (name) {
case 'innerHTML':
case 'dangerouslySetInnerHTML':
case 'suppressContentEditableWarning':
case 'suppressHydrationWarning':
case 'style':
case 'ref':
return;
case 'className': {
attributeName = 'class';
if (__DEV__) {
checkAttributeStringCoercion(value, attributeName);
}
attributeValue = '' + (value: any);
break;
}
case 'hidden': {
if (value === false) {
return;
}
attributeValue = '';
break;
}
case 'src':
case 'href': {
value = sanitizeURL(value);
if (__DEV__) {
checkAttributeStringCoercion(value, attributeName);
}
attributeValue = '' + (value: any);
break;
}
default: {
if (
name.length > 2 &&
(name[0] === 'o' || name[0] === 'O') &&
(name[1] === 'n' || name[1] === 'N')
) {
return;
}
if (!isAttributeNameSafe(name)) {
return;
}
if (__DEV__) {
checkAttributeStringCoercion(value, attributeName);
}
attributeValue = '' + (value: any);
}
}
writeChunk(destination, arrayInterstitial);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(JSON.stringify(attributeName))),
);
writeChunk(destination, arrayInterstitial);
writeChunk(
destination,
stringToChunk(escapeTextForBrowser(JSON.stringify(attributeValue))),
);
}
type StylesheetState = 0 | 1 | 2 | 3;
const PENDING: StylesheetState = 0;
const PRELOADED: StylesheetState = 1;
const PREAMBLE: StylesheetState = 2;
const LATE: StylesheetState = 3;
type PreconnectProps = {
rel: 'preconnect' | 'dns-prefetch',
href: string,
[string]: mixed,
};
type PreloadAsProps = {
rel: 'preload',
as: string,
href: ?string,
[string]: ?string,
};
type PreloadModuleProps = {
rel: 'modulepreload',
href: ?string,
[string]: ?string,
};
type PreloadProps = PreloadAsProps | PreloadModuleProps;
type ScriptProps = {
async: true,
src: string,
crossOrigin?: ?CrossOriginEnum,
[string]: mixed,
};
type ModuleScriptProps = {
async: true,
src: string,
type: 'module',
crossOrigin?: ?CrossOriginEnum,
[string]: mixed,
};
export type Resource = Array<Chunk | PrecomputedChunk>;
type StylesheetProps = {
rel: 'stylesheet',
href: string,
'data-precedence': string,
crossOrigin?: ?CrossOriginEnum,
integrity?: ?string,
nonce?: ?string,
type?: ?string,
fetchPriority?: ?string,
referrerPolicy?: ?string,
media?: ?string,
[string]: mixed,
};
type StylesheetResource = {
props: StylesheetProps,
state: StylesheetState,
};
export type HoistableState = {
styles: Set<StyleQueue>,
stylesheets: Set<StylesheetResource>,
};
export type StyleQueue = {
precedence: Chunk | PrecomputedChunk,
rules: Array<Chunk | PrecomputedChunk>,
hrefs: Array<Chunk | PrecomputedChunk>,
sheets: Map<string, StylesheetResource>,
};
export function createHoistableState(): HoistableState {
return {
styles: new Set(),
stylesheets: new Set(),
};
}
function getResourceKey(href: string): string {
return href;
}
function getImageResourceKey(
href: string,
imageSrcSet?: ?string,
imageSizes?: ?string,
): string {
if (imageSrcSet) {
return imageSrcSet + '\n' + (imageSizes || '');
}
return href;
}
function prefetchDNS(href: string) {
const request = resolveRequest();
if (!request) {
previousDispatcher.D( href);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (typeof href === 'string' && href) {
const key = getResourceKey(href);
if (!resumableState.dnsResources.hasOwnProperty(key)) {
resumableState.dnsResources[key] = EXISTS;
const headers = renderState.headers;
let header;
if (
headers &&
headers.remainingCapacity > 0 &&
((header = getPrefetchDNSAsHeader(href)),
(headers.remainingCapacity -= header.length + 2) >= 0)
) {
renderState.resets.dns[key] = EXISTS;
if (headers.preconnects) {
headers.preconnects += ', ';
}
headers.preconnects += header;
} else {
const resource: Resource = [];
pushLinkImpl(resource, ({href, rel: 'dns-prefetch'}: PreconnectProps));
renderState.preconnects.add(resource);
}
}
flushResources(request);
}
}
function preconnect(href: string, crossOrigin: ?CrossOriginEnum) {
const request = resolveRequest();
if (!request) {
previousDispatcher.C( href, crossOrigin);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (typeof href === 'string' && href) {
const bucket =
crossOrigin === 'use-credentials'
? 'credentials'
: typeof crossOrigin === 'string'
? 'anonymous'
: 'default';
const key = getResourceKey(href);
if (!resumableState.connectResources[bucket].hasOwnProperty(key)) {
resumableState.connectResources[bucket][key] = EXISTS;
const headers = renderState.headers;
let header;
if (
headers &&
headers.remainingCapacity > 0 &&
((header = getPreconnectAsHeader(href, crossOrigin)),
(headers.remainingCapacity -= header.length + 2) >= 0)
) {
renderState.resets.connect[bucket][key] = EXISTS;
if (headers.preconnects) {
headers.preconnects += ', ';
}
headers.preconnects += header;
} else {
const resource: Resource = [];
pushLinkImpl(
resource,
({rel: 'preconnect', href, crossOrigin}: PreconnectProps),
);
renderState.preconnects.add(resource);
}
}
flushResources(request);
}
}
function preload(href: string, as: string, options?: ?PreloadImplOptions) {
const request = resolveRequest();
if (!request) {
previousDispatcher.L( href, as, options);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (as && href) {
switch (as) {
case 'image': {
let imageSrcSet, imageSizes, fetchPriority;
if (options) {
imageSrcSet = options.imageSrcSet;
imageSizes = options.imageSizes;
fetchPriority = options.fetchPriority;
}
const key = getImageResourceKey(href, imageSrcSet, imageSizes);
if (resumableState.imageResources.hasOwnProperty(key)) {
return;
}
resumableState.imageResources[key] = PRELOAD_NO_CREDS;
const headers = renderState.headers;
let header: string;
if (
headers &&
headers.remainingCapacity > 0 &&
typeof imageSrcSet !== 'string' &&
fetchPriority === 'high' &&
((header = getPreloadAsHeader(href, as, options)),
(headers.remainingCapacity -= header.length + 2) >= 0)
) {
renderState.resets.image[key] = PRELOAD_NO_CREDS;
if (headers.highImagePreloads) {
headers.highImagePreloads += ', ';
}
headers.highImagePreloads += header;
} else {
const resource = ([]: Resource);
pushLinkImpl(
resource,
Object.assign(
({
rel: 'preload',
href: imageSrcSet ? undefined : href,
as,
}: PreloadAsProps),
options,
),
);
if (fetchPriority === 'high') {
renderState.highImagePreloads.add(resource);
} else {
renderState.bulkPreloads.add(resource);
renderState.preloads.images.set(key, resource);
}
}
break;
}
case 'style': {
const key = getResourceKey(href);
if (resumableState.styleResources.hasOwnProperty(key)) {
return;
}
const resource = ([]: Resource);
pushLinkImpl(
resource,
Object.assign(({rel: 'preload', href, as}: PreloadAsProps), options),
);
resumableState.styleResources[key] =
options &&
(typeof options.crossOrigin === 'string' ||
typeof options.integrity === 'string')
? [options.crossOrigin, options.integrity]
: PRELOAD_NO_CREDS;
renderState.preloads.stylesheets.set(key, resource);
renderState.bulkPreloads.add(resource);
break;
}
case 'script': {
const key = getResourceKey(href);
if (resumableState.scriptResources.hasOwnProperty(key)) {
return;
}
const resource = ([]: Resource);
renderState.preloads.scripts.set(key, resource);
renderState.bulkPreloads.add(resource);
pushLinkImpl(
resource,
Object.assign(({rel: 'preload', href, as}: PreloadAsProps), options),
);
resumableState.scriptResources[key] =
options &&
(typeof options.crossOrigin === 'string' ||
typeof options.integrity === 'string')
? [options.crossOrigin, options.integrity]
: PRELOAD_NO_CREDS;
break;
}
default: {
const key = getResourceKey(href);
const hasAsType = resumableState.unknownResources.hasOwnProperty(as);
let resources;
if (hasAsType) {
resources = resumableState.unknownResources[as];
if (resources.hasOwnProperty(key)) {
return;
}
} else {
resources = ({}: ResumableState['unknownResources']['asType']);
resumableState.unknownResources[as] = resources;
}
resources[key] = PRELOAD_NO_CREDS;
const headers = renderState.headers;
let header;
if (
headers &&
headers.remainingCapacity > 0 &&
as === 'font' &&
((header = getPreloadAsHeader(href, as, options)),
(headers.remainingCapacity -= header.length + 2) >= 0)
) {
renderState.resets.font[key] = PRELOAD_NO_CREDS;
if (headers.fontPreloads) {
headers.fontPreloads += ', ';
}
headers.fontPreloads += header;
} else {
const resource = ([]: Resource);
const props = Object.assign(
({
rel: 'preload',
href,
as,
}: PreloadAsProps),
options,
);
pushLinkImpl(resource, props);
switch (as) {
case 'font':
renderState.fontPreloads.add(resource);
break;
default:
renderState.bulkPreloads.add(resource);
}
}
}
}
flushResources(request);
}
}
function preloadModule(
href: string,
options?: ?PreloadModuleImplOptions,
): void {
const request = resolveRequest();
if (!request) {
previousDispatcher.m( href, options);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (href) {
const key = getResourceKey(href);
const as =
options && typeof options.as === 'string' ? options.as : 'script';
let resource;
switch (as) {
case 'script': {
if (resumableState.moduleScriptResources.hasOwnProperty(key)) {
return;
}
resource = ([]: Resource);
resumableState.moduleScriptResources[key] =
options &&
(typeof options.crossOrigin === 'string' ||
typeof options.integrity === 'string')
? [options.crossOrigin, options.integrity]
: PRELOAD_NO_CREDS;
renderState.preloads.moduleScripts.set(key, resource);
break;
}
default: {
const hasAsType =
resumableState.moduleUnknownResources.hasOwnProperty(as);
let resources;
if (hasAsType) {
resources = resumableState.unknownResources[as];
if (resources.hasOwnProperty(key)) {
return;
}
} else {
resources = ({}: ResumableState['moduleUnknownResources']['asType']);
resumableState.moduleUnknownResources[as] = resources;
}
resource = ([]: Resource);
resources[key] = PRELOAD_NO_CREDS;
}
}
pushLinkImpl(
resource,
Object.assign(
({
rel: 'modulepreload',
href,
}: PreloadModuleProps),
options,
),
);
renderState.bulkPreloads.add(resource);
flushResources(request);
}
}
function preinitStyle(
href: string,
precedence: ?string,
options?: ?PreinitStyleOptions,
): void {
const request = resolveRequest();
if (!request) {
previousDispatcher.S( href, precedence, options);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (href) {
precedence = precedence || 'default';
const key = getResourceKey(href);
let styleQueue = renderState.styles.get(precedence);
const hasKey = resumableState.styleResources.hasOwnProperty(key);
const resourceState = hasKey
? resumableState.styleResources[key]
: undefined;
if (resourceState !== EXISTS) {
resumableState.styleResources[key] = EXISTS;
if (!styleQueue) {
styleQueue = {
precedence: stringToChunk(escapeTextForBrowser(precedence)),
rules: ([]: Array<Chunk | PrecomputedChunk>),
hrefs: ([]: Array<Chunk | PrecomputedChunk>),
sheets: (new Map(): Map<string, StylesheetResource>),
};
renderState.styles.set(precedence, styleQueue);
}
const resource = {
state: PENDING,
props: Object.assign(
({
rel: 'stylesheet',
href,
'data-precedence': precedence,
}: StylesheetProps),
options,
),
};
if (resourceState) {
const preloadState: Preloaded | PreloadedWithCredentials =
resourceState;
if (preloadState.length === 2) {
adoptPreloadCredentials(resource.props, preloadState);
}
const preloadResource = renderState.preloads.stylesheets.get(key);
if (preloadResource && preloadResource.length > 0) {
preloadResource.length = 0;
} else {
resource.state = PRELOADED;
}
} else {
}
styleQueue.sheets.set(key, resource);
flushResources(request);
}
}
}
function preinitScript(src: string, options?: ?PreinitScriptOptions): void {
const request = resolveRequest();
if (!request) {
previousDispatcher.X( src, options);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (src) {
const key = getResourceKey(src);
const hasKey = resumableState.scriptResources.hasOwnProperty(key);
const resourceState = hasKey
? resumableState.scriptResources[key]
: undefined;
if (resourceState !== EXISTS) {
resumableState.scriptResources[key] = EXISTS;
const props: ScriptProps = Object.assign(
({
src,
async: true,
}: ScriptProps),
options,
);
if (resourceState) {
const preloadState: Preloaded | PreloadedWithCredentials =
resourceState;
if (preloadState.length === 2) {
adoptPreloadCredentials(props, preloadState);
}
const preloadResource = renderState.preloads.scripts.get(key);
if (preloadResource) {
preloadResource.length = 0;
}
}
const resource: Resource = [];
renderState.scripts.add(resource);
pushScriptImpl(resource, props);
flushResources(request);
}
return;
}
}
function preinitModuleScript(
src: string,
options?: ?PreinitModuleScriptOptions,
): void {
const request = resolveRequest();
if (!request) {
previousDispatcher.M( src, options);
return;
}
const resumableState = getResumableState(request);
const renderState = getRenderState(request);
if (src) {
const key = getResourceKey(src);
const hasKey = resumableState.moduleScriptResources.hasOwnProperty(key);
const resourceState = hasKey
? resumableState.moduleScriptResources[key]
: undefined;
if (resourceState !== EXISTS) {
resumableState.moduleScriptResources[key] = EXISTS;
const props = Object.assign(
({
src,
type: 'module',
async: true,
}: ModuleScriptProps),
options,
);
if (resourceState) {
const preloadState: Preloaded | PreloadedWithCredentials =
resourceState;
if (preloadState.length === 2) {
adoptPreloadCredentials(props, preloadState);
}
const preloadResource = renderState.preloads.moduleScripts.get(key);
if (preloadResource) {
preloadResource.length = 0;
}
}
const resource: Resource = [];
renderState.scripts.add(resource);
pushScriptImpl(resource, props);
flushResources(request);
}
return;
}
}
function preloadBootstrapScriptOrModule(
resumableState: ResumableState,
renderState: RenderState,
href: string,
props: PreloadProps,
): void {
const key = getResourceKey(href);
if (__DEV__) {
if (
resumableState.scriptResources.hasOwnProperty(key) ||
resumableState.moduleScriptResources.hasOwnProperty(key)
) {
console.error(
'Internal React Error: React expected bootstrap script or module with src "%s" to not have been preloaded already. please file an issue',
href,
);
}
}
resumableState.scriptResources[key] = EXISTS;
resumableState.moduleScriptResources[key] = EXISTS;
const resource: Resource = [];
pushLinkImpl(resource, props);
renderState.bootstrapScripts.add(resource);
}
function internalPreinitScript(
resumableState: ResumableState,
renderState: RenderState,
src: string,
chunks: Array<Chunk | PrecomputedChunk>,
): void {
const key = getResourceKey(src);
if (!resumableState.scriptResources.hasOwnProperty(key)) {
const resource: Resource = chunks;
resumableState.scriptResources[key] = EXISTS;
renderState.scripts.add(resource);
}
return;
}
function preloadAsStylePropsFromProps(href: string, props: any): PreloadProps {
return {
rel: 'preload',
as: 'style',
href: href,
crossOrigin: props.crossOrigin,
fetchPriority: props.fetchPriority,
integrity: props.integrity,
media: props.media,
hrefLang: props.hrefLang,
referrerPolicy: props.referrerPolicy,
};
}
function stylesheetPropsFromRawProps(rawProps: any): StylesheetProps {
return {
...rawProps,
'data-precedence': rawProps.precedence,
precedence: null,
};
}
function adoptPreloadCredentials(
target: StylesheetProps | ScriptProps | ModuleScriptProps,
preloadState: PreloadedWithCredentials,
): void {
if (target.crossOrigin == null) target.crossOrigin = preloadState[0];
if (target.integrity == null) target.integrity = preloadState[1];
}
function getPrefetchDNSAsHeader(href: string): string {
const escapedHref = escapeHrefForLinkHeaderURLContext(href);
return `<${escapedHref}>; rel=dns-prefetch`;
}
function getPreconnectAsHeader(
href: string,
crossOrigin?: ?CrossOriginEnum,
): string {
const escapedHref = escapeHrefForLinkHeaderURLContext(href);
let value = `<${escapedHref}>; rel=preconnect`;
if (typeof crossOrigin === 'string') {
const escapedCrossOrigin = escapeStringForLinkHeaderQuotedParamValueContext(
crossOrigin,
'crossOrigin',
);
value += `; crossorigin="${escapedCrossOrigin}"`;
}
return value;
}
function getPreloadAsHeader(
href: string,
as: string,
params: ?PreloadImplOptions,
): string {
const escapedHref = escapeHrefForLinkHeaderURLContext(href);
const escapedAs = escapeStringForLinkHeaderQuotedParamValueContext(as, 'as');
let value = `<${escapedHref}>; rel=preload; as="${escapedAs}"`;
for (const paramName in params) {
if (hasOwnProperty.call(params, paramName)) {
const paramValue = params[paramName];
if (typeof paramValue === 'string') {
value += `; ${paramName.toLowerCase()}="${escapeStringForLinkHeaderQuotedParamValueContext(
paramValue,
paramName,
)}"`;
}
}
}
return value;
}
function getStylesheetPreloadAsHeader(stylesheet: StylesheetResource): string {
const props = stylesheet.props;
const preloadOptions: PreloadImplOptions = {
crossOrigin: props.crossOrigin,
integrity: props.integrity,
nonce: props.nonce,
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy,
media: props.media,
};
return getPreloadAsHeader(props.href, 'style', preloadOptions);
}
const regexForHrefInLinkHeaderURLContext = /[<>\r\n]/g;
function escapeHrefForLinkHeaderURLContext(hrefInput: string): string {
if (__DEV__) {
checkAttributeStringCoercion(hrefInput, 'href');
}
const coercedHref = '' + hrefInput;
return coercedHref.replace(
regexForHrefInLinkHeaderURLContext,
escapeHrefForLinkHeaderURLContextReplacer,
);
}
function escapeHrefForLinkHeaderURLContextReplacer(match: string): string {
switch (match) {
case '<':
return '%3C';
case '>':
return '%3E';
case '\n':
return '%0A';
case '\r':
return '%0D';
default: {
throw new Error(
'escapeLinkHrefForHeaderContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React',
);
}
}
}
const regexForLinkHeaderQuotedParamValueContext = /["';,\r\n]/g;
function escapeStringForLinkHeaderQuotedParamValueContext(
value: string,
name: string,
): string {
if (__DEV__) {
checkOptionStringCoercion(value, name);
}
const coerced = '' + value;
return coerced.replace(
regexForLinkHeaderQuotedParamValueContext,
escapeStringForLinkHeaderQuotedParamValueContextReplacer,
);
}
function escapeStringForLinkHeaderQuotedParamValueContextReplacer(
match: string,
): string {
switch (match) {
case '"':
return '%22';
case "'":
return '%27';
case ';':
return '%3B';
case ',':
return '%2C';
case '\n':
return '%0A';
case '\r':
return '%0D';
default: {
throw new Error(
'escapeStringForLinkHeaderQuotedParamValueContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React',
);
}
}
}
function hoistStyleQueueDependency(
this: HoistableState,
styleQueue: StyleQueue,
) {
this.styles.add(styleQueue);
}
function hoistStylesheetDependency(
this: HoistableState,
stylesheet: StylesheetResource,
) {
this.stylesheets.add(stylesheet);
}
export function hoistHoistables(
parentState: HoistableState,
childState: HoistableState,
): void {
childState.styles.forEach(hoistStyleQueueDependency, parentState);
childState.stylesheets.forEach(hoistStylesheetDependency, parentState);
}
export function emitEarlyPreloads(
renderState: RenderState,
resumableState: ResumableState,
shellComplete: boolean,
): void {
const onHeaders = renderState.onHeaders;
if (onHeaders) {
const headers = renderState.headers;
if (headers) {
renderState.headers = null;
let linkHeader = headers.preconnects;
if (headers.fontPreloads) {
if (linkHeader) {
linkHeader += ', ';
}
linkHeader += headers.fontPreloads;
}
if (headers.highImagePreloads) {
if (linkHeader) {
linkHeader += ', ';
}
linkHeader += headers.highImagePreloads;
}
if (!shellComplete) {
const queueIter = renderState.styles.values();
outer: for (
let queueStep = queueIter.next();
headers.remainingCapacity > 0 && !queueStep.done;
queueStep = queueIter.next()
) {
const sheets = queueStep.value.sheets;
const sheetIter = sheets.values();
for (
let sheetStep = sheetIter.next();
headers.remainingCapacity > 0 && !sheetStep.done;
sheetStep = sheetIter.next()
) {
const sheet = sheetStep.value;
const props = sheet.props;
const key = getResourceKey(props.href);
const header = getStylesheetPreloadAsHeader(sheet);
if ((headers.remainingCapacity -= header.length + 2) >= 0) {
renderState.resets.style[key] = PRELOAD_NO_CREDS;
if (linkHeader) {
linkHeader += ', ';
}
linkHeader += header;
renderState.resets.style[key] =
typeof props.crossOrigin === 'string' ||
typeof props.integrity === 'string'
? [props.crossOrigin, props.integrity]
: PRELOAD_NO_CREDS;
} else {
break outer;
}
}
}
}
if (linkHeader) {
onHeaders({
Link: linkHeader,
});
} else {
onHeaders({});
}
return;
}
}
}
export type TransitionStatus = FormStatus;
export const NotPendingTransition: TransitionStatus = NotPending;