diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js b/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js index 7007a520b88db..39d47a369ecc6 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js +++ b/packages/react-dom-bindings/src/client/ReactDOMComponentTree.js @@ -34,7 +34,7 @@ import { import {getParentSuspenseInstance} from './ReactFiberConfigDOM'; -import {enableScopeAPI, enableFloat} from 'shared/ReactFeatureFlags'; +import {enableScopeAPI} from 'shared/ReactFeatureFlags'; const randomKey = Math.random().toString(36).slice(2); const internalInstanceKey = '__reactFiber$' + randomKey; @@ -175,7 +175,7 @@ export function getInstanceFromNode(node: Node): Fiber | null { tag === HostComponent || tag === HostText || tag === SuspenseComponent || - (enableFloat ? tag === HostHoistable : false) || + tag === HostHoistable || tag === HostSingleton || tag === HostRoot ) { @@ -195,7 +195,7 @@ export function getNodeFromInstance(inst: Fiber): Instance | TextInstance { const tag = inst.tag; if ( tag === HostComponent || - (enableFloat ? tag === HostHoistable : false) || + tag === HostHoistable || tag === HostSingleton || tag === HostText ) { diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index b3293ea83bd8a..213d80c35087d 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -91,7 +91,6 @@ import { enableBigIntSupport, enableCreateEventHandleAPI, enableScopeAPI, - enableFloat, enableTrustedTypesIntegration, enableFormActions, enableAsyncActions, @@ -2161,25 +2160,16 @@ function preconnectAs( } function prefetchDNS(href: string) { - if (!enableFloat) { - return; - } previousDispatcher.prefetchDNS(href); preconnectAs('dns-prefetch', href, null); } function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) { - if (!enableFloat) { - return; - } previousDispatcher.preconnect(href, crossOrigin); preconnectAs('preconnect', href, crossOrigin); } function preload(href: string, as: string, options?: ?PreloadImplOptions) { - if (!enableFloat) { - return; - } previousDispatcher.preload(href, as, options); const ownerDocument = getGlobalDocument(); if (ownerDocument && href && as) { @@ -2258,9 +2248,6 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) { } function preloadModule(href: string, options?: ?PreloadModuleImplOptions) { - if (!enableFloat) { - return; - } previousDispatcher.preloadModule(href, options); const ownerDocument = getGlobalDocument(); if (ownerDocument && href) { @@ -2322,9 +2309,6 @@ function preinitStyle( precedence: ?string, options?: ?PreinitStyleOptions, ) { - if (!enableFloat) { - return; - } previousDispatcher.preinitStyle(href, precedence, options); const ownerDocument = getGlobalDocument(); @@ -2399,9 +2383,6 @@ function preinitStyle( } function preinitScript(src: string, options?: ?PreinitScriptOptions) { - if (!enableFloat) { - return; - } previousDispatcher.preinitScript(src, options); const ownerDocument = getGlobalDocument(); @@ -2458,9 +2439,6 @@ function preinitModuleScript( src: string, options?: ?PreinitModuleScriptOptions, ) { - if (!enableFloat) { - return; - } previousDispatcher.preinitModuleScript(src, options); const ownerDocument = getGlobalDocument(); diff --git a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js index 5bb1fdc91150e..6386168268cd7 100644 --- a/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js @@ -52,7 +52,6 @@ import { enableLegacyFBSupport, enableCreateEventHandleAPI, enableScopeAPI, - enableFloat, enableFormActions, } from 'shared/ReactFeatureFlags'; import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener'; @@ -647,7 +646,7 @@ export function dispatchEventForPluginEventSystem( if ( parentTag === HostComponent || parentTag === HostText || - (enableFloat ? parentTag === HostHoistable : false) || + parentTag === HostHoistable || parentTag === HostSingleton ) { node = ancestorInst = parentNode; @@ -705,7 +704,7 @@ export function accumulateSinglePhaseListeners( // Handle listeners that are on HostComponents (i.e.
) if ( (tag === HostComponent || - (enableFloat ? tag === HostHoistable : false) || + tag === HostHoistable || tag === HostSingleton) && stateNode !== null ) { @@ -819,7 +818,7 @@ export function accumulateTwoPhaseListeners( // Handle listeners that are on HostComponents (i.e.
) if ( (tag === HostComponent || - (enableFloat ? tag === HostHoistable : false) || + tag === HostHoistable || tag === HostSingleton) && stateNode !== null ) { @@ -922,7 +921,7 @@ function accumulateEnterLeaveListenersForEvent( } if ( (tag === HostComponent || - (enableFloat ? tag === HostHoistable : false) || + tag === HostHoistable || tag === HostSingleton) && stateNode !== null ) { diff --git a/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js b/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js index 5cd5dbfb6dbbd..64efccddedefa 100644 --- a/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js +++ b/packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js @@ -16,8 +16,6 @@ import type { PreinitModuleScriptOptions, } from 'react-dom/src/shared/ReactDOMTypes'; -import {enableFloat} from 'shared/ReactFeatureFlags'; - import { emitHint, getHints, @@ -40,107 +38,99 @@ ReactDOMCurrentDispatcher.current = { }; function prefetchDNS(href: string) { - if (enableFloat) { - if (typeof href === 'string' && href) { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); - const key = 'D|' + href; - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); - emitHint(request, 'D', href); - } else { - previousDispatcher.prefetchDNS(href); + if (typeof href === 'string' && href) { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); + const key = 'D|' + href; + if (hints.has(key)) { + // duplicate hint + return; } + hints.add(key); + emitHint(request, 'D', href); + } else { + previousDispatcher.prefetchDNS(href); } } } function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) { - if (enableFloat) { - if (typeof href === 'string') { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); + if (typeof href === 'string') { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); - const key = `C|${crossOrigin == null ? 'null' : crossOrigin}|${href}`; - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); - if (typeof crossOrigin === 'string') { - emitHint(request, 'C', [href, crossOrigin]); - } else { - emitHint(request, 'C', href); - } + const key = `C|${crossOrigin == null ? 'null' : crossOrigin}|${href}`; + if (hints.has(key)) { + // duplicate hint + return; + } + hints.add(key); + if (typeof crossOrigin === 'string') { + emitHint(request, 'C', [href, crossOrigin]); } else { - previousDispatcher.preconnect(href, crossOrigin); + emitHint(request, 'C', href); } + } else { + previousDispatcher.preconnect(href, crossOrigin); } } } function preload(href: string, as: string, options?: ?PreloadImplOptions) { - if (enableFloat) { - if (typeof href === 'string') { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); - let key = 'L'; - if (as === 'image' && options) { - key += getImagePreloadKey( - href, - options.imageSrcSet, - options.imageSizes, - ); - } else { - key += `[${as}]${href}`; - } - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); + if (typeof href === 'string') { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); + let key = 'L'; + if (as === 'image' && options) { + key += getImagePreloadKey( + href, + options.imageSrcSet, + options.imageSizes, + ); + } else { + key += `[${as}]${href}`; + } + if (hints.has(key)) { + // duplicate hint + return; + } + hints.add(key); - const trimmed = trimOptions(options); - if (trimmed) { - emitHint(request, 'L', [href, as, trimmed]); - } else { - emitHint(request, 'L', [href, as]); - } + const trimmed = trimOptions(options); + if (trimmed) { + emitHint(request, 'L', [href, as, trimmed]); } else { - previousDispatcher.preload(href, as, options); + emitHint(request, 'L', [href, as]); } + } else { + previousDispatcher.preload(href, as, options); } } } function preloadModule(href: string, options?: ?PreloadModuleImplOptions) { - if (enableFloat) { - if (typeof href === 'string') { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); - const key = 'm|' + href; - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); + if (typeof href === 'string') { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); + const key = 'm|' + href; + if (hints.has(key)) { + // duplicate hint + return; + } + hints.add(key); - const trimmed = trimOptions(options); - if (trimmed) { - return emitHint(request, 'm', [href, trimmed]); - } else { - return emitHint(request, 'm', href); - } + const trimmed = trimOptions(options); + if (trimmed) { + return emitHint(request, 'm', [href, trimmed]); } else { - previousDispatcher.preloadModule(href, options); + return emitHint(request, 'm', href); } + } else { + previousDispatcher.preloadModule(href, options); } } } @@ -150,59 +140,55 @@ function preinitStyle( precedence: ?string, options?: ?PreinitStyleOptions, ) { - if (enableFloat) { - if (typeof href === 'string') { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); - const key = 'S|' + href; - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); + if (typeof href === 'string') { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); + const key = 'S|' + href; + if (hints.has(key)) { + // duplicate hint + return; + } + hints.add(key); - const trimmed = trimOptions(options); - if (trimmed) { - return emitHint(request, 'S', [ - href, - typeof precedence === 'string' ? precedence : 0, - trimmed, - ]); - } else if (typeof precedence === 'string') { - return emitHint(request, 'S', [href, precedence]); - } else { - return emitHint(request, 'S', href); - } + const trimmed = trimOptions(options); + if (trimmed) { + return emitHint(request, 'S', [ + href, + typeof precedence === 'string' ? precedence : 0, + trimmed, + ]); + } else if (typeof precedence === 'string') { + return emitHint(request, 'S', [href, precedence]); } else { - previousDispatcher.preinitStyle(href, precedence, options); + return emitHint(request, 'S', href); } + } else { + previousDispatcher.preinitStyle(href, precedence, options); } } } function preinitScript(src: string, options?: ?PreinitScriptOptions) { - if (enableFloat) { - if (typeof src === 'string') { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); - const key = 'X|' + src; - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); + if (typeof src === 'string') { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); + const key = 'X|' + src; + if (hints.has(key)) { + // duplicate hint + return; + } + hints.add(key); - const trimmed = trimOptions(options); - if (trimmed) { - return emitHint(request, 'X', [src, trimmed]); - } else { - return emitHint(request, 'X', src); - } + const trimmed = trimOptions(options); + if (trimmed) { + return emitHint(request, 'X', [src, trimmed]); } else { - previousDispatcher.preinitScript(src, options); + return emitHint(request, 'X', src); } + } else { + previousDispatcher.preinitScript(src, options); } } } @@ -211,27 +197,25 @@ function preinitModuleScript( src: string, options?: ?PreinitModuleScriptOptions, ) { - if (enableFloat) { - if (typeof src === 'string') { - const request = resolveRequest(); - if (request) { - const hints = getHints(request); - const key = 'M|' + src; - if (hints.has(key)) { - // duplicate hint - return; - } - hints.add(key); + if (typeof src === 'string') { + const request = resolveRequest(); + if (request) { + const hints = getHints(request); + const key = 'M|' + src; + if (hints.has(key)) { + // duplicate hint + return; + } + hints.add(key); - const trimmed = trimOptions(options); - if (trimmed) { - return emitHint(request, 'M', [src, trimmed]); - } else { - return emitHint(request, 'M', src); - } + const trimmed = trimOptions(options); + if (trimmed) { + return emitHint(request, 'M', [src, trimmed]); } else { - previousDispatcher.preinitModuleScript(src, options); + return emitHint(request, 'M', src); } + } else { + previousDispatcher.preinitModuleScript(src, options); } } } diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 17442ee73e785..6839fbd1cff9d 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -31,7 +31,6 @@ import { enableBigIntSupport, enableFilterEmptyStringAttributesDOM, enableCustomElementPropertySupport, - enableFloat, enableFormActions, enableFizzExternalRuntime, enableNewBooleanProps, @@ -383,11 +382,6 @@ export function createRenderState( ); } if (enableFizzExternalRuntime) { - if (!enableFloat) { - throw new Error( - 'enableFizzExternalRuntime without enableFloat is not supported. This should never appear in production, since it means you are using a misconfigured React bundle.', - ); - } if (externalRuntimeConfig !== undefined) { if (typeof externalRuntimeConfig === 'string') { externalRuntimeScript = { @@ -2333,44 +2327,40 @@ function pushMeta( noscriptTagInScope: boolean, isFallback: boolean, ): null { - if (enableFloat) { - if ( - insertionMode === SVG_MODE || - noscriptTagInScope || - props.itemProp != null - ) { - return pushSelfClosing(target, props, 'meta'); - } else { - if (textEmbedded) { - // This link follows text but we aren't writing a tag. while not as efficient as possible we need - // to be safe and assume text will follow by inserting a textSeparator - target.push(textSeparator); - } + if ( + insertionMode === SVG_MODE || + noscriptTagInScope || + props.itemProp != null + ) { + return pushSelfClosing(target, props, 'meta'); + } else { + if (textEmbedded) { + // This link follows text but we aren't writing a tag. while not as efficient as possible we need + // to be safe and assume text will follow by inserting a textSeparator + target.push(textSeparator); + } - if (isFallback) { - // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early - // because they are likely superceded by primary content and we want to avoid needing to clean - // them up when the primary content is ready. They are never hydrated on the client anyway because - // boundaries in fallback are awaited or client render, in either case there is never hydration - return null; - } else if (typeof props.charSet === 'string') { - // "charset" Should really be config and not picked up from tags however since this is - // the only way to embed the tag today we flush it on a special queue on the Request so it - // can go before everything else. Like viewport this means that the tag will escape it's - // parent container. - return pushSelfClosing(renderState.charsetChunks, props, 'meta'); - } else if (props.name === 'viewport') { - // "viewport" is flushed on the Request so it can go earlier that Float resources that - // might be affected by it. This means it can escape the boundary it is rendered within. - // This is a pragmatic solution to viewport being incredibly sensitive to document order - // without requiring all hoistables to be flushed too early. - return pushSelfClosing(renderState.viewportChunks, props, 'meta'); - } else { - return pushSelfClosing(renderState.hoistableChunks, props, 'meta'); - } + if (isFallback) { + // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early + // because they are likely superceded by primary content and we want to avoid needing to clean + // them up when the primary content is ready. They are never hydrated on the client anyway because + // boundaries in fallback are awaited or client render, in either case there is never hydration + return null; + } else if (typeof props.charSet === 'string') { + // "charset" Should really be config and not picked up from tags however since this is + // the only way to embed the tag today we flush it on a special queue on the Request so it + // can go before everything else. Like viewport this means that the tag will escape it's + // parent container. + return pushSelfClosing(renderState.charsetChunks, props, 'meta'); + } else if (props.name === 'viewport') { + // "viewport" is flushed on the Request so it can go earlier that Float resources that + // might be affected by it. This means it can escape the boundary it is rendered within. + // This is a pragmatic solution to viewport being incredibly sensitive to document order + // without requiring all hoistables to be flushed too early. + return pushSelfClosing(renderState.viewportChunks, props, 'meta'); + } else { + return pushSelfClosing(renderState.hoistableChunks, props, 'meta'); } - } else { - return pushSelfClosing(target, props, 'meta'); } } @@ -2385,172 +2375,168 @@ function pushLink( noscriptTagInScope: boolean, isFallback: boolean, ): null { - if (enableFloat) { - const rel = props.rel; - const href = props.href; - const precedence = props.precedence; + const rel = props.rel; + const href = props.href; + const precedence = props.precedence; + if ( + insertionMode === SVG_MODE || + noscriptTagInScope || + props.itemProp != null || + typeof rel !== 'string' || + typeof href !== 'string' || + href === '' + ) { + if (__DEV__) { + if (rel === 'stylesheet' && typeof props.precedence === 'string') { + if (typeof href !== 'string' || !href) { + console.error( + 'React encountered a `` with a `precedence` prop and expected the `href` prop to be a non-empty string but ecountered %s instead. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop ensure there is a non-empty string `href` prop as well, otherwise remove the `precedence` prop.', + getValueDescriptorExpectingObjectForWarning(href), + ); + } + } + } + pushLinkImpl(target, props); + return null; + } + + if (props.rel === 'stylesheet') { + // This may hoistable as a Stylesheet Resource, otherwise it will emit in place + const key = getResourceKey(href); if ( - insertionMode === SVG_MODE || - noscriptTagInScope || - props.itemProp != null || - typeof rel !== 'string' || - typeof href !== 'string' || - href === '' + typeof precedence !== 'string' || + props.disabled != null || + props.onLoad || + props.onError ) { + // This stylesheet is either not opted into Resource semantics or has conflicting properties which + // disqualify it for such. We can still create a preload resource to help it load faster on the + // client if (__DEV__) { - if (rel === 'stylesheet' && typeof props.precedence === 'string') { - if (typeof href !== 'string' || !href) { + if (typeof precedence === 'string') { + if (props.disabled != null) { + console.error( + 'React encountered a `` with a `precedence` prop and a `disabled` prop. The presence of the `disabled` prop indicates an intent to manage the stylesheet active state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the `disabled` prop, otherwise remove the `precedence` prop.', + ); + } else if (props.onLoad || props.onError) { + const propDescription = + props.onLoad && props.onError + ? '`onLoad` and `onError` props' + : props.onLoad + ? '`onLoad` prop' + : '`onError` prop'; console.error( - 'React encountered a `` with a `precedence` prop and expected the `href` prop to be a non-empty string but ecountered %s instead. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop ensure there is a non-empty string `href` prop as well, otherwise remove the `precedence` prop.', - getValueDescriptorExpectingObjectForWarning(href), + 'React encountered a `` with a `precedence` prop and %s. The presence of loading and error handlers indicates an intent to manage the stylesheet loading state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the %s, otherwise remove the `precedence` prop.', + propDescription, + propDescription, ); } } } - pushLinkImpl(target, props); - return null; - } - - if (props.rel === 'stylesheet') { - // This may hoistable as a Stylesheet Resource, otherwise it will emit in place - const key = getResourceKey(href); - if ( - typeof precedence !== 'string' || - props.disabled != null || - props.onLoad || - props.onError - ) { - // This stylesheet is either not opted into Resource semantics or has conflicting properties which - // disqualify it for such. We can still create a preload resource to help it load faster on the - // client - if (__DEV__) { - if (typeof precedence === 'string') { - if (props.disabled != null) { - console.error( - 'React encountered a `` with a `precedence` prop and a `disabled` prop. The presence of the `disabled` prop indicates an intent to manage the stylesheet active state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the `disabled` prop, otherwise remove the `precedence` prop.', - ); - } else if (props.onLoad || props.onError) { - const propDescription = - props.onLoad && props.onError - ? '`onLoad` and `onError` props' - : props.onLoad - ? '`onLoad` prop' - : '`onError` prop'; - console.error( - 'React encountered a `` with a `precedence` prop and %s. The presence of loading and error handlers indicates an intent to manage the stylesheet loading state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the %s, otherwise remove the `precedence` prop.', - propDescription, - propDescription, - ); - } - } - } - return pushLinkImpl(target, props); - } else { - // This stylesheet refers to a Resource and we create a new one if necessary - let styleQueue = renderState.styles.get(precedence); - const hasKey = resumableState.styleResources.hasOwnProperty(key); - const resourceState = hasKey - ? resumableState.styleResources[key] - : undefined; - if (resourceState !== EXISTS) { - // We are going to create this resource now so it is marked as Exists - resumableState.styleResources[key] = EXISTS; - - // If this is the first time we've encountered this precedence we need - // to create a StyleQueue - if (!styleQueue) { - styleQueue = { - precedence: stringToChunk(escapeTextForBrowser(precedence)), - rules: ([]: Array), - hrefs: ([]: Array), - sheets: (new Map(): Map), - }; - renderState.styles.set(precedence, styleQueue); - } - - const resource: StylesheetResource = { - state: PENDING, - props: stylesheetPropsFromRawProps(props), + return pushLinkImpl(target, props); + } else { + // This stylesheet refers to a Resource and we create a new one if necessary + let styleQueue = renderState.styles.get(precedence); + const hasKey = resumableState.styleResources.hasOwnProperty(key); + const resourceState = hasKey + ? resumableState.styleResources[key] + : undefined; + if (resourceState !== EXISTS) { + // We are going to create this resource now so it is marked as Exists + resumableState.styleResources[key] = EXISTS; + + // If this is the first time we've encountered this precedence we need + // to create a StyleQueue + if (!styleQueue) { + styleQueue = { + precedence: stringToChunk(escapeTextForBrowser(precedence)), + rules: ([]: Array), + hrefs: ([]: Array), + sheets: (new Map(): Map), }; + renderState.styles.set(precedence, styleQueue); + } - if (resourceState) { - // When resourceState is truty it is a Preload state. We cast it for clarity - const preloadState: Preloaded | PreloadedWithCredentials = - resourceState; - if (preloadState.length === 2) { - adoptPreloadCredentials(resource.props, preloadState); - } + const resource: StylesheetResource = { + state: PENDING, + props: stylesheetPropsFromRawProps(props), + }; - const preloadResource = renderState.preloads.stylesheets.get(key); - if (preloadResource && preloadResource.length > 0) { - // The Preload for this resource was created in this render pass and has not flushed yet so - // we need to clear it to avoid it flushing. - preloadResource.length = 0; - } else { - // Either the preload resource from this render already flushed in this render pass - // or the preload flushed in a prior pass (prerender). In either case we need to mark - // this resource as already having been preloaded. - resource.state = PRELOADED; - } - } else { - // We don't need to check whether a preloadResource exists in the renderState - // because if it did exist then the resourceState would also exist and we would - // have hit the primary if condition above. + if (resourceState) { + // When resourceState is truty it is a Preload state. We cast it for clarity + const preloadState: Preloaded | PreloadedWithCredentials = + resourceState; + if (preloadState.length === 2) { + adoptPreloadCredentials(resource.props, preloadState); } - // We add the newly created resource to our StyleQueue and if necessary - // track the resource with the currently rendering boundary - styleQueue.sheets.set(key, resource); - if (hoistableState) { - hoistableState.stylesheets.add(resource); + const preloadResource = renderState.preloads.stylesheets.get(key); + if (preloadResource && preloadResource.length > 0) { + // The Preload for this resource was created in this render pass and has not flushed yet so + // we need to clear it to avoid it flushing. + preloadResource.length = 0; + } else { + // Either the preload resource from this render already flushed in this render pass + // or the preload flushed in a prior pass (prerender). In either case we need to mark + // this resource as already having been preloaded. + resource.state = PRELOADED; } } else { - // We need to track whether this boundary should wait on this resource or not. - // Typically this resource should always exist since we either had it or just created - // it. However, it's possible when you resume that the style has already been emitted - // and then it wouldn't be recreated in the RenderState and there's no need to track - // it again since we should've hoisted it to the shell already. - if (styleQueue) { - const resource = styleQueue.sheets.get(key); - if (resource) { - if (hoistableState) { - hoistableState.stylesheets.add(resource); - } + // We don't need to check whether a preloadResource exists in the renderState + // because if it did exist then the resourceState would also exist and we would + // have hit the primary if condition above. + } + + // We add the newly created resource to our StyleQueue and if necessary + // track the resource with the currently rendering boundary + styleQueue.sheets.set(key, resource); + if (hoistableState) { + hoistableState.stylesheets.add(resource); + } + } else { + // We need to track whether this boundary should wait on this resource or not. + // Typically this resource should always exist since we either had it or just created + // it. However, it's possible when you resume that the style has already been emitted + // and then it wouldn't be recreated in the RenderState and there's no need to track + // it again since we should've hoisted it to the shell already. + if (styleQueue) { + const resource = styleQueue.sheets.get(key); + if (resource) { + if (hoistableState) { + hoistableState.stylesheets.add(resource); } } } - if (textEmbedded) { - // This link follows text but we aren't writing a tag. while not as efficient as possible we need - // to be safe and assume text will follow by inserting a textSeparator - target.push(textSeparator); - } - return null; } - } else if (props.onLoad || props.onError) { - // When using load handlers we cannot hoist and need to emit links in place - return pushLinkImpl(target, props); - } else { - // We can hoist this link so we may need to emit a text separator. - // @TODO refactor text separators so we don't have to defensively add - // them when we don't end up emitting a tag as a result of pushStartInstance if (textEmbedded) { // This link follows text but we aren't writing a tag. while not as efficient as possible we need // to be safe and assume text will follow by inserting a textSeparator target.push(textSeparator); } - - if (isFallback) { - // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early - // because they are likely superceded by primary content and we want to avoid needing to clean - // them up when the primary content is ready. They are never hydrated on the client anyway because - // boundaries in fallback are awaited or client render, in either case there is never hydration - return null; - } else { - return pushLinkImpl(renderState.hoistableChunks, props); - } + return null; } - } else { + } else if (props.onLoad || props.onError) { + // When using load handlers we cannot hoist and need to emit links in place return pushLinkImpl(target, props); + } else { + // We can hoist this link so we may need to emit a text separator. + // @TODO refactor text separators so we don't have to defensively add + // them when we don't end up emitting a tag as a result of pushStartInstance + if (textEmbedded) { + // This link follows text but we aren't writing a tag. while not as efficient as possible we need + // to be safe and assume text will follow by inserting a textSeparator + target.push(textSeparator); + } + + if (isFallback) { + // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early + // because they are likely superceded by primary content and we want to avoid needing to clean + // them up when the primary content is ready. They are never hydrated on the client anyway because + // boundaries in fallback are awaited or client render, in either case there is never hydration + return null; + } else { + return pushLinkImpl(renderState.hoistableChunks, props); + } } } @@ -2623,84 +2609,78 @@ function pushStyle( } } } - if (enableFloat) { - const precedence = props.precedence; - const href = props.href; + const precedence = props.precedence; + const href = props.href; - if ( - insertionMode === SVG_MODE || - noscriptTagInScope || - props.itemProp != null || - typeof precedence !== 'string' || - typeof href !== 'string' || - href === '' - ) { - // This style tag is not able to be turned into a Style Resource - return pushStyleImpl(target, props); + if ( + insertionMode === SVG_MODE || + noscriptTagInScope || + props.itemProp != null || + typeof precedence !== 'string' || + typeof href !== 'string' || + href === '' + ) { + // This style tag is not able to be turned into a Style Resource + return pushStyleImpl(target, props); + } + + if (__DEV__) { + if (href.includes(' ')) { + console.error( + 'React expected the `href` prop for a