/** * @license React * react-dom-server.bun.production.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { Children, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED$1, } from 'react'; import {__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from 'react-dom'; // TODO: this is special because it gets imported during build. // // TODO: 18.0.0 has not been released to NPM; // It exists as a placeholder so that DevTools can support work tag changes between releases. // When we next publish a release, update the matching TODO in backend/renderer.js // TODO: This module is used both by the release scripts and to expose a version // at runtime. We should instead inject the version number as part of the build // process, and use the ReactVersions.js module as the single source of truth. var ReactVersion = '18.2.0'; function scheduleWork(callback) { callback(); } function beginWriting(destination) {} function writeChunk(destination, chunk) { if (chunk.length === 0) { return; } destination.write(chunk); } function writeChunkAndReturn(destination, chunk) { return !!destination.write(chunk); } function completeWriting(destination) {} function close(destination) { destination.end(); } function stringToChunk(content) { return content; } function stringToPrecomputedChunk(content) { return content; } function closeWithError(destination, error) { // $FlowFixMe[method-unbinding] if (typeof destination.error === 'function') { // $FlowFixMe: This is an Error object or the destination accepts other types. destination.error(error); } else { // Earlier implementations doesn't support this method. In that environment you're // supposed to throw from a promise returned but we don't return a promise in our // approach. We could fork this implementation but this is environment is an edge // case to begin with. It's even less common to run this in an older environment. // Even then, this is not where errors are supposed to happen and they get reported // to a global callback in addition to this anyway. So it's fine just to close this. destination.close(); } } // ----------------------------------------------------------------------------- const enableFloat = true; // When a node is unmounted, recurse into the Fiber subtree and clean out // $FlowFixMe[method-unbinding] const hasOwnProperty = Object.prototype.hasOwnProperty; // A reserved attribute. // It is handled by React separately and shouldn't be written to the DOM. const RESERVED = 0; // A simple string attribute. // Attributes that aren't in the filter are presumed to have this type. const STRING = 1; // A string attribute that accepts booleans in React. In HTML, these are called // "enumerated" attributes with "true" and "false" as possible values. // When true, it should be set to a "true" string. // When false, it should be set to a "false" string. const BOOLEANISH_STRING = 2; // A real boolean attribute. // When true, it should be present (set either to an empty string or its name). // When false, it should be omitted. const BOOLEAN = 3; // An attribute that can be used as a flag as well as with a value. // When true, it should be present (set either to an empty string or its name). // When false, it should be omitted. // For any other value, should be present with that value. const OVERLOADED_BOOLEAN = 4; // An attribute that must be numeric or parse as a numeric. // When falsy, it should be removed. const NUMERIC = 5; // An attribute that must be positive numeric or parse as a positive numeric. // When falsy, it should be removed. const POSITIVE_NUMERIC = 6; /* eslint-disable max-len */ const ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; /* eslint-enable max-len */ const ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040'; const VALID_ATTRIBUTE_NAME_REGEX = new RegExp( '^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$' ); const illegalAttributeNameCache = {}; const validatedAttributeNameCache = {}; function isAttributeNameSafe(attributeName) { if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) { return true; } if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) { return false; } if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { validatedAttributeNameCache[attributeName] = true; return true; } illegalAttributeNameCache[attributeName] = true; return false; } function getPropertyInfo(name) { return properties.hasOwnProperty(name) ? properties[name] : null; } function PropertyInfoRecord( name, type, mustUseProperty, attributeName, attributeNamespace, sanitizeURL, removeEmptyString ) { this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN; this.attributeName = attributeName; this.attributeNamespace = attributeNamespace; this.mustUseProperty = mustUseProperty; this.propertyName = name; this.type = type; this.sanitizeURL = sanitizeURL; this.removeEmptyString = removeEmptyString; } // When adding attributes to this list, be sure to also add them to // the `possibleStandardNames` module to ensure casing and incorrect // name warnings. const properties = {}; // These props are reserved by React. They shouldn't be written to the DOM. const reservedProps = [ 'children', 'dangerouslySetInnerHTML', // TODO: This prevents the assignment of defaultValue to regular // elements (not just inputs). Now that ReactDOMInput assigns to the // defaultValue property -- do we need this? 'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style', ]; { reservedProps.push('innerText', 'textContent'); } reservedProps.forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, RESERVED, false, // mustUseProperty name, // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // A few React string attributes have a different name. // This is a mapping from React prop names to the attribute names. [ ['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv'], ].forEach((_ref) => { let name = _ref[0], attributeName = _ref[1]; // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, STRING, false, // mustUseProperty attributeName, // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are "enumerated" HTML attributes that accept "true" and "false". // In React, we let users pass `true` and `false` even though technically // these aren't boolean attributes (they are coerced to strings). ['contentEditable', 'draggable', 'spellCheck', 'value'].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, BOOLEANISH_STRING, false, // mustUseProperty name.toLowerCase(), // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are "enumerated" SVG attributes that accept "true" and "false". // In React, we let users pass `true` and `false` even though technically // these aren't boolean attributes (they are coerced to strings). // Since these are SVG attributes, their attribute names are case-sensitive. [ 'autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha', ].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, BOOLEANISH_STRING, false, // mustUseProperty name, // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are HTML boolean attributes. [ 'allowFullScreen', 'async', // Note: there is a special case that prevents it from being written to the DOM // on the client side because the browsers are inconsistent. Instead we call focus(). 'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'disablePictureInPicture', 'disableRemotePlayback', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless', // Microdata 'itemScope', ].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, BOOLEAN, false, // mustUseProperty name.toLowerCase(), // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are the few React props that we set as DOM properties // rather than attributes. These are all booleans. [ 'checked', // Note: `option.selected` is not updated if `select.multiple` is // disabled with `removeAttribute`. We have special logic for handling this. 'multiple', 'muted', 'selected', // NOTE: if you add a camelCased prop to this list, // you'll need to set attributeName to name.toLowerCase() // instead in the assignment below. ].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, BOOLEAN, true, // mustUseProperty name, // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are HTML attributes that are "overloaded booleans": they behave like // booleans, but can also accept a string value. [ 'capture', 'download', // NOTE: if you add a camelCased prop to this list, // you'll need to set attributeName to name.toLowerCase() // instead in the assignment below. ].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, OVERLOADED_BOOLEAN, false, // mustUseProperty name, // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are HTML attributes that must be positive numbers. [ 'cols', 'rows', 'size', 'span', // NOTE: if you add a camelCased prop to this list, // you'll need to set attributeName to name.toLowerCase() // instead in the assignment below. ].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, POSITIVE_NUMERIC, false, // mustUseProperty name, // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These are HTML attributes that must be numbers. ['rowSpan', 'start'].forEach((name) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, NUMERIC, false, // mustUseProperty name.toLowerCase(), // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); const CAMELIZE = /[\-\:]([a-z])/g; const capitalize = (token) => token[1].toUpperCase(); // This is a list of all SVG attributes that need special casing, namespacing, // or boolean value assignment. Regular attributes that just accept strings // and have the same names are omitted, just like in the HTML attribute filter. // Some of these attributes can be hard to find. This list was created by // scraping the MDN documentation. [ 'accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height', // NOTE: if you add a camelCased prop to this list, // you'll need to set attributeName to name.toLowerCase() // instead in the assignment below. ].forEach((attributeName) => { const name = attributeName.replace(CAMELIZE, capitalize); // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, STRING, false, // mustUseProperty attributeName, null, // attributeNamespace false, // sanitizeURL false ); }); // String SVG attributes with the xlink namespace. [ 'xlink:actuate', 'xlink:arcrole', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', // NOTE: if you add a camelCased prop to this list, // you'll need to set attributeName to name.toLowerCase() // instead in the assignment below. ].forEach((attributeName) => { const name = attributeName.replace(CAMELIZE, capitalize); // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, STRING, false, // mustUseProperty attributeName, 'http://www.w3.org/1999/xlink', false, // sanitizeURL false ); }); // String SVG attributes with the xml namespace. [ 'xml:base', 'xml:lang', 'xml:space', // NOTE: if you add a camelCased prop to this list, // you'll need to set attributeName to name.toLowerCase() // instead in the assignment below. ].forEach((attributeName) => { const name = attributeName.replace(CAMELIZE, capitalize); // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[name] = new PropertyInfoRecord( name, STRING, false, // mustUseProperty attributeName, 'http://www.w3.org/XML/1998/namespace', false, // sanitizeURL false ); }); // These attribute exists both in HTML and SVG. // The attribute name is case-sensitive in SVG so we can't just use // the React name like we do for attributes that exist only in HTML. ['tabIndex', 'crossOrigin'].forEach((attributeName) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[attributeName] = new PropertyInfoRecord( attributeName, STRING, false, // mustUseProperty attributeName.toLowerCase(), // attributeName null, // attributeNamespace false, // sanitizeURL false ); }); // These attributes accept URLs. These must not allow javascript: URLS. // These will also need to accept Trusted Types object in the future. const xlinkHref = 'xlinkHref'; // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[xlinkHref] = new PropertyInfoRecord( 'xlinkHref', STRING, false, // mustUseProperty 'xlink:href', 'http://www.w3.org/1999/xlink', true, // sanitizeURL false ); ['src', 'href', 'action', 'formAction'].forEach((attributeName) => { // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions properties[attributeName] = new PropertyInfoRecord( attributeName, STRING, false, // mustUseProperty attributeName.toLowerCase(), // attributeName null, // attributeNamespace true, // sanitizeURL true ); }); /** * CSS properties which accept numbers but are not in units of "px". */ const isUnitlessNumber = { animationIterationCount: true, aspectRatio: true, borderImageOutset: true, borderImageSlice: true, borderImageWidth: true, boxFlex: true, boxFlexGroup: true, boxOrdinalGroup: true, columnCount: true, columns: true, flex: true, flexGrow: true, flexPositive: true, flexShrink: true, flexNegative: true, flexOrder: true, gridArea: true, gridRow: true, gridRowEnd: true, gridRowSpan: true, gridRowStart: true, gridColumn: true, gridColumnEnd: true, gridColumnSpan: true, gridColumnStart: true, fontWeight: true, lineClamp: true, lineHeight: true, opacity: true, order: true, orphans: true, tabSize: true, widows: true, zIndex: true, zoom: true, // SVG-related properties fillOpacity: true, floodOpacity: true, stopOpacity: true, strokeDasharray: true, strokeDashoffset: true, strokeMiterlimit: true, strokeOpacity: true, strokeWidth: true, }; /** * @param {string} prefix vendor-specific prefix, eg: Webkit * @param {string} key style name, eg: transitionDuration * @return {string} style name prefixed with `prefix`, properly camelCased, eg: * WebkitTransitionDuration */ function prefixKey(prefix, key) { return prefix + key.charAt(0).toUpperCase() + key.substring(1); } /** * Support style names that may come passed in prefixed by adding permutations * of vendor prefixes. */ const prefixes = ['Webkit', 'ms', 'Moz', 'O']; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an // infinite loop, because it iterates over the newly added props too. Object.keys(isUnitlessNumber).forEach(function (prop) { prefixes.forEach(function (prefix) { isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; }); }); // code copied and modified from escape-html const matchHtmlRegExp = /["'&<>]/; /** * Escapes special characters and HTML entities in a given html string. * * @param {string} string HTML string to escape for later insertion * @return {string} * @public */ function escapeHtml(string) { const str = '' + string; const match = matchHtmlRegExp.exec(str); if (!match) { return str; } let escape; let html = ''; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: // " escape = '"'; break; case 38: // & escape = '&'; break; case 39: // ' escape = '''; // modified from escape-html; used to be ''' break; case 60: // < escape = '<'; break; case 62: // > escape = '>'; break; default: continue; } if (lastIndex !== index) { html += str.substring(lastIndex, index); } lastIndex = index + 1; html += escape; } return lastIndex !== index ? html + str.substring(lastIndex, index) : html; } // end code copied and modified from escape-html /** * Escapes text to prevent scripting attacks. * * @param {*} text Text value to escape. * @return {string} An escaped string. */ function escapeTextForBrowser(text) { if (typeof text === 'boolean' || typeof text === 'number') { // this shortcircuit helps perf for types that we know will never have // special characters, especially given that this function is used often // for numeric dom ids. return '' + text; } return escapeHtml(text); } const uppercasePattern = /([A-Z])/g; const msPattern = /^ms-/; /** * Hyphenates a camelcased CSS property name, for example: * * > hyphenateStyleName('backgroundColor') * < "background-color" * > hyphenateStyleName('MozTransition') * < "-moz-transition" * > hyphenateStyleName('msTransition') * < "-ms-transition" * * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix * is converted to `-ms-`. */ function hyphenateStyleName(name) { return name .replace(uppercasePattern, '-$1') .toLowerCase() .replace(msPattern, '-ms-'); } const isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare function isArray(a) { return isArrayImpl(a); } const assign = Object.assign; // @TODO add bootstrap script to implicit preloads function createResources() { return { // persistent preloadsMap: new Map(), stylesMap: new Map(), scriptsMap: new Map(), headsMap: new Map(), // cleared on flush charset: null, bases: new Set(), preconnects: new Set(), fontPreloads: new Set(), // usedImagePreloads: new Set(), precedences: new Map(), usedStylePreloads: new Set(), scripts: new Set(), usedScriptPreloads: new Set(), explicitStylePreloads: new Set(), // explicitImagePreloads: new Set(), explicitScriptPreloads: new Set(), headResources: new Set(), // cache for tracking structured meta tags structuredMetaKeys: new Map(), // like a module global for currently rendering boundary boundaryResources: null, }; } function createBoundaryResources() { return new Set(); } let currentResources = null; const currentResourcesStack = []; function prepareToRenderResources(resources) { currentResourcesStack.push(currentResources); currentResources = resources; } function finishRenderingResources() { currentResources = currentResourcesStack.pop(); } function setCurrentlyRenderingBoundaryResourcesTarget( resources, boundaryResources ) { resources.boundaryResources = boundaryResources; } const ReactDOMServerDispatcher = { preload, preinit, }; function preload(href, options) { if (!currentResources) { // While we expect that preload calls are primarily going to be observed // during render because effects and events don't run on the server it is // still possible that these get called in module scope. This is valid on // the client since there is still a document to interact with but on the // server we need a request to associate the call to. Because of this we // simply return and do not warn. return; } const resources = currentResources; if ( typeof href === 'string' && href && typeof options === 'object' && options !== null ) { const as = options.as; let resource = resources.preloadsMap.get(href); if (resource); else { resource = createPreloadResource( resources, href, as, preloadPropsFromPreloadOptions(href, as, options) ); } switch (as) { case 'font': { resources.fontPreloads.add(resource); break; } case 'style': { resources.explicitStylePreloads.add(resource); break; } case 'script': { resources.explicitScriptPreloads.add(resource); break; } } } } function preinit(href, options) { if (!currentResources) { // While we expect that preinit calls are primarily going to be observed // during render because effects and events don't run on the server it is // still possible that these get called in module scope. This is valid on // the client since there is still a document to interact with but on the // server we need a request to associate the call to. Because of this we // simply return and do not warn. return; } const resources = currentResources; if ( typeof href === 'string' && href && typeof options === 'object' && options !== null ) { const as = options.as; switch (as) { case 'style': { let resource = resources.stylesMap.get(href); if (resource); else { const precedence = options.precedence || 'default'; const resourceProps = stylePropsFromPreinitOptions( href, precedence, options ); resource = createStyleResource( resources, href, precedence, resourceProps ); } resource.set.add(resource); resources.explicitStylePreloads.add(resource.hint); return; } case 'script': { const src = href; let resource = resources.scriptsMap.get(src); if (resource); else { const scriptProps = scriptPropsFromPreinitOptions(src, options); resource = createScriptResource(resources, src, scriptProps); resources.scripts.add(resource); } return; } } } } function preloadPropsFromPreloadOptions(href, as, options) { return { href, rel: 'preload', as, crossOrigin: as === 'font' ? '' : options.crossOrigin, integrity: options.integrity, }; } function preloadPropsFromRawProps(href, as, rawProps) { const props = assign({}, rawProps); props.href = href; props.rel = 'preload'; props.as = as; if (as === 'font') { // Font preloads always need CORS anonymous mode so we set it here // regardless of the props provided. This should warn elsewhere in // dev props.crossOrigin = ''; } return props; } function preloadAsStylePropsFromProps(href, props) { return { rel: 'preload', as: 'style', href: href, crossOrigin: props.crossOrigin, integrity: props.integrity, media: props.media, hrefLang: props.hrefLang, referrerPolicy: props.referrerPolicy, }; } function preloadAsScriptPropsFromProps(href, props) { return { rel: 'preload', as: 'script', href, crossOrigin: props.crossOrigin, integrity: props.integrity, referrerPolicy: props.referrerPolicy, }; } function createPreloadResource(resources, href, as, props) { const preloadsMap = resources.preloadsMap; const resource = { type: 'preload', as, href, flushed: false, props, }; preloadsMap.set(href, resource); return resource; } function stylePropsFromRawProps(href, precedence, rawProps) { const props = assign({}, rawProps); props.href = href; props.rel = 'stylesheet'; props['data-precedence'] = precedence; delete props.precedence; return props; } function stylePropsFromPreinitOptions(href, precedence, options) { return { rel: 'stylesheet', href, 'data-precedence': precedence, crossOrigin: options.crossOrigin, }; } function createStyleResource(resources, href, precedence, props) { const stylesMap = resources.stylesMap, preloadsMap = resources.preloadsMap, precedences = resources.precedences; // If this is the first time we've seen this precedence we encode it's position in our set even though // we don't add the resource to this set yet let precedenceSet = precedences.get(precedence); if (!precedenceSet) { precedenceSet = new Set(); precedences.set(precedence, precedenceSet); } let hint = preloadsMap.get(href); if (hint) { // If a preload for this style Resource already exists there are certain props we want to adopt // on the style Resource, primarily focussed on making sure the style network pathways utilize // the preload pathways. For instance if you have diffreent crossOrigin attributes for a preload // and a stylesheet the stylesheet will make a new request even if the preload had already loaded adoptPreloadPropsForStyleProps(props, hint.props); } else { const preloadResourceProps = preloadAsStylePropsFromProps(href, props); hint = createPreloadResource( resources, href, 'style', preloadResourceProps ); resources.explicitStylePreloads.add(hint); } const resource = { type: 'style', href, precedence, flushed: false, inShell: false, props, hint, set: precedenceSet, }; stylesMap.set(href, resource); return resource; } function adoptPreloadPropsForStyleProps(resourceProps, preloadProps) { if (resourceProps.crossOrigin == null) resourceProps.crossOrigin = preloadProps.crossOrigin; if (resourceProps.referrerPolicy == null) resourceProps.referrerPolicy = preloadProps.referrerPolicy; if (resourceProps.title == null) resourceProps.title = preloadProps.title; } function scriptPropsFromPreinitOptions(src, options) { return { src, async: true, crossOrigin: options.crossOrigin, integrity: options.integrity, }; } function scriptPropsFromRawProps(src, rawProps) { const props = assign({}, rawProps); props.src = src; return props; } function createScriptResource(resources, src, props) { const scriptsMap = resources.scriptsMap, preloadsMap = resources.preloadsMap; let hint = preloadsMap.get(src); if (hint) { // If a preload for this style Resource already exists there are certain props we want to adopt // on the style Resource, primarily focussed on making sure the style network pathways utilize // the preload pathways. For instance if you have diffreent crossOrigin attributes for a preload // and a stylesheet the stylesheet will make a new request even if the preload had already loaded adoptPreloadPropsForScriptProps(props, hint.props); } else { const preloadResourceProps = preloadAsScriptPropsFromProps(src, props); hint = createPreloadResource( resources, src, 'script', preloadResourceProps ); resources.explicitScriptPreloads.add(hint); } const resource = { type: 'script', src, flushed: false, props, hint, }; scriptsMap.set(src, resource); return resource; } function adoptPreloadPropsForScriptProps(resourceProps, preloadProps) { if (resourceProps.crossOrigin == null) resourceProps.crossOrigin = preloadProps.crossOrigin; if (resourceProps.referrerPolicy == null) resourceProps.referrerPolicy = preloadProps.referrerPolicy; if (resourceProps.integrity == null) resourceProps.integrity = preloadProps.integrity; } function titlePropsFromRawProps(child, rawProps) { const props = assign({}, rawProps); props.children = child; return props; } function resourcesFromElement(type, props) { if (!currentResources) { throw new Error( '"currentResources" was expected to exist. This is a bug in React.' ); } const resources = currentResources; switch (type) { case 'title': { let child = props.children; if (Array.isArray(child) && child.length === 1) { child = child[0]; } if (typeof child === 'string' || typeof child === 'number') { const key = 'title::' + child; let resource = resources.headsMap.get(key); if (!resource) { resource = { type: 'title', props: titlePropsFromRawProps(child, props), flushed: false, }; resources.headsMap.set(key, resource); resources.headResources.add(resource); } } return true; } case 'meta': { let key, propertyPath; if (typeof props.charSet === 'string') { key = 'charSet'; } else if (typeof props.content === 'string') { const contentKey = '::' + props.content; if (typeof props.httpEquiv === 'string') { key = 'httpEquiv::' + props.httpEquiv + contentKey; } else if (typeof props.name === 'string') { key = 'name::' + props.name + contentKey; } else if (typeof props.itemProp === 'string') { key = 'itemProp::' + props.itemProp + contentKey; } else if (typeof props.property === 'string') { const property = props.property; key = 'property::' + property + contentKey; propertyPath = property; const parentPath = property.split(':').slice(0, -1).join(':'); const parentResource = resources.structuredMetaKeys.get(parentPath); if (parentResource) { key = parentResource.key + '::child::' + key; } } } if (key) { if (!resources.headsMap.has(key)) { const resource = { type: 'meta', key, props: assign({}, props), flushed: false, }; resources.headsMap.set(key, resource); if (key === 'charSet') { resources.charset = resource; } else { if (propertyPath) { resources.structuredMetaKeys.set(propertyPath, resource); } resources.headResources.add(resource); } } } return true; } case 'base': { const target = props.target, href = props.href; // We mirror the key construction on the client since we will likely unify // this code in the future to better guarantee key semantics are identical // in both environments let key = 'base'; key += typeof href === 'string' ? '[href="' + href + '"]' : ':not([href])'; key += typeof target === 'string' ? '[target="' + target + '"]' : ':not([target])'; if (!resources.headsMap.has(key)) { const resource = { type: 'base', props: assign({}, props), flushed: false, }; resources.headsMap.set(key, resource); resources.bases.add(resource); } return true; } } return false; } // Construct a resource from link props. function resourcesFromLink(props) { if (!currentResources) { throw new Error( '"currentResources" was expected to exist. This is a bug in React.' ); } const resources = currentResources; const rel = props.rel, href = props.href; if (!href || typeof href !== 'string' || !rel || typeof rel !== 'string') { return false; } let key = ''; switch (rel) { case 'stylesheet': { const onLoad = props.onLoad, onError = props.onError, precedence = props.precedence, disabled = props.disabled; if ( typeof precedence !== 'string' || onLoad || onError || disabled != null ) { let preloadResource = resources.preloadsMap.get(href); if (!preloadResource) { preloadResource = createPreloadResource( // $FlowFixMe[incompatible-call] found when upgrading Flow resources, href, 'style', preloadAsStylePropsFromProps(href, props) ); resources.usedStylePreloads.add(preloadResource); } return false; } else { // We are able to convert this link element to a resource exclusively. We construct the relevant Resource // and return true indicating that this link was fully consumed. let resource = resources.stylesMap.get(href); if (resource); else { const resourceProps = stylePropsFromRawProps(href, precedence, props); resource = createStyleResource( // $FlowFixMe[incompatible-call] found when upgrading Flow currentResources, href, precedence, resourceProps ); resources.usedStylePreloads.add(resource.hint); } if (resources.boundaryResources) { resources.boundaryResources.add(resource); } else { resource.set.add(resource); } return true; } } case 'preload': { const as = props.as; switch (as) { case 'script': case 'style': case 'font': { let resource = resources.preloadsMap.get(href); if (resource); else { resource = createPreloadResource( resources, href, as, preloadPropsFromRawProps(href, as, props) ); switch (as) { case 'script': { resources.explicitScriptPreloads.add(resource); break; } case 'style': { resources.explicitStylePreloads.add(resource); break; } case 'font': { resources.fontPreloads.add(resource); break; } } } return true; } } break; } } if (props.onLoad || props.onError) { // When a link has these props we can't treat it is a Resource but if we rendered it on the // server it would look like a Resource in the rendered html (the onLoad/onError aren't emitted) // Instead we expect the client to insert them rather than hydrate them which also guarantees // that the onLoad and onError won't fire before the event handlers are attached return true; } const sizes = typeof props.sizes === 'string' ? props.sizes : ''; const media = typeof props.media === 'string' ? props.media : ''; key = 'rel:' + rel + '::href:' + href + '::sizes:' + sizes + '::media:' + media; let resource = resources.headsMap.get(key); if (!resource) { resource = { type: 'link', props: assign({}, props), flushed: false, }; resources.headsMap.set(key, resource); switch (rel) { case 'preconnect': case 'dns-prefetch': { resources.preconnects.add(resource); break; } default: { resources.headResources.add(resource); } } } return true; } // Construct a resource from link props. function resourcesFromScript(props) { if (!currentResources) { throw new Error( '"currentResources" was expected to exist. This is a bug in React.' ); } const resources = currentResources; const src = props.src, async = props.async, onLoad = props.onLoad, onError = props.onError; if (!src || typeof src !== 'string') { return false; } if (async) { if (onLoad || onError) { let preloadResource = resources.preloadsMap.get(src); if (!preloadResource) { preloadResource = createPreloadResource( // $FlowFixMe[incompatible-call] found when upgrading Flow resources, src, 'script', preloadAsScriptPropsFromProps(src, props) ); resources.usedScriptPreloads.add(preloadResource); } } else { let resource = resources.scriptsMap.get(src); if (resource); else { const resourceProps = scriptPropsFromRawProps(src, props); resource = createScriptResource(resources, src, resourceProps); resources.scripts.add(resource); } } return true; } return false; } function hoistResources(resources, source) { const currentBoundaryResources = resources.boundaryResources; if (currentBoundaryResources) { source.forEach((resource) => currentBoundaryResources.add(resource)); source.clear(); } } function hoistResourcesToRoot(resources, boundaryResources) { boundaryResources.forEach((resource) => resource.set.add(resource)); boundaryResources.clear(); } // The build script is at scripts/rollup/generate-inline-fizz-runtime.js. // Run `yarn generate-inline-fizz-runtime` to generate. const clientRenderBoundary = '$RX=function(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),b._reactRetry&&b._reactRetry())};'; const completeBoundary = '$RC=function(b,c,e){c=document.getElementById(c);c.parentNode.removeChild(c);var a=document.getElementById(b);if(a){b=a.previousSibling;if(e)b.data="$!",a.setAttribute("data-dgst",e);else{e=b.parentNode;a=b.nextSibling;var f=0;do{if(a&&8===a.nodeType){var d=a.data;if("/$"===d)if(0===f)break;else f--;else"$"!==d&&"$?"!==d&&"$!"!==d||f++}d=a.nextSibling;e.removeChild(a);a=d}while(a);for(;c.firstChild;)e.insertBefore(c.firstChild,a);b.data="$"}b._reactRetry&&b._reactRetry()}};'; const completeBoundaryWithStyles = '$RM=new Map;\n$RR=function(p,q,v){function r(l){this.s=l}for(var t=$RC,u=$RM,m=new Map,n=document,g,e,f=n.querySelectorAll("link[data-precedence],style[data-precedence]"),d=0;e=f[d++];)m.set(e.dataset.precedence,g=e);e=0;f=[];for(var c,h,b,a;c=v[e++];){var k=0;h=c[k++];if(b=u.get(h))"l"!==b.s&&f.push(b);else{a=n.createElement("link");a.href=h;a.rel="stylesheet";for(a.dataset.precedence=d=c[k++];b=c[k++];)a.setAttribute(b,c[k++]);b=a._p=new Promise(function(l,w){a.onload=l;a.onerror=w});b.then(r.bind(b,\n"l"),r.bind(b,"e"));u.set(h,b);f.push(b);c=m.get(d)||g;c===g&&(g=a);m.set(d,a);c?c.parentNode.insertBefore(a,c.nextSibling):(d=n.head,d.insertBefore(a,d.firstChild))}}Promise.all(f).then(t.bind(null,p,q,""),t.bind(null,p,q,"Resource failed to load"))};'; const completeSegment = '$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};'; const ReactDOMSharedInternals = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; function prepareToRender(resources) { prepareToRenderResources(resources); const previousHostDispatcher = ReactDOMCurrentDispatcher.current; ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher; return previousHostDispatcher; } function cleanupAfterRender(previousDispatcher) { finishRenderingResources(); ReactDOMCurrentDispatcher.current = previousDispatcher; } // Used to distinguish these contexts from ones used in other renderers. const startInlineScript = stringToPrecomputedChunk(''); const startScriptSrc = stringToPrecomputedChunk(''); /** * This escaping function is designed to work with bootstrapScriptContent only. * because we know we are escaping the entire script. We can avoid for instance * escaping html comment string sequences that are valid javascript as well because * if there are no sebsequent '); function writeCompletedSegmentInstruction( destination, responseState, contentSegmentID ) { writeChunk(destination, responseState.startInlineScript); if (!responseState.sentCompleteSegmentFunction) { // The first time we write this, we'll need to include the full implementation. responseState.sentCompleteSegmentFunction = true; writeChunk(destination, completeSegmentScript1Full); } else { // Future calls can just reuse the same function. writeChunk(destination, completeSegmentScript1Partial); } writeChunk(destination, responseState.segmentPrefix); const formattedID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, formattedID); writeChunk(destination, completeSegmentScript2); writeChunk(destination, responseState.placeholderPrefix); writeChunk(destination, formattedID); return writeChunkAndReturn(destination, completeSegmentScript3); } const completeBoundaryScript1Full = stringToPrecomputedChunk( completeBoundary + ';$RC("' ); const completeBoundaryScript1Partial = stringToPrecomputedChunk('$RC("'); const completeBoundaryWithStylesScript1FullBoth = stringToPrecomputedChunk( completeBoundary + ';' + completeBoundaryWithStyles + ';$RR("' ); const completeBoundaryWithStylesScript1FullPartial = stringToPrecomputedChunk( completeBoundaryWithStyles + ';$RR("' ); const completeBoundaryWithStylesScript1Partial = stringToPrecomputedChunk('$RR("'); const completeBoundaryScript2 = stringToPrecomputedChunk('","'); const completeBoundaryScript2a = stringToPrecomputedChunk('",'); const completeBoundaryScript3 = stringToPrecomputedChunk('"'); const completeBoundaryScript4 = stringToPrecomputedChunk(')'); function writeCompletedBoundaryInstruction( destination, responseState, boundaryID, contentSegmentID, boundaryResources ) { let hasStyleDependencies; { hasStyleDependencies = hasStyleResourceDependencies(boundaryResources); } writeChunk(destination, responseState.startInlineScript); if (hasStyleDependencies) { if (!responseState.sentCompleteBoundaryFunction) { responseState.sentCompleteBoundaryFunction = true; responseState.sentStyleInsertionFunction = true; writeChunk(destination, completeBoundaryWithStylesScript1FullBoth); } else if (!responseState.sentStyleInsertionFunction) { responseState.sentStyleInsertionFunction = true; writeChunk(destination, completeBoundaryWithStylesScript1FullPartial); } else { writeChunk(destination, completeBoundaryWithStylesScript1Partial); } } else { if (!responseState.sentCompleteBoundaryFunction) { responseState.sentCompleteBoundaryFunction = true; writeChunk(destination, completeBoundaryScript1Full); } else { writeChunk(destination, completeBoundaryScript1Partial); } } if (boundaryID === null) { throw new Error( 'An ID must have been assigned before we can complete the boundary.' ); } const formattedContentID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, boundaryID); writeChunk(destination, completeBoundaryScript2); writeChunk(destination, responseState.segmentPrefix); writeChunk(destination, formattedContentID); if (hasStyleDependencies) { writeChunk(destination, completeBoundaryScript2a); writeStyleResourceDependencies(destination, boundaryResources); } else { writeChunk(destination, completeBoundaryScript3); } return writeChunkAndReturn(destination, completeBoundaryScript4); } const clientRenderScript1Full = stringToPrecomputedChunk( clientRenderBoundary + ';$RX("' ); const clientRenderScript1Partial = stringToPrecomputedChunk('$RX("'); const clientRenderScript1A = stringToPrecomputedChunk('"'); const clientRenderScript2 = stringToPrecomputedChunk(')'); const clientRenderErrorScriptArgInterstitial = stringToPrecomputedChunk(','); function writeClientRenderBoundaryInstruction( destination, responseState, boundaryID, errorDigest, errorMessage, errorComponentStack ) { writeChunk(destination, responseState.startInlineScript); if (!responseState.sentClientRenderFunction) { // The first time we write this, we'll need to include the full implementation. responseState.sentClientRenderFunction = true; writeChunk(destination, clientRenderScript1Full); } else { // Future calls can just reuse the same function. writeChunk(destination, clientRenderScript1Partial); } if (boundaryID === null) { throw new Error( 'An ID must have been assigned before we can complete the boundary.' ); } writeChunk(destination, boundaryID); writeChunk(destination, clientRenderScript1A); if (errorDigest || errorMessage || errorComponentStack) { writeChunk(destination, clientRenderErrorScriptArgInterstitial); writeChunk( destination, stringToChunk(escapeJSStringsForInstructionScripts(errorDigest || '')) ); } if (errorMessage || errorComponentStack) { writeChunk(destination, clientRenderErrorScriptArgInterstitial); writeChunk( destination, stringToChunk(escapeJSStringsForInstructionScripts(errorMessage || '')) ); } if (errorComponentStack) { writeChunk(destination, clientRenderErrorScriptArgInterstitial); writeChunk( destination, stringToChunk(escapeJSStringsForInstructionScripts(errorComponentStack)) ); } return writeChunkAndReturn(destination, clientRenderScript2); } const regexForJSStringsInInstructionScripts = /[<\u2028\u2029]/g; function escapeJSStringsForInstructionScripts(input) { const escaped = JSON.stringify(input); return escaped.replace(regexForJSStringsInInstructionScripts, (match) => { switch (match) { // santizing breaking out of strings and script tags case '<': return '\\u003c'; case '\u2028': return '\\u2028'; case '\u2029': return '\\u2029'; default: { // eslint-disable-next-line react-internal/prod-error-codes 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) { const escaped = JSON.stringify(input); return escaped.replace(regexForJSStringsInScripts, (match) => { switch (match) { // santizing breaking out of strings and script tags case '&': return '\\u0026'; case '>': return '\\u003e'; case '<': return '\\u003c'; case '\u2028': return '\\u2028'; case '\u2029': return '\\u2029'; default: { // eslint-disable-next-line react-internal/prod-error-codes 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 precedencePlaceholderStart = stringToPrecomputedChunk( ''); function writeInitialResources(destination, resources, responseState) { function flushLinkResource(resource) { if (!resource.flushed) { pushLinkImpl(target, resource.props, responseState); resource.flushed = true; } } const target = []; const charset = resources.charset, bases = resources.bases, preconnects = resources.preconnects, fontPreloads = resources.fontPreloads, precedences = resources.precedences, usedStylePreloads = resources.usedStylePreloads, scripts = resources.scripts, usedScriptPreloads = resources.usedScriptPreloads, explicitStylePreloads = resources.explicitStylePreloads, explicitScriptPreloads = resources.explicitScriptPreloads, headResources = resources.headResources; if (charset) { pushSelfClosing(target, charset.props, 'meta', responseState); charset.flushed = true; resources.charset = null; } bases.forEach((r) => { pushSelfClosing(target, r.props, 'base', responseState); r.flushed = true; }); bases.clear(); preconnects.forEach((r) => { // font preload Resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); r.flushed = true; }); preconnects.clear(); fontPreloads.forEach((r) => { // font preload Resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); r.flushed = true; }); fontPreloads.clear(); // Flush stylesheets first by earliest precedence precedences.forEach((p, precedence) => { if (p.size) { p.forEach((r) => { // resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); r.flushed = true; r.inShell = true; r.hint.flushed = true; }); p.clear(); } else { target.push( precedencePlaceholderStart, stringToChunk(escapeTextForBrowser(precedence)), precedencePlaceholderEnd ); } }); usedStylePreloads.forEach(flushLinkResource); usedStylePreloads.clear(); scripts.forEach((r) => { // should never be flushed already pushScriptImpl(target, r.props, responseState); r.flushed = true; r.hint.flushed = true; }); scripts.clear(); usedScriptPreloads.forEach(flushLinkResource); usedScriptPreloads.clear(); explicitStylePreloads.forEach(flushLinkResource); explicitStylePreloads.clear(); explicitScriptPreloads.forEach(flushLinkResource); explicitScriptPreloads.clear(); headResources.forEach((r) => { switch (r.type) { case 'title': { pushTitleImpl(target, r.props, responseState); break; } case 'meta': { pushSelfClosing(target, r.props, 'meta', responseState); break; } case 'link': { pushLinkImpl(target, r.props, responseState); break; } } r.flushed = true; }); headResources.clear(); let i; let r = true; for (i = 0; i < target.length - 1; i++) { writeChunk(destination, target[i]); } if (i < target.length) { r = writeChunkAndReturn(destination, target[i]); } return r; } function writeImmediateResources(destination, resources, responseState) { function flushLinkResource(resource) { if (!resource.flushed) { pushLinkImpl(target, resource.props, responseState); resource.flushed = true; } } const target = []; const charset = resources.charset, preconnects = resources.preconnects, fontPreloads = resources.fontPreloads, usedStylePreloads = resources.usedStylePreloads, scripts = resources.scripts, usedScriptPreloads = resources.usedScriptPreloads, explicitStylePreloads = resources.explicitStylePreloads, explicitScriptPreloads = resources.explicitScriptPreloads, headResources = resources.headResources; if (charset) { pushSelfClosing(target, charset.props, 'meta', responseState); charset.flushed = true; resources.charset = null; } preconnects.forEach((r) => { // font preload Resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); r.flushed = true; }); preconnects.clear(); fontPreloads.forEach((r) => { // font preload Resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); r.flushed = true; }); fontPreloads.clear(); usedStylePreloads.forEach(flushLinkResource); usedStylePreloads.clear(); scripts.forEach((r) => { // should never be flushed already pushStartGenericElement(target, r.props, 'script', responseState); pushEndInstance(target, target, 'script', r.props); r.flushed = true; r.hint.flushed = true; }); scripts.clear(); usedScriptPreloads.forEach(flushLinkResource); usedScriptPreloads.clear(); explicitStylePreloads.forEach(flushLinkResource); explicitStylePreloads.clear(); explicitScriptPreloads.forEach(flushLinkResource); explicitScriptPreloads.clear(); headResources.forEach((r) => { switch (r.type) { case 'title': { pushTitleImpl(target, r.props, responseState); break; } case 'meta': { pushSelfClosing(target, r.props, 'meta', responseState); break; } case 'link': { pushLinkImpl(target, r.props, responseState); break; } } r.flushed = true; }); headResources.clear(); let i; let r = true; for (i = 0; i < target.length - 1; i++) { writeChunk(destination, target[i]); } if (i < target.length) { r = writeChunkAndReturn(destination, target[i]); } return r; } function hasStyleResourceDependencies(boundaryResources) { const iter = boundaryResources.values(); // At the moment boundaries only accumulate style resources // so we assume the type is correct and don't check it while (true) { const _iter$next = iter.next(), resource = _iter$next.value; if (!resource) break; // If every style Resource flushed in the shell we do not need to send // any dependencies if (!resource.inShell) { return true; } } return false; } const arrayFirstOpenBracket = stringToPrecomputedChunk('['); const arraySubsequentOpenBracket = stringToPrecomputedChunk(',['); const arrayInterstitial = stringToPrecomputedChunk(','); const arrayCloseBracket = stringToPrecomputedChunk(']'); function writeStyleResourceDependencies(destination, boundaryResources) { writeChunk(destination, arrayFirstOpenBracket); let nextArrayOpenBrackChunk = arrayFirstOpenBracket; boundaryResources.forEach((resource) => { if (resource.inShell); else if (resource.flushed) { writeChunk(destination, nextArrayOpenBrackChunk); writeStyleResourceDependencyHrefOnly(destination, resource.href); writeChunk(destination, arrayCloseBracket); nextArrayOpenBrackChunk = arraySubsequentOpenBracket; } else { writeChunk(destination, nextArrayOpenBrackChunk); writeStyleResourceDependency( destination, resource.href, resource.precedence, resource.props ); writeChunk(destination, arrayCloseBracket); nextArrayOpenBrackChunk = arraySubsequentOpenBracket; resource.flushed = true; resource.hint.flushed = true; } }); writeChunk(destination, arrayCloseBracket); } function writeStyleResourceDependencyHrefOnly(destination, href) { const coercedHref = '' + href; writeChunk( destination, stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)) ); } function writeStyleResourceDependency(destination, href, precedence, props) { const coercedHref = '' + href; writeChunk( destination, stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)) ); const coercedPrecedence = '' + precedence; 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`.' ); // eslint-disable-next-line-no-fallthrough default: writeStyleResourceAttribute(destination, propKey, propValue); break; } } } return null; } function writeStyleResourceAttribute(destination, name, value) { let attributeName = name.toLowerCase(); let attributeValue; switch (typeof value) { case 'function': case 'symbol': return; } switch (name) { // Reserved names case 'innerHTML': case 'dangerouslySetInnerHTML': case 'suppressContentEditableWarning': case 'suppressHydrationWarning': case 'style': // Ignored return; // Attribute renames case 'className': attributeName = 'class'; break; // Booleans case 'hidden': if (value === false) { return; } attributeValue = ''; break; // Santized URLs case 'src': case 'href': { attributeValue = '' + value; break; } default: { if (!isAttributeNameSafe(name)) { return; } } } if ( // shouldIgnoreAttribute // We have already filtered out null/undefined and reserved words. name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N') ) { return; } attributeValue = '' + value; writeChunk(destination, arrayInterstitial); writeChunk( destination, stringToChunk(escapeJSObjectForInstructionScripts(attributeName)) ); writeChunk(destination, arrayInterstitial); writeChunk( destination, stringToChunk(escapeJSObjectForInstructionScripts(attributeValue)) ); } // ATTENTION // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' // The Symbol used to tag the ReactElement-like types. const REACT_ELEMENT_TYPE = Symbol.for('react.element'); const REACT_PORTAL_TYPE = Symbol.for('react.portal'); const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); const REACT_PROFILER_TYPE = Symbol.for('react.profiler'); const REACT_PROVIDER_TYPE = Symbol.for('react.provider'); const REACT_CONTEXT_TYPE = Symbol.for('react.context'); const REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context'); const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); const REACT_MEMO_TYPE = Symbol.for('react.memo'); const REACT_LAZY_TYPE = Symbol.for('react.lazy'); const REACT_SCOPE_TYPE = Symbol.for('react.scope'); const REACT_DEBUG_TRACING_MODE_TYPE = Symbol.for('react.debug_trace_mode'); const REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); const REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden'); const REACT_CACHE_TYPE = Symbol.for('react.cache'); const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for( 'react.default_value' ); const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel'); const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; const FAUX_ITERATOR_SYMBOL = '@@iterator'; function getIteratorFn(maybeIterable) { if (maybeIterable === null || typeof maybeIterable !== 'object') { return null; } const maybeIterator = (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || maybeIterable[FAUX_ITERATOR_SYMBOL]; if (typeof maybeIterator === 'function') { return maybeIterator; } return null; } function getWrappedName(outerType, innerType, wrapperName) { const displayName = outerType.displayName; if (displayName) { return displayName; } const functionName = innerType.displayName || innerType.name || ''; return functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName; } // Keep in sync with react-reconciler/getComponentNameFromFiber function getContextName(type) { return type.displayName || 'Context'; } // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead. function getComponentNameFromType(type) { if (type == null) { // Host root, text node or just invalid type. return null; } if (typeof type === 'function') { return type.displayName || type.name || null; } if (typeof type === 'string') { return type; } switch (type) { case REACT_FRAGMENT_TYPE: return 'Fragment'; case REACT_PORTAL_TYPE: return 'Portal'; case REACT_PROFILER_TYPE: return 'Profiler'; case REACT_STRICT_MODE_TYPE: return 'StrictMode'; case REACT_SUSPENSE_TYPE: return 'Suspense'; case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; case REACT_CACHE_TYPE: { return 'Cache'; } } if (typeof type === 'object') { switch (type.$$typeof) { case REACT_CONTEXT_TYPE: const context = type; return getContextName(context) + '.Consumer'; case REACT_PROVIDER_TYPE: const provider = type; return getContextName(provider._context) + '.Provider'; case REACT_FORWARD_REF_TYPE: return getWrappedName(type, type.render, 'ForwardRef'); case REACT_MEMO_TYPE: const outerName = type.displayName || null; if (outerName !== null) { return outerName; } return getComponentNameFromType(type.type) || 'Memo'; case REACT_LAZY_TYPE: { const lazyComponent = type; const payload = lazyComponent._payload; const init = lazyComponent._init; try { return getComponentNameFromType(init(payload)); } catch (x) { return null; } } case REACT_SERVER_CONTEXT_TYPE: { const context2 = type; return (context2.displayName || context2._globalName) + '.Provider'; } // eslint-disable-next-line no-fallthrough } } return null; } const ReactSharedInternals = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED$1; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; const emptyContextObject = {}; function getMaskedContext(type, unmaskedContext) { { const contextTypes = type.contextTypes; if (!contextTypes) { return emptyContextObject; } const context = {}; for (const key in contextTypes) { context[key] = unmaskedContext[key]; } return context; } } function processChildContext(instance, type, parentContext, childContextTypes) { { // TODO (bvaughn) Replace this behavior with an invariant() in the future. // It has only been added in Fiber to match the (unintentional) behavior in Stack. if (typeof instance.getChildContext !== 'function') { return parentContext; } const childContext = instance.getChildContext(); for (const contextKey in childContext) { if (!(contextKey in childContextTypes)) { throw new Error( (getComponentNameFromType(type) || 'Unknown') + '.getChildContext(): key "' + contextKey + '" is not defined in childContextTypes.' ); } } return assign({}, parentContext, childContext); } } // Forming a reverse tree. const rootContextSnapshot = null; // We assume that this runtime owns the "current" field on all ReactContext instances. // This global (actually thread local) state represents what state all those "current", // fields are currently in. let currentActiveSnapshot = null; function popNode(prev) { { prev.context._currentValue = prev.parentValue; } } function pushNode(next) { { next.context._currentValue = next.value; } } function popToNearestCommonAncestor(prev, next) { if (prev === next); else { popNode(prev); const parentPrev = prev.parent; const parentNext = next.parent; if (parentPrev === null) { if (parentNext !== null) { throw new Error( 'The stacks must reach the root at the same time. This is a bug in React.' ); } } else { if (parentNext === null) { throw new Error( 'The stacks must reach the root at the same time. This is a bug in React.' ); } popToNearestCommonAncestor(parentPrev, parentNext); } // On the way back, we push the new ones that weren't common. pushNode(next); } } function popAllPrevious(prev) { popNode(prev); const parentPrev = prev.parent; if (parentPrev !== null) { popAllPrevious(parentPrev); } } function pushAllNext(next) { const parentNext = next.parent; if (parentNext !== null) { pushAllNext(parentNext); } pushNode(next); } function popPreviousToCommonLevel(prev, next) { popNode(prev); const parentPrev = prev.parent; if (parentPrev === null) { throw new Error( 'The depth must equal at least at zero before reaching the root. This is a bug in React.' ); } if (parentPrev.depth === next.depth) { // We found the same level. Now we just need to find a shared ancestor. popToNearestCommonAncestor(parentPrev, next); } else { // We must still be deeper. popPreviousToCommonLevel(parentPrev, next); } } function popNextToCommonLevel(prev, next) { const parentNext = next.parent; if (parentNext === null) { throw new Error( 'The depth must equal at least at zero before reaching the root. This is a bug in React.' ); } if (prev.depth === parentNext.depth) { // We found the same level. Now we just need to find a shared ancestor. popToNearestCommonAncestor(prev, parentNext); } else { // We must still be deeper. popNextToCommonLevel(prev, parentNext); } pushNode(next); } // Perform context switching to the new snapshot. // To make it cheap to read many contexts, while not suspending, we make the switch eagerly by // updating all the context's current values. That way reads, always just read the current value. // At the cost of updating contexts even if they're never read by this subtree. function switchContext(newSnapshot) { // The basic algorithm we need to do is to pop back any contexts that are no longer on the stack. // We also need to update any new contexts that are now on the stack with the deepest value. // The easiest way to update new contexts is to just reapply them in reverse order from the // perspective of the backpointers. To avoid allocating a lot when switching, we use the stack // for that. Therefore this algorithm is recursive. // 1) First we pop which ever snapshot tree was deepest. Popping old contexts as we go. // 2) Then we find the nearest common ancestor from there. Popping old contexts as we go. // 3) Then we reapply new contexts on the way back up the stack. const prev = currentActiveSnapshot; const next = newSnapshot; if (prev !== next) { if (prev === null) { // $FlowFixMe: This has to be non-null since it's not equal to prev. pushAllNext(next); } else if (next === null) { popAllPrevious(prev); } else if (prev.depth === next.depth) { popToNearestCommonAncestor(prev, next); } else if (prev.depth > next.depth) { popPreviousToCommonLevel(prev, next); } else { popNextToCommonLevel(prev, next); } currentActiveSnapshot = next; } } function pushProvider(context, nextValue) { let prevValue; { prevValue = context._currentValue; context._currentValue = nextValue; } const prevNode = currentActiveSnapshot; const newNode = { parent: prevNode, depth: prevNode === null ? 0 : prevNode.depth + 1, context: context, parentValue: prevValue, value: nextValue, }; currentActiveSnapshot = newNode; return newNode; } function popProvider(context) { const prevSnapshot = currentActiveSnapshot; if (prevSnapshot === null) { throw new Error( 'Tried to pop a Context at the root of the app. This is a bug in React.' ); } { const value = prevSnapshot.parentValue; if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) { prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue; } else { prevSnapshot.context._currentValue = value; } } return (currentActiveSnapshot = prevSnapshot.parent); } function getActiveContext() { return currentActiveSnapshot; } function readContext(context) { const value = context._currentValue; return value; } /** * `ReactInstanceMap` maintains a mapping from a public facing stateful * instance (key) and the internal representation (value). This allows public * methods to accept the user facing instance as an argument and map them back * to internal methods. * * Note that this module is currently shared and assumed to be stateless. * If this becomes an actual Map, that will break. */ function get(key) { return key._reactInternals; } function set(key, value) { key._reactInternals = value; } const classComponentUpdater = { isMounted(inst) { return false; }, enqueueSetState(inst, payload, callback) { const internals = get(inst); if (internals.queue === null); else { internals.queue.push(payload); } }, enqueueReplaceState(inst, payload, callback) { const internals = get(inst); internals.replace = true; internals.queue = [payload]; }, enqueueForceUpdate(inst, callback) { const internals = get(inst); if (internals.queue === null); }, }; function applyDerivedStateFromProps( instance, ctor, getDerivedStateFromProps, prevState, nextProps ) { const partialState = getDerivedStateFromProps(nextProps, prevState); const newState = partialState === null || partialState === undefined ? prevState : assign({}, prevState, partialState); return newState; } function constructClassInstance(ctor, props, maskedLegacyContext) { let context = emptyContextObject; const contextType = ctor.contextType; if (typeof contextType === 'object' && contextType !== null) { context = readContext(contextType); } else { context = maskedLegacyContext; } const instance = new ctor(props, context); return instance; } function callComponentWillMount(type, instance) { const oldState = instance.state; if (typeof instance.componentWillMount === 'function') { instance.componentWillMount(); } if (typeof instance.UNSAFE_componentWillMount === 'function') { instance.UNSAFE_componentWillMount(); } if (oldState !== instance.state) { classComponentUpdater.enqueueReplaceState(instance, instance.state, null); } } function processUpdateQueue( internalInstance, inst, props, maskedLegacyContext ) { if (internalInstance.queue !== null && internalInstance.queue.length > 0) { const oldQueue = internalInstance.queue; const oldReplace = internalInstance.replace; internalInstance.queue = null; internalInstance.replace = false; if (oldReplace && oldQueue.length === 1) { inst.state = oldQueue[0]; } else { let nextState = oldReplace ? oldQueue[0] : inst.state; let dontMutate = true; for (let i = oldReplace ? 1 : 0; i < oldQueue.length; i++) { const partial = oldQueue[i]; const partialState = typeof partial === 'function' ? partial.call(inst, nextState, props, maskedLegacyContext) : partial; if (partialState != null) { if (dontMutate) { dontMutate = false; nextState = assign({}, nextState, partialState); } else { assign(nextState, partialState); } } } inst.state = nextState; } } else { internalInstance.queue = null; } } // Invokes the mount life-cycles on a previously never rendered instance. function mountClassInstance(instance, ctor, newProps, maskedLegacyContext) { const initialState = instance.state !== undefined ? instance.state : null; instance.updater = classComponentUpdater; instance.props = newProps; instance.state = initialState; // We don't bother initializing the refs object on the server, since we're not going to resolve them anyway. // The internal instance will be used to manage updates that happen during this mount. const internalInstance = { queue: [], replace: false, }; set(instance, internalInstance); const contextType = ctor.contextType; if (typeof contextType === 'object' && contextType !== null) { instance.context = readContext(contextType); } else { instance.context = maskedLegacyContext; } const getDerivedStateFromProps = ctor.getDerivedStateFromProps; if (typeof getDerivedStateFromProps === 'function') { instance.state = applyDerivedStateFromProps( instance, ctor, getDerivedStateFromProps, initialState, newProps ); } // In order to support react-lifecycles-compat polyfilled components, // Unsafe lifecycles should not be invoked for components using the new APIs. if ( typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function') ) { callComponentWillMount(ctor, instance); // If we had additional state updates during this life-cycle, let's // process them now. processUpdateQueue( internalInstance, instance, newProps, maskedLegacyContext ); } } // Ids are base 32 strings whose binary representation corresponds to the // position of a node in a tree. // Every time the tree forks into multiple children, we add additional bits to // the left of the sequence that represent the position of the child within the // current level of children. // // 00101 00010001011010101 // ╰─┬─╯ ╰───────┬───────╯ // Fork 5 of 20 Parent id // // The leading 0s are important. In the above example, you only need 3 bits to // represent slot 5. However, you need 5 bits to represent all the forks at // the current level, so we must account for the empty bits at the end. // // For this same reason, slots are 1-indexed instead of 0-indexed. Otherwise, // the zeroth id at a level would be indistinguishable from its parent. // // If a node has only one child, and does not materialize an id (i.e. does not // contain a useId hook), then we don't need to allocate any space in the // sequence. It's treated as a transparent indirection. For example, these two // trees produce the same ids: // // <> <> // // // // // // // However, we cannot skip any node that materializes an id. Otherwise, a parent // id that does not fork would be indistinguishable from its child id. For // example, this tree does not fork, but the parent and child must have // different ids. // // // // // // To handle this scenario, every time we materialize an id, we allocate a // new level with a single slot. You can think of this as a fork with only one // prong, or an array of children with length 1. // // It's possible for the size of the sequence to exceed 32 bits, the max // size for bitwise operations. When this happens, we make more room by // converting the right part of the id to a string and storing it in an overflow // variable. We use a base 32 string representation, because 32 is the largest // power of 2 that is supported by toString(). We want the base to be large so // that the resulting ids are compact, and we want the base to be a power of 2 // because every log2(base) bits corresponds to a single character, i.e. every // log2(32) = 5 bits. That means we can lop bits off the end 5 at a time without // affecting the final result. const emptyTreeContext = { id: 1, overflow: '', }; function getTreeId(context) { const overflow = context.overflow; const idWithLeadingBit = context.id; const id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit); return id.toString(32) + overflow; } function pushTreeContext(baseContext, totalChildren, index) { const baseIdWithLeadingBit = baseContext.id; const baseOverflow = baseContext.overflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part // of the id; we use it to account for leading 0s. const baseLength = getBitLength(baseIdWithLeadingBit) - 1; const baseId = baseIdWithLeadingBit & ~(1 << baseLength); const slot = index + 1; const length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into // consideration the leading 1 we use to mark the end of the sequence. if (length > 30) { // We overflowed the bitwise-safe range. Fall back to slower algorithm. // This branch assumes the length of the base id is greater than 5; it won't // work for smaller ids, because you need 5 bits per character. // // We encode the id in multiple steps: first the base id, then the // remaining digits. // // Each 5 bit sequence corresponds to a single base 32 character. So for // example, if the current id is 23 bits long, we can convert 20 of those // bits into a string of 4 characters, with 3 bits left over. // // First calculate how many bits in the base id represent a complete // sequence of characters. const numberOfOverflowBits = baseLength - (baseLength % 5); // Then create a bitmask that selects only those bits. const newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string. const newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id. const restOfBaseId = baseId >> numberOfOverflowBits; const restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because // we made more room, this time it won't overflow. const restOfLength = getBitLength(totalChildren) + restOfBaseLength; const restOfNewBits = slot << restOfBaseLength; const id = restOfNewBits | restOfBaseId; const overflow = newOverflow + baseOverflow; return { id: (1 << restOfLength) | id, overflow, }; } else { // Normal path const newBits = slot << baseLength; const id = newBits | baseId; const overflow = baseOverflow; return { id: (1 << length) | id, overflow, }; } } function getBitLength(number) { return 32 - clz32(number); } function getLeadingBit(id) { return 1 << (getBitLength(id) - 1); } // TODO: Math.clz32 is supported in Node 12+. Maybe we can drop the fallback. const clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros. // Based on: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 const log = Math.log; const LN2 = Math.LN2; function clz32Fallback(x) { const asUint = x >>> 0; if (asUint === 0) { return 32; } return (31 - ((log(asUint) / LN2) | 0)) | 0; } // Corresponds to ReactFiberWakeable and ReactFlightWakeable modules. Generally, // changes to one module should be reflected in the others. // TODO: Rename this module and the corresponding Fiber one to "Thenable" // instead of "Wakeable". Or some other more appropriate name. function createThenableState() { // The ThenableState is created the first time a component suspends. If it // suspends again, we'll reuse the same state. return []; } function noop() {} function trackUsedThenable(thenableState, thenable, index) { const previous = thenableState[index]; if (previous === undefined) { thenableState.push(thenable); } else { if (previous !== thenable) { // Reuse the previous thenable, and drop the new one. We can assume // they represent the same value, because components are idempotent. // Avoid an unhandled rejection errors for the Promises that we'll // intentionally ignore. thenable.then(noop, noop); thenable = previous; } } // We use an expando to track the status and result of a thenable so that we // can synchronously unwrap the value. Think of this as an extension of the // Promise API, or a custom interface that is a superset of Thenable. // // If the thenable doesn't have a status, set it to "pending" and attach // a listener that will update its status and result when it resolves. switch (thenable.status) { case 'fulfilled': { const fulfilledValue = thenable.value; return fulfilledValue; } case 'rejected': { const rejectedError = thenable.reason; throw rejectedError; } default: { if (typeof thenable.status === 'string'); else { const pendingThenable = thenable; pendingThenable.status = 'pending'; pendingThenable.then( (fulfilledValue) => { if (thenable.status === 'pending') { const fulfilledThenable = thenable; fulfilledThenable.status = 'fulfilled'; fulfilledThenable.value = fulfilledValue; } }, (error) => { if (thenable.status === 'pending') { const rejectedThenable = thenable; rejectedThenable.status = 'rejected'; rejectedThenable.reason = error; } } ); } // Suspend. // TODO: Throwing here is an implementation detail that allows us to // unwind the call stack. But we shouldn't allow it to leak into // userspace. Throw an opaque placeholder value instead of the // actual thenable. If it doesn't get captured by the work loop, log // a warning, because that means something in userspace must have // caught it. throw thenable; } } } /** * inlined Object.is polyfill to avoid requiring consumers ship their own * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is */ function is(x, y) { return ( (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare ); } const objectIs = typeof Object.is === 'function' ? Object.is : is; // $FlowFixMe[method-unbinding] let currentlyRenderingComponent = null; let currentlyRenderingTask = null; let firstWorkInProgressHook = null; let workInProgressHook = null; // Whether the work-in-progress hook is a re-rendered hook let isReRender = false; // Whether an update was scheduled during the currently executing render pass. let didScheduleRenderPhaseUpdate = false; // Counts the number of useId hooks in this component let localIdCounter = 0; // Counts the number of use(thenable) calls in this component let thenableIndexCounter = 0; let thenableState = null; // Lazily created map of render-phase updates let renderPhaseUpdates = null; // Counter to prevent infinite loops. let numberOfReRenders = 0; const RE_RENDER_LIMIT = 25; function resolveCurrentlyRenderingComponent() { if (currentlyRenderingComponent === null) { throw new Error( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.' ); } return currentlyRenderingComponent; } function areHookInputsEqual(nextDeps, prevDeps) { if (prevDeps === null) { return false; } for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) { // $FlowFixMe[incompatible-use] found when upgrading Flow if (objectIs(nextDeps[i], prevDeps[i])) { continue; } return false; } return true; } function createHook() { if (numberOfReRenders > 0) { throw new Error('Rendered more hooks than during the previous render'); } return { memoizedState: null, queue: null, next: null, }; } function createWorkInProgressHook() { if (workInProgressHook === null) { // This is the first hook in the list if (firstWorkInProgressHook === null) { isReRender = false; firstWorkInProgressHook = workInProgressHook = createHook(); } else { // There's already a work-in-progress. Reuse it. isReRender = true; workInProgressHook = firstWorkInProgressHook; } } else { if (workInProgressHook.next === null) { isReRender = false; // Append to the end of the list workInProgressHook = workInProgressHook.next = createHook(); } else { // There's already a work-in-progress. Reuse it. isReRender = true; workInProgressHook = workInProgressHook.next; } } return workInProgressHook; } function prepareToUseHooks(task, componentIdentity, prevThenableState) { currentlyRenderingComponent = componentIdentity; currentlyRenderingTask = task; // didScheduleRenderPhaseUpdate = false; // firstWorkInProgressHook = null; // numberOfReRenders = 0; // renderPhaseUpdates = null; // workInProgressHook = null; localIdCounter = 0; thenableIndexCounter = 0; thenableState = prevThenableState; } function finishHooks(Component, props, children, refOrContext) { // This must be called after every function component to prevent hooks from // being used in classes. while (didScheduleRenderPhaseUpdate) { // Updates were scheduled during the render phase. They are stored in // the `renderPhaseUpdates` map. Call the component again, reusing the // work-in-progress hooks and applying the additional updates on top. Keep // restarting until no more updates are scheduled. didScheduleRenderPhaseUpdate = false; localIdCounter = 0; thenableIndexCounter = 0; numberOfReRenders += 1; // Start over from the beginning of the list workInProgressHook = null; children = Component(props, refOrContext); } resetHooksState(); return children; } function getThenableStateAfterSuspending() { const state = thenableState; thenableState = null; return state; } function checkDidRenderIdHook() { // This should be called immediately after every finishHooks call. // Conceptually, it's part of the return value of finishHooks; it's only a // separate function to avoid using an array tuple. const didRenderIdHook = localIdCounter !== 0; return didRenderIdHook; } // Reset the internal hooks state if an error occurs while rendering a component function resetHooksState() { currentlyRenderingComponent = null; currentlyRenderingTask = null; didScheduleRenderPhaseUpdate = false; firstWorkInProgressHook = null; numberOfReRenders = 0; renderPhaseUpdates = null; workInProgressHook = null; } function readContext$1(context) { return readContext(context); } function useContext(context) { resolveCurrentlyRenderingComponent(); return readContext(context); } function basicStateReducer(state, action) { // $FlowFixMe: Flow doesn't like mixed types return typeof action === 'function' ? action(state) : action; } function useState(initialState) { return useReducer( basicStateReducer, // useReducer has a special case to support lazy useState initializers initialState ); } function useReducer(reducer, initialArg, init) { currentlyRenderingComponent = resolveCurrentlyRenderingComponent(); workInProgressHook = createWorkInProgressHook(); if (isReRender) { // This is a re-render. Apply the new render phase updates to the previous // current hook. const queue = workInProgressHook.queue; const dispatch = queue.dispatch; if (renderPhaseUpdates !== null) { // Render phase updates are stored in a map of queue -> linked list const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue); if (firstRenderPhaseUpdate !== undefined) { // $FlowFixMe[incompatible-use] found when upgrading Flow renderPhaseUpdates.delete(queue); // $FlowFixMe[incompatible-use] found when upgrading Flow let newState = workInProgressHook.memoizedState; let update = firstRenderPhaseUpdate; do { // Process this render phase update. We don't have to check the // priority because it will always be the same as the current // render's. const action = update.action; newState = reducer(newState, action); update = update.next; } while (update !== null); // $FlowFixMe[incompatible-use] found when upgrading Flow workInProgressHook.memoizedState = newState; return [newState, dispatch]; } } // $FlowFixMe[incompatible-use] found when upgrading Flow return [workInProgressHook.memoizedState, dispatch]; } else { let initialState; if (reducer === basicStateReducer) { // Special case for `useState`. initialState = typeof initialArg === 'function' ? initialArg() : initialArg; } else { initialState = init !== undefined ? init(initialArg) : initialArg; } workInProgressHook.memoizedState = initialState; // $FlowFixMe[incompatible-use] found when upgrading Flow const queue = (workInProgressHook.queue = { last: null, dispatch: null, }); const dispatch = (queue.dispatch = dispatchAction.bind( null, currentlyRenderingComponent, queue )); // $FlowFixMe[incompatible-use] found when upgrading Flow return [workInProgressHook.memoizedState, dispatch]; } } function useMemo(nextCreate, deps) { currentlyRenderingComponent = resolveCurrentlyRenderingComponent(); workInProgressHook = createWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; if (workInProgressHook !== null) { const prevState = workInProgressHook.memoizedState; if (prevState !== null) { if (nextDeps !== null) { const prevDeps = prevState[1]; if (areHookInputsEqual(nextDeps, prevDeps)) { return prevState[0]; } } } } const nextValue = nextCreate(); workInProgressHook.memoizedState = [nextValue, nextDeps]; return nextValue; } function useRef(initialValue) { currentlyRenderingComponent = resolveCurrentlyRenderingComponent(); workInProgressHook = createWorkInProgressHook(); const previousRef = workInProgressHook.memoizedState; if (previousRef === null) { const ref = { current: initialValue, }; workInProgressHook.memoizedState = ref; return ref; } else { return previousRef; } } function useLayoutEffect(create, inputs) {} function dispatchAction(componentIdentity, queue, action) { if (numberOfReRenders >= RE_RENDER_LIMIT) { throw new Error( 'Too many re-renders. React limits the number of renders to prevent ' + 'an infinite loop.' ); } if (componentIdentity === currentlyRenderingComponent) { // This is a render phase update. Stash it in a lazily-created map of // queue -> linked list of updates. After this render pass, we'll restart // and apply the stashed updates on top of the work-in-progress hook. didScheduleRenderPhaseUpdate = true; const update = { action, next: null, }; if (renderPhaseUpdates === null) { renderPhaseUpdates = new Map(); } const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue); if (firstRenderPhaseUpdate === undefined) { // $FlowFixMe[incompatible-use] found when upgrading Flow renderPhaseUpdates.set(queue, update); } else { // Append the update to the end of the list. let lastRenderPhaseUpdate = firstRenderPhaseUpdate; while (lastRenderPhaseUpdate.next !== null) { lastRenderPhaseUpdate = lastRenderPhaseUpdate.next; } lastRenderPhaseUpdate.next = update; } } } function useCallback(callback, deps) { return useMemo(() => callback, deps); } function throwOnUseEventCall() { throw new Error( "A function wrapped in useEvent can't be called during rendering." ); } function useEvent(callback) { // $FlowIgnore[incompatible-return] return throwOnUseEventCall; } // TODO Decide on how to implement this hook for server rendering. // If a mutation occurs during render, consider triggering a Suspense boundary // and falling back to client rendering. function useMutableSource(source, getSnapshot, subscribe) { resolveCurrentlyRenderingComponent(); return getSnapshot(source._source); } function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) { if (getServerSnapshot === undefined) { throw new Error( 'Missing getServerSnapshot, which is required for ' + 'server-rendered content. Will revert to client rendering.' ); } return getServerSnapshot(); } function useDeferredValue(value) { resolveCurrentlyRenderingComponent(); return value; } function unsupportedStartTransition() { throw new Error('startTransition cannot be called during server rendering.'); } function useTransition() { resolveCurrentlyRenderingComponent(); return [false, unsupportedStartTransition]; } function useId() { const task = currentlyRenderingTask; const treeId = getTreeId(task.treeContext); const responseState = currentResponseState; if (responseState === null) { throw new Error( 'Invalid hook call. Hooks can only be called inside of the body of a function component.' ); } const localId = localIdCounter++; return makeId(responseState, treeId, localId); } function use(usable) { if (usable !== null && typeof usable === 'object') { // $FlowFixMe[method-unbinding] if (typeof usable.then === 'function') { // This is a thenable. const thenable = usable; // Track the position of the thenable within this fiber. const index = thenableIndexCounter; thenableIndexCounter += 1; if (thenableState === null) { thenableState = createThenableState(); } return trackUsedThenable(thenableState, thenable, index); } else if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE ) { const context = usable; return readContext$1(context); } } // eslint-disable-next-line react-internal/safe-string-coercion throw new Error('An unsupported type was passed to use(): ' + String(usable)); } function unsupportedRefresh() { throw new Error('Cache cannot be refreshed during server rendering.'); } function useCacheRefresh() { return unsupportedRefresh; } function useMemoCache(size) { const data = new Array(size); for (let i = 0; i < size; i++) { data[i] = REACT_MEMO_CACHE_SENTINEL; } return data; } function noop$1() {} const HooksDispatcher = { readContext: readContext$1, useContext, useMemo, useReducer, useRef, useState, useInsertionEffect: noop$1, useLayoutEffect, useCallback, // useImperativeHandle is not run in the server environment useImperativeHandle: noop$1, // Effects are not run in the server environment. useEffect: noop$1, // Debugging effect useDebugValue: noop$1, useDeferredValue, useTransition, useId, // Subscriptions are not setup in a server environment. useMutableSource, useSyncExternalStore, }; { HooksDispatcher.useCacheRefresh = useCacheRefresh; } { HooksDispatcher.useEvent = useEvent; } { HooksDispatcher.useMemoCache = useMemoCache; } { HooksDispatcher.use = use; } let currentResponseState = null; function setCurrentResponseState(responseState) { currentResponseState = responseState; } function getCacheSignal() { throw new Error('Not implemented.'); } function getCacheForType(resourceType) { throw new Error('Not implemented.'); } const DefaultCacheDispatcher = { getCacheSignal, getCacheForType, }; const ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher; const ReactCurrentCache = ReactSharedInternals.ReactCurrentCache; const ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame; const PENDING = 0; const COMPLETED = 1; const FLUSHED = 2; const ABORTED = 3; const ERRORED = 4; const OPEN = 0; const CLOSING = 1; const CLOSED = 2; // This is a default heuristic for how to split up the HTML content into progressive // loading. Our goal is to be able to display additional new content about every 500ms. // Faster than that is unnecessary and should be throttled on the client. It also // adds unnecessary overhead to do more splits. We don't know if it's a higher or lower // end device but higher end suffer less from the overhead than lower end does from // not getting small enough pieces. We error on the side of low end. // We base this on low end 3G speeds which is about 500kbits per second. We assume // that there can be a reasonable drop off from max bandwidth which leaves you with // as little as 80%. We can receive half of that each 500ms - at best. In practice, // a little bandwidth is lost to processing and contention - e.g. CSS and images that // are downloaded along with the main content. So we estimate about half of that to be // the lower end throughput. In other words, we expect that you can at least show // about 12.5kb of content per 500ms. Not counting starting latency for the first // paint. // 500 * 1024 / 8 * .8 * 0.5 / 2 const DEFAULT_PROGRESSIVE_CHUNK_SIZE = 12800; function defaultErrorHandler(error) { console['error'](error); // Don't transform to our wrapper return null; } function noop$2() {} function createRequest( children, responseState, rootFormatContext, progressiveChunkSize, onError, onAllReady, onShellReady, onShellError, onFatalError ) { const pingedTasks = []; const abortSet = new Set(); const resources = createResources(); const request = { destination: null, responseState, progressiveChunkSize: progressiveChunkSize === undefined ? DEFAULT_PROGRESSIVE_CHUNK_SIZE : progressiveChunkSize, status: OPEN, fatalError: null, nextSegmentId: 0, allPendingTasks: 0, pendingRootTasks: 0, resources, completedRootSegment: null, abortableTasks: abortSet, pingedTasks: pingedTasks, clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], preamble: [], postamble: [], onError: onError === undefined ? defaultErrorHandler : onError, onAllReady: onAllReady === undefined ? noop$2 : onAllReady, onShellReady: onShellReady === undefined ? noop$2 : onShellReady, onShellError: onShellError === undefined ? noop$2 : onShellError, onFatalError: onFatalError === undefined ? noop$2 : onFatalError, }; // This segment represents the root fallback. const rootSegment = createPendingSegment( request, 0, null, rootFormatContext, // Root segments are never embedded in Text on either edge false, false ); // There is no parent so conceptually, we're unblocked to flush this segment. rootSegment.parentFlushed = true; const rootTask = createTask( request, null, children, null, rootSegment, abortSet, emptyContextObject, rootContextSnapshot, emptyTreeContext ); pingedTasks.push(rootTask); return request; } function pingTask(request, task) { const pingedTasks = request.pingedTasks; pingedTasks.push(task); if (pingedTasks.length === 1) { scheduleWork(() => performWork(request)); } } function createSuspenseBoundary(request, fallbackAbortableTasks) { return { id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID, rootSegmentID: -1, parentFlushed: false, pendingTasks: 0, forceClientRender: false, completedSegments: [], byteSize: 0, fallbackAbortableTasks, errorDigest: null, resources: createBoundaryResources(), }; } function createTask( request, thenableState, node, blockedBoundary, blockedSegment, abortSet, legacyContext, context, treeContext ) { request.allPendingTasks++; if (blockedBoundary === null) { request.pendingRootTasks++; } else { blockedBoundary.pendingTasks++; } const task = { node, ping: () => pingTask(request, task), blockedBoundary, blockedSegment, abortSet, legacyContext, context, treeContext, thenableState, }; abortSet.add(task); return task; } function createPendingSegment( request, index, boundary, formatContext, lastPushedText, textEmbedded ) { return { status: PENDING, id: -1, // lazily assigned later index, parentFlushed: false, chunks: [], children: [], formatContext, boundary, lastPushedText, textEmbedded, }; } // DEV-only global reference to the currently executing task function pushFunctionComponentStackInDEV(task, type) {} function popComponentStackInDEV(task) {} // stash the component stack of an unwinding error until it is processed function logRecoverableError(request, error) { // If this callback errors, we intentionally let that error bubble up to become a fatal error // so that someone fixes the error reporting instead of hiding it. const errorDigest = request.onError(error); if (errorDigest != null && typeof errorDigest !== 'string') { // eslint-disable-next-line react-internal/prod-error-codes throw new Error( 'onError returned something with a type other than "string". onError should return a string and may return null or undefined but must not return anything else. It received something of type "' + typeof errorDigest + '" instead' ); } return errorDigest; } function fatalError(request, error) { // This is called outside error handling code such as if the root errors outside // a suspense boundary or if the root suspense boundary's fallback errors. // It's also called if React itself or its host configs errors. const onShellError = request.onShellError; onShellError(error); const onFatalError = request.onFatalError; onFatalError(error); if (request.destination !== null) { request.status = CLOSED; closeWithError(request.destination, error); } else { request.status = CLOSING; request.fatalError = error; } } function renderSuspenseBoundary(request, task, props) { const parentBoundary = task.blockedBoundary; const parentSegment = task.blockedSegment; // Each time we enter a suspense boundary, we split out into a new segment for // the fallback so that we can later replace that segment with the content. // This also lets us split out the main content even if it doesn't suspend, // in case it ends up generating a large subtree of content. const fallback = props.fallback; const content = props.children; const fallbackAbortSet = new Set(); const newBoundary = createSuspenseBoundary(request, fallbackAbortSet); const insertionIndex = parentSegment.chunks.length; // The children of the boundary segment is actually the fallback. const boundarySegment = createPendingSegment( request, insertionIndex, newBoundary, parentSegment.formatContext, // boundaries never require text embedding at their edges because comment nodes bound them false, false ); parentSegment.children.push(boundarySegment); // The parentSegment has a child Segment at this index so we reset the lastPushedText marker on the parent parentSegment.lastPushedText = false; // This segment is the actual child content. We can start rendering that immediately. const contentRootSegment = createPendingSegment( request, 0, null, parentSegment.formatContext, // boundaries never require text embedding at their edges because comment nodes bound them false, false ); // We mark the root segment as having its parent flushed. It's not really flushed but there is // no parent segment so there's nothing to wait on. contentRootSegment.parentFlushed = true; // Currently this is running synchronously. We could instead schedule this to pingedTasks. // I suspect that there might be some efficiency benefits from not creating the suspended task // and instead just using the stack if possible. // TODO: Call this directly instead of messing with saving and restoring contexts. // We can reuse the current context and task to render the content immediately without // context switching. We just need to temporarily switch which boundary and which segment // we're writing to. If something suspends, it'll spawn new suspended task with that context. task.blockedBoundary = newBoundary; task.blockedSegment = contentRootSegment; { setCurrentlyRenderingBoundaryResourcesTarget( request.resources, newBoundary.resources ); } try { // We use the safe form because we don't handle suspending here. Only error handling. renderNode(request, task, content); pushSegmentFinale( contentRootSegment.chunks, request.responseState, contentRootSegment.lastPushedText, contentRootSegment.textEmbedded ); contentRootSegment.status = COMPLETED; if (enableFloat) { if (newBoundary.pendingTasks === 0) { hoistCompletedBoundaryResources(request, newBoundary); } } queueCompletedSegment(newBoundary, contentRootSegment); if (newBoundary.pendingTasks === 0) { // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. popComponentStackInDEV(task); return; } } catch (error) { contentRootSegment.status = ERRORED; newBoundary.forceClientRender = true; newBoundary.errorDigest = logRecoverableError(request, error); // We don't need to schedule any task because we know the parent has written yet. // We do need to fallthrough to create the fallback though. } finally { { setCurrentlyRenderingBoundaryResourcesTarget( request.resources, parentBoundary ? parentBoundary.resources : null ); } task.blockedBoundary = parentBoundary; task.blockedSegment = parentSegment; } // We create suspended task for the fallback because we don't want to actually work // on it yet in case we finish the main content, so we queue for later. const suspendedFallbackTask = createTask( request, null, fallback, parentBoundary, boundarySegment, fallbackAbortSet, task.legacyContext, task.context, task.treeContext ); // on preparing fallbacks if we don't have any more main content to task on. request.pingedTasks.push(suspendedFallbackTask); } function hoistCompletedBoundaryResources(request, completedBoundary) { if (request.completedRootSegment !== null || request.pendingRootTasks > 0) { // The Shell has not flushed yet. we can hoist Resources for this boundary // all the way to the Root. hoistResourcesToRoot(request.resources, completedBoundary.resources); } // We don't hoist if the root already flushed because late resources will be hoisted // as boundaries flush } function renderHostElement(request, task, type, props) { const segment = task.blockedSegment; const children = pushStartInstance( segment.chunks, request.preamble, type, props, request.responseState, segment.formatContext, segment.lastPushedText ); segment.lastPushedText = false; const prevContext = segment.formatContext; segment.formatContext = getChildFormatContext(prevContext, type, props); // We use the non-destructive form because if something suspends, we still // need to pop back up and finish this subtree of HTML. renderNode(request, task, children); // We expect that errors will fatal the whole task and that we don't need // the correct context. Therefore this is not in a finally. segment.formatContext = prevContext; pushEndInstance(segment.chunks, request.postamble, type); segment.lastPushedText = false; } function shouldConstruct(Component) { return Component.prototype && Component.prototype.isReactComponent; } function renderWithHooks( request, task, prevThenableState, Component, props, secondArg ) { const componentIdentity = {}; prepareToUseHooks(task, componentIdentity, prevThenableState); const result = Component(props, secondArg); return finishHooks(Component, props, result, secondArg); } function finishClassComponent(request, task, instance, Component, props) { const nextChildren = instance.render(); { const childContextTypes = Component.childContextTypes; if (childContextTypes !== null && childContextTypes !== undefined) { const previousContext = task.legacyContext; const mergedContext = processChildContext( instance, Component, previousContext, childContextTypes ); task.legacyContext = mergedContext; renderNodeDestructive(request, task, null, nextChildren); task.legacyContext = previousContext; return; } } renderNodeDestructive(request, task, null, nextChildren); } function renderClassComponent(request, task, Component, props) { const maskedContext = getMaskedContext(Component, task.legacyContext); const instance = constructClassInstance(Component, props, maskedContext); mountClassInstance(instance, Component, props, maskedContext); finishClassComponent(request, task, instance, Component); } // components for some reason. function renderIndeterminateComponent( request, task, prevThenableState, Component, props ) { let legacyContext; { legacyContext = getMaskedContext(Component, task.legacyContext); } const value = renderWithHooks( request, task, prevThenableState, Component, props, legacyContext ); const hasId = checkDidRenderIdHook(); if ( // Run these checks in production only if the flag is off. // Eventually we'll delete this branch altogether. typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined ) { mountClassInstance(value, Component, props, legacyContext); finishClassComponent(request, task, value, Component); } else { // the previous task every again, so we can use the destructive recursive form. if (hasId) { // This component materialized an id. We treat this as its own level, with // a single "child" slot. const prevTreeContext = task.treeContext; const totalChildren = 1; const index = 0; task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index); try { renderNodeDestructive(request, task, null, value); } finally { task.treeContext = prevTreeContext; } } else { renderNodeDestructive(request, task, null, value); } } } function resolveDefaultProps(Component, baseProps) { if (Component && Component.defaultProps) { // Resolve default props. Taken from ReactElement const props = assign({}, baseProps); const defaultProps = Component.defaultProps; for (const propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } return props; } return baseProps; } function renderForwardRef(request, task, prevThenableState, type, props, ref) { pushFunctionComponentStackInDEV(task, type.render); const children = renderWithHooks( request, task, prevThenableState, type.render, props, ref ); const hasId = checkDidRenderIdHook(); if (hasId) { // This component materialized an id. We treat this as its own level, with // a single "child" slot. const prevTreeContext = task.treeContext; const totalChildren = 1; const index = 0; task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index); try { renderNodeDestructive(request, task, null, children); } finally { task.treeContext = prevTreeContext; } } else { renderNodeDestructive(request, task, null, children); } } function renderMemo(request, task, prevThenableState, type, props, ref) { const innerType = type.type; const resolvedProps = resolveDefaultProps(innerType, props); renderElement( request, task, prevThenableState, innerType, resolvedProps, ref ); } function renderContextConsumer(request, task, context, props) { const render = props.children; const newValue = readContext(context); const newChildren = render(newValue); renderNodeDestructive(request, task, null, newChildren); } function renderContextProvider(request, task, type, props) { const context = type._context; const value = props.value; const children = props.children; task.context = pushProvider(context, value); renderNodeDestructive(request, task, null, children); task.context = popProvider(); } function renderLazyComponent( request, task, prevThenableState, lazyComponent, props, ref ) { const payload = lazyComponent._payload; const init = lazyComponent._init; const Component = init(payload); const resolvedProps = resolveDefaultProps(Component, props); renderElement( request, task, prevThenableState, Component, resolvedProps, ref ); } function renderOffscreen(request, task, props) { const mode = props.mode; if (mode === 'hidden'); else { // A visible Offscreen boundary is treated exactly like a fragment: a // pure indirection. renderNodeDestructive(request, task, null, props.children); } } function renderElement(request, task, prevThenableState, type, props, ref) { if (typeof type === 'function') { if (shouldConstruct(type)) { renderClassComponent(request, task, type, props); return; } else { renderIndeterminateComponent( request, task, prevThenableState, type, props ); return; } } if (typeof type === 'string') { renderHostElement(request, task, type, props); return; } switch (type) { // LegacyHidden acts the same as a fragment. This only works because we // currently assume that every instance of LegacyHidden is accompanied by a // host component wrapper. In the hidden mode, the host component is given a // `hidden` attribute, which ensures that the initial HTML is not visible. // To support the use of LegacyHidden as a true fragment, without an extra // DOM node, we would have to hide the initial HTML in some other way. // TODO: Delete in LegacyHidden. It's an unstable API only used in the // www build. As a migration step, we could add a special prop to Offscreen // that simulates the old behavior (no hiding, no change to effects). case REACT_LEGACY_HIDDEN_TYPE: case REACT_DEBUG_TRACING_MODE_TYPE: case REACT_STRICT_MODE_TYPE: case REACT_PROFILER_TYPE: case REACT_FRAGMENT_TYPE: { renderNodeDestructive(request, task, null, props.children); return; } case REACT_OFFSCREEN_TYPE: { renderOffscreen(request, task, props); return; } case REACT_SUSPENSE_LIST_TYPE: { renderNodeDestructive(request, task, null, props.children); return; } case REACT_SCOPE_TYPE: { throw new Error('ReactDOMServer does not yet support scope components.'); } // eslint-disable-next-line-no-fallthrough case REACT_SUSPENSE_TYPE: { { renderSuspenseBoundary(request, task, props); } return; } } if (typeof type === 'object' && type !== null) { switch (type.$$typeof) { case REACT_FORWARD_REF_TYPE: { renderForwardRef(request, task, prevThenableState, type, props, ref); return; } case REACT_MEMO_TYPE: { renderMemo(request, task, prevThenableState, type, props, ref); return; } case REACT_PROVIDER_TYPE: { renderContextProvider(request, task, type, props); return; } case REACT_CONTEXT_TYPE: { renderContextConsumer(request, task, type, props); return; } case REACT_LAZY_TYPE: { renderLazyComponent(request, task, prevThenableState, type, props); return; } } } let info = ''; throw new Error( 'Element type is invalid: expected a string (for built-in ' + 'components) or a class/function (for composite components) ' + ('but got: ' + (type == null ? type : typeof type) + '.' + info) ); } function renderNodeDestructive( request, task, // The thenable state reused from the previous attempt, if any. This is almost // always null, except when called by retryTask. prevThenableState, node ) { { return renderNodeDestructiveImpl(request, task, prevThenableState, node); } } // This function by it self renders a node and consumes the task by mutating it // to update the current execution state. function renderNodeDestructiveImpl(request, task, prevThenableState, node) { // Stash the node we're working on. We'll pick up from this task in case // something suspends. task.node = node; // Handle object types if (typeof node === 'object' && node !== null) { switch (node.$$typeof) { case REACT_ELEMENT_TYPE: { const element = node; const type = element.type; const props = element.props; const ref = element.ref; renderElement(request, task, prevThenableState, type, props, ref); return; } case REACT_PORTAL_TYPE: throw new Error( 'Portals are not currently supported by the server renderer. ' + 'Render them conditionally so that they only appear on the client render.' ); // eslint-disable-next-line-no-fallthrough case REACT_LAZY_TYPE: { const lazyNode = node; const payload = lazyNode._payload; const init = lazyNode._init; let resolvedNode; { resolvedNode = init(payload); } renderNodeDestructive(request, task, null, resolvedNode); return; } } if (isArray(node)) { renderChildrenArray(request, task, node); return; } const iteratorFn = getIteratorFn(node); if (iteratorFn) { const iterator = iteratorFn.call(node); if (iterator) { // We need to know how many total children are in this set, so that we // can allocate enough id slots to acommodate them. So we must exhaust // the iterator before we start recursively rendering the children. // TODO: This is not great but I think it's inherent to the id // generation algorithm. let step = iterator.next(); // If there are not entries, we need to push an empty so we start by checking that. if (!step.done) { const children = []; do { children.push(step.value); step = iterator.next(); } while (!step.done); renderChildrenArray(request, task, children); return; } return; } } // $FlowFixMe[method-unbinding] const childString = Object.prototype.toString.call(node); throw new Error( 'Objects are not valid as a React child (found: ' + (childString === '[object Object]' ? 'object with keys {' + Object.keys(node).join(', ') + '}' : childString) + '). ' + 'If you meant to render a collection of children, use an array ' + 'instead.' ); } if (typeof node === 'string') { const segment = task.blockedSegment; segment.lastPushedText = pushTextInstance( task.blockedSegment.chunks, node, request.responseState, segment.lastPushedText ); return; } if (typeof node === 'number') { const segment = task.blockedSegment; segment.lastPushedText = pushTextInstance( task.blockedSegment.chunks, '' + node, request.responseState, segment.lastPushedText ); return; } } function renderChildrenArray(request, task, children) { const totalChildren = children.length; for (let i = 0; i < totalChildren; i++) { const prevTreeContext = task.treeContext; task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i); try { // We need to use the non-destructive form so that we can safely pop back // up and render the sibling if something suspends. renderNode(request, task, children[i]); } finally { task.treeContext = prevTreeContext; } } } function spawnNewSuspendedTask(request, task, thenableState, x) { // Something suspended, we'll need to create a new segment and resolve it later. const segment = task.blockedSegment; const insertionIndex = segment.chunks.length; const newSegment = createPendingSegment( request, insertionIndex, null, segment.formatContext, // Adopt the parent segment's leading text embed segment.lastPushedText, // Assume we are text embedded at the trailing edge true ); segment.children.push(newSegment); // Reset lastPushedText for current Segment since the new Segment "consumed" it segment.lastPushedText = false; const newTask = createTask( request, thenableState, task.node, task.blockedBoundary, newSegment, task.abortSet, task.legacyContext, task.context, task.treeContext ); const ping = newTask.ping; x.then(ping, ping); } // This is a non-destructive form of rendering a node. If it suspends it spawns // a new task and restores the context of this task to what it was before. function renderNode(request, task, node) { // TODO: Store segment.children.length here and reset it in case something // suspended partially through writing something. // Snapshot the current context in case something throws to interrupt the // process. const previousFormatContext = task.blockedSegment.formatContext; const previousLegacyContext = task.legacyContext; const previousContext = task.context; try { return renderNodeDestructive(request, task, null, node); } catch (x) { resetHooksState(); if (typeof x === 'object' && x !== null && typeof x.then === 'function') { const thenableState = getThenableStateAfterSuspending(); spawnNewSuspendedTask(request, task, thenableState, x); // Restore the context. We assume that this will be restored by the inner // functions in case nothing throws so we don't use "finally" here. task.blockedSegment.formatContext = previousFormatContext; task.legacyContext = previousLegacyContext; task.context = previousContext; // Restore all active ReactContexts to what they were before. switchContext(previousContext); return; } else { // Restore the context. We assume that this will be restored by the inner // functions in case nothing throws so we don't use "finally" here. task.blockedSegment.formatContext = previousFormatContext; task.legacyContext = previousLegacyContext; task.context = previousContext; // Restore all active ReactContexts to what they were before. switchContext(previousContext); // Let's terminate the rest of the tree and don't render any siblings. throw x; } } } function erroredTask(request, boundary, segment, error) { // Report the error to a global handler. const errorDigest = logRecoverableError(request, error); if (boundary === null) { fatalError(request, error); } else { boundary.pendingTasks--; if (!boundary.forceClientRender) { boundary.forceClientRender = true; boundary.errorDigest = errorDigest; // so we can flush it, if the parent already flushed. if (boundary.parentFlushed) { // We don't have a preference where in the queue this goes since it's likely // to error on the client anyway. However, intentionally client-rendered // boundaries should be flushed earlier so that they can start on the client. // We reuse the same queue for errors. request.clientRenderedBoundaries.push(boundary); } } } request.allPendingTasks--; if (request.allPendingTasks === 0) { const onAllReady = request.onAllReady; onAllReady(); } } function abortTaskSoft(task) { // This aborts task without aborting the parent boundary that it blocks. // It's used for when we didn't need this task to complete the tree. // If task was needed, then it should use abortTask instead. const request = this; const boundary = task.blockedBoundary; const segment = task.blockedSegment; segment.status = ABORTED; finishedTask(request, boundary, segment); } function abortTask(task, request, error) { // This aborts the task and aborts the parent that it blocks, putting it into // client rendered mode. const boundary = task.blockedBoundary; const segment = task.blockedSegment; segment.status = ABORTED; if (boundary === null) { request.allPendingTasks--; // We didn't complete the root so we have nothing to show. We can close // the request; if (request.status !== CLOSING && request.status !== CLOSED) { logRecoverableError(request, error); fatalError(request, error); } } else { boundary.pendingTasks--; if (!boundary.forceClientRender) { boundary.forceClientRender = true; boundary.errorDigest = request.onError(error); if (boundary.parentFlushed) { request.clientRenderedBoundaries.push(boundary); } } // If this boundary was still pending then we haven't already cancelled its fallbacks. // We'll need to abort the fallbacks, which will also error that parent boundary. boundary.fallbackAbortableTasks.forEach((fallbackTask) => abortTask(fallbackTask, request, error) ); boundary.fallbackAbortableTasks.clear(); request.allPendingTasks--; if (request.allPendingTasks === 0) { const onAllReady = request.onAllReady; onAllReady(); } } } function queueCompletedSegment(boundary, segment) { if ( segment.chunks.length === 0 && segment.children.length === 1 && segment.children[0].boundary === null ) { // This is an empty segment. There's nothing to write, so we can instead transfer the ID // to the child. That way any existing references point to the child. const childSegment = segment.children[0]; childSegment.id = segment.id; childSegment.parentFlushed = true; if (childSegment.status === COMPLETED) { queueCompletedSegment(boundary, childSegment); } } else { const completedSegments = boundary.completedSegments; completedSegments.push(segment); } } function finishedTask(request, boundary, segment) { if (boundary === null) { if (segment.parentFlushed) { if (request.completedRootSegment !== null) { throw new Error( 'There can only be one root segment. This is a bug in React.' ); } request.completedRootSegment = segment; } request.pendingRootTasks--; if (request.pendingRootTasks === 0) { // We have completed the shell so the shell can't error anymore. request.onShellError = noop$2; const onShellReady = request.onShellReady; onShellReady(); } } else { boundary.pendingTasks--; if (boundary.forceClientRender); else if (boundary.pendingTasks === 0) { // This must have been the last segment we were waiting on. This boundary is now complete. if (segment.parentFlushed) { // Our parent segment already flushed, so we need to schedule this segment to be emitted. // If it is a segment that was aborted, we'll write other content instead so we don't need // to emit it. if (segment.status === COMPLETED) { queueCompletedSegment(boundary, segment); } } { hoistCompletedBoundaryResources(request, boundary); } if (boundary.parentFlushed) { // The segment might be part of a segment that didn't flush yet, but if the boundary's // parent flushed, we need to schedule the boundary to be emitted. request.completedBoundaries.push(boundary); } // We can now cancel any pending task on the fallback since we won't need to show it anymore. // This needs to happen after we read the parentFlushed flags because aborting can finish // work which can trigger user code, which can start flushing, which can change those flags. boundary.fallbackAbortableTasks.forEach(abortTaskSoft, request); boundary.fallbackAbortableTasks.clear(); } else { if (segment.parentFlushed) { // Our parent already flushed, so we need to schedule this segment to be emitted. // If it is a segment that was aborted, we'll write other content instead so we don't need // to emit it. if (segment.status === COMPLETED) { queueCompletedSegment(boundary, segment); const completedSegments = boundary.completedSegments; if (completedSegments.length === 1) { // This is the first time since we last flushed that we completed anything. // We can schedule this boundary to emit its partially completed segments early // in case the parent has already been flushed. if (boundary.parentFlushed) { request.partialBoundaries.push(boundary); } } } } } } request.allPendingTasks--; if (request.allPendingTasks === 0) { // This needs to be called at the very end so that we can synchronously write the result // in the callback if needed. const onAllReady = request.onAllReady; onAllReady(); } } function retryTask(request, task) { { const blockedBoundary = task.blockedBoundary; setCurrentlyRenderingBoundaryResourcesTarget( request.resources, blockedBoundary ? blockedBoundary.resources : null ); } const segment = task.blockedSegment; if (segment.status !== PENDING) { // We completed this by other means before we had a chance to retry it. return; } // We restore the context to what it was when we suspended. // We don't restore it after we leave because it's likely that we'll end up // needing a very similar context soon again. switchContext(task.context); try { // We call the destructive form that mutates this task. That way if something // suspends again, we can reuse the same task instead of spawning a new one. // Reset the task's thenable state before continuing, so that if a later // component suspends we can reuse the same task object. If the same // component suspends again, the thenable state will be restored. const prevThenableState = task.thenableState; task.thenableState = null; renderNodeDestructive(request, task, prevThenableState, task.node); pushSegmentFinale( segment.chunks, request.responseState, segment.lastPushedText, segment.textEmbedded ); task.abortSet.delete(task); segment.status = COMPLETED; finishedTask(request, task.blockedBoundary, segment); } catch (x) { resetHooksState(); if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended again, let's pick it back up later. const ping = task.ping; x.then(ping, ping); task.thenableState = getThenableStateAfterSuspending(); } else { task.abortSet.delete(task); segment.status = ERRORED; erroredTask(request, task.blockedBoundary, segment, x); } } finally { { setCurrentlyRenderingBoundaryResourcesTarget(request.resources, null); } } } function performWork(request) { if (request.status === CLOSED) { return; } const prevContext = getActiveContext(); const prevDispatcher = ReactCurrentDispatcher$1.current; ReactCurrentDispatcher$1.current = HooksDispatcher; let prevCacheDispatcher; { prevCacheDispatcher = ReactCurrentCache.current; ReactCurrentCache.current = DefaultCacheDispatcher; } const previousHostDispatcher = prepareToRender(request.resources); const prevResponseState = currentResponseState; setCurrentResponseState(request.responseState); try { const pingedTasks = request.pingedTasks; let i; for (i = 0; i < pingedTasks.length; i++) { const task = pingedTasks[i]; retryTask(request, task); } pingedTasks.splice(0, i); if (request.destination !== null) { flushCompletedQueues(request, request.destination); } } catch (error) { logRecoverableError(request, error); fatalError(request, error); } finally { setCurrentResponseState(prevResponseState); ReactCurrentDispatcher$1.current = prevDispatcher; { ReactCurrentCache.current = prevCacheDispatcher; } cleanupAfterRender(previousHostDispatcher); if (prevDispatcher === HooksDispatcher) { // This means that we were in a reentrant work loop. This could happen // in a renderer that supports synchronous work like renderToString, // when it's called from within another renderer. // Normally we don't bother switching the contexts to their root/default // values when leaving because we'll likely need the same or similar // context again. However, when we're inside a synchronous loop like this // we'll to restore the context to what it was before returning. switchContext(prevContext); } } } function flushSubtree(request, destination, segment) { segment.parentFlushed = true; switch (segment.status) { case PENDING: { // We're emitting a placeholder for this segment to be filled in later. // Therefore we'll need to assign it an ID - to refer to it by. const segmentID = (segment.id = request.nextSegmentId++); // When this segment finally completes it won't be embedded in text since it will flush separately segment.lastPushedText = false; segment.textEmbedded = false; return writePlaceholder(destination, request.responseState, segmentID); } case COMPLETED: { segment.status = FLUSHED; let r = true; const chunks = segment.chunks; let chunkIdx = 0; const children = segment.children; for (let childIdx = 0; childIdx < children.length; childIdx++) { const nextChild = children[childIdx]; // Write all the chunks up until the next child. for (; chunkIdx < nextChild.index; chunkIdx++) { writeChunk(destination, chunks[chunkIdx]); } r = flushSegment(request, destination, nextChild); } // Finally just write all the remaining chunks for (; chunkIdx < chunks.length - 1; chunkIdx++) { writeChunk(destination, chunks[chunkIdx]); } if (chunkIdx < chunks.length) { r = writeChunkAndReturn(destination, chunks[chunkIdx]); } return r; } default: { throw new Error( 'Aborted, errored or already flushed boundaries should not be flushed again. This is a bug in React.' ); } } } function flushSegment(request, destination, segment) { const boundary = segment.boundary; if (boundary === null) { // Not a suspense boundary. return flushSubtree(request, destination, segment); } boundary.parentFlushed = true; // This segment is a Suspense boundary. We need to decide whether to // emit the content or the fallback now. if (boundary.forceClientRender) { // Emit a client rendered suspense boundary wrapper. // We never queue the inner boundary so we'll never emit its content or partial segments. writeStartClientRenderedSuspenseBoundary( destination, request.responseState, boundary.errorDigest, boundary.errorMessage, boundary.errorComponentStack ); // Flush the fallback. flushSubtree(request, destination, segment); return writeEndClientRenderedSuspenseBoundary( destination, request.responseState ); } else if (boundary.pendingTasks > 0) { // This boundary is still loading. Emit a pending suspense boundary wrapper. // Assign an ID to refer to the future content by. boundary.rootSegmentID = request.nextSegmentId++; if (boundary.completedSegments.length > 0) { // If this is at least partially complete, we can queue it to be partially emitted early. request.partialBoundaries.push(boundary); } /// This is the first time we should have referenced this ID. const id = (boundary.id = assignSuspenseBoundaryID(request.responseState)); writeStartPendingSuspenseBoundary(destination, request.responseState, id); // Flush the fallback. flushSubtree(request, destination, segment); return writeEndPendingSuspenseBoundary(destination, request.responseState); } else if (boundary.byteSize > request.progressiveChunkSize) { // This boundary is large and will be emitted separately so that we can progressively show // other content. We add it to the queue during the flush because we have to ensure that // the parent flushes first so that there's something to inject it into. // We also have to make sure that it's emitted into the queue in a deterministic slot. // I.e. we can't insert it here when it completes. // Assign an ID to refer to the future content by. boundary.rootSegmentID = request.nextSegmentId++; request.completedBoundaries.push(boundary); // Emit a pending rendered suspense boundary wrapper. writeStartPendingSuspenseBoundary( destination, request.responseState, boundary.id ); // Flush the fallback. flushSubtree(request, destination, segment); return writeEndPendingSuspenseBoundary(destination, request.responseState); } else { { hoistResources(request.resources, boundary.resources); } // We can inline this boundary's content as a complete boundary. writeStartCompletedSuspenseBoundary(destination, request.responseState); const completedSegments = boundary.completedSegments; if (completedSegments.length !== 1) { throw new Error( 'A previously unvisited boundary must have exactly one root segment. This is a bug in React.' ); } const contentSegment = completedSegments[0]; flushSegment(request, destination, contentSegment); return writeEndCompletedSuspenseBoundary( destination, request.responseState ); } } function flushInitialResources(destination, resources, responseState) { writeInitialResources(destination, resources, responseState); } function flushImmediateResources(destination, request) { writeImmediateResources( destination, request.resources, request.responseState ); } function flushClientRenderedBoundary(request, destination, boundary) { return writeClientRenderBoundaryInstruction( destination, request.responseState, boundary.id, boundary.errorDigest, boundary.errorMessage, boundary.errorComponentStack ); } function flushSegmentContainer(request, destination, segment) { writeStartSegment( destination, request.responseState, segment.formatContext, segment.id ); flushSegment(request, destination, segment); return writeEndSegment(destination, segment.formatContext); } function flushCompletedBoundary(request, destination, boundary) { { setCurrentlyRenderingBoundaryResourcesTarget( request.resources, boundary.resources ); } const completedSegments = boundary.completedSegments; let i = 0; for (; i < completedSegments.length; i++) { const segment = completedSegments[i]; flushPartiallyCompletedSegment(request, destination, boundary, segment); } completedSegments.length = 0; return writeCompletedBoundaryInstruction( destination, request.responseState, boundary.id, boundary.rootSegmentID, boundary.resources ); } function flushPartialBoundary(request, destination, boundary) { { setCurrentlyRenderingBoundaryResourcesTarget( request.resources, boundary.resources ); } const completedSegments = boundary.completedSegments; let i = 0; for (; i < completedSegments.length; i++) { const segment = completedSegments[i]; if ( !flushPartiallyCompletedSegment(request, destination, boundary, segment) ) { i++; completedSegments.splice(0, i); // Only write as much as the buffer wants. Something higher priority // might want to write later. return false; } } completedSegments.splice(0, i); return true; } function flushPartiallyCompletedSegment( request, destination, boundary, segment ) { if (segment.status === FLUSHED) { // We've already flushed this inline. return true; } const segmentID = segment.id; if (segmentID === -1) { // This segment wasn't previously referred to. This happens at the root of // a boundary. We make kind of a leap here and assume this is the root. const rootSegmentID = (segment.id = boundary.rootSegmentID); if (rootSegmentID === -1) { throw new Error( 'A root segment ID must have been assigned by now. This is a bug in React.' ); } return flushSegmentContainer(request, destination, segment); } else { flushSegmentContainer(request, destination, segment); return writeCompletedSegmentInstruction( destination, request.responseState, segmentID ); } } function flushCompletedQueues(request, destination) { try { // The structure of this is to go through each queue one by one and write // until the sink tells us to stop. When we should stop, we still finish writing // that item fully and then yield. At that point we remove the already completed // items up until the point we completed them. let i; const completedRootSegment = request.completedRootSegment; if (completedRootSegment !== null) { if (request.pendingRootTasks === 0) { if (enableFloat) { const preamble = request.preamble; for (i = 0; i < preamble.length; i++) { // we expect the preamble to be tiny and will ignore backpressure writeChunk(destination, preamble[i]); } flushInitialResources( destination, request.resources, request.responseState ); } flushSegment(request, destination, completedRootSegment); request.completedRootSegment = null; writeCompletedRoot(destination, request.responseState); } else { // We haven't flushed the root yet so we don't need to check any other branches further down return; } } else if (enableFloat) { flushImmediateResources(destination, request); } // We emit client rendering instructions for already emitted boundaries first. // This is so that we can signal to the client to start client rendering them as // soon as possible. const clientRenderedBoundaries = request.clientRenderedBoundaries; for (i = 0; i < clientRenderedBoundaries.length; i++) { const boundary = clientRenderedBoundaries[i]; if (!flushClientRenderedBoundary(request, destination, boundary)) { request.destination = null; i++; clientRenderedBoundaries.splice(0, i); return; } } clientRenderedBoundaries.splice(0, i); // Next we emit any complete boundaries. It's better to favor boundaries // that are completely done since we can actually show them, than it is to emit // any individual segments from a partially complete boundary. const completedBoundaries = request.completedBoundaries; for (i = 0; i < completedBoundaries.length; i++) { const boundary = completedBoundaries[i]; if (!flushCompletedBoundary(request, destination, boundary)) { request.destination = null; i++; completedBoundaries.splice(0, i); return; } } completedBoundaries.splice(0, i); // Allow anything written so far to flush to the underlying sink before // we continue with lower priorities. completeWriting(destination); beginWriting(destination); // TODO: Here we'll emit data used by hydration. // Next we emit any segments of any boundaries that are partially complete // but not deeply complete. const partialBoundaries = request.partialBoundaries; for (i = 0; i < partialBoundaries.length; i++) { const boundary = partialBoundaries[i]; if (!flushPartialBoundary(request, destination, boundary)) { request.destination = null; i++; partialBoundaries.splice(0, i); return; } } partialBoundaries.splice(0, i); // Next we check the completed boundaries again. This may have had // boundaries added to it in case they were too larged to be inlined. // New ones might be added in this loop. const largeBoundaries = request.completedBoundaries; for (i = 0; i < largeBoundaries.length; i++) { const boundary = largeBoundaries[i]; if (!flushCompletedBoundary(request, destination, boundary)) { request.destination = null; i++; largeBoundaries.splice(0, i); return; } } largeBoundaries.splice(0, i); } finally { if ( request.allPendingTasks === 0 && request.pingedTasks.length === 0 && request.clientRenderedBoundaries.length === 0 && request.completedBoundaries.length === 0 // We don't need to check any partially completed segments because // either they have pending task or they're complete. ) { { const postamble = request.postamble; for (let i = 0; i < postamble.length; i++) { writeChunk(destination, postamble[i]); } } close(destination); } } } function startWork(request) { scheduleWork(() => performWork(request)); } function startFlowing(request, destination) { if (request.status === CLOSING) { request.status = CLOSED; closeWithError(destination, request.fatalError); return; } if (request.status === CLOSED) { return; } if (request.destination !== null) { // We're already flowing. return; } request.destination = destination; try { flushCompletedQueues(request, destination); } catch (error) { logRecoverableError(request, error); fatalError(request, error); } } // This is called to early terminate a request. It puts all pending boundaries in client rendered state. function abort(request, reason) { try { const abortableTasks = request.abortableTasks; if (abortableTasks.size > 0) { const error = reason === undefined ? new Error('The render was aborted by the server without a reason.') : reason; abortableTasks.forEach((task) => abortTask(task, request, error)); abortableTasks.clear(); } if (request.destination !== null) { flushCompletedQueues(request, request.destination); } } catch (error) { logRecoverableError(request, error); fatalError(request, error); } } function renderToReadableStream(children, options) { return new Promise((resolve, reject) => { let onFatalError; let onAllReady; const allReady = new Promise((res, rej) => { onAllReady = res; onFatalError = rej; }); function onShellReady() { const stream = new ReadableStream( { type: 'direct', pull: (controller) => { // $FlowIgnore startFlowing(request, controller); }, cancel: (reason) => { abort(request); }, }, // $FlowFixMe size() methods are not allowed on byte streams. { highWaterMark: 2048, } ); // TODO: Move to sub-classing ReadableStream. stream.allReady = allReady; resolve(stream); } function onShellError(error) { // If the shell errors the caller of `renderToReadableStream` won't have access to `allReady`. // However, `allReady` will be rejected by `onFatalError` as well. // So we need to catch the duplicate, uncatchable fatal error in `allReady` to prevent a `UnhandledPromiseRejection`. allReady.catch(() => {}); reject(error); } const request = createRequest( children, createResponseState( options ? options.identifierPrefix : undefined, options ? options.nonce : undefined, options ? options.bootstrapScriptContent : undefined, options ? options.bootstrapScripts : undefined, options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined ), createRootFormatContext(options ? options.namespaceURI : undefined), options ? options.progressiveChunkSize : undefined, options ? options.onError : undefined, onAllReady, onShellReady, onShellError, onFatalError ); if (options && options.signal) { const signal = options.signal; if (signal.aborted) { abort(request, signal.reason); } else { const listener = () => { abort(request, signal.reason); signal.removeEventListener('abort', listener); }; signal.addEventListener('abort', listener); } } startWork(request); }); } export {renderToReadableStream, ReactVersion as version};