diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 5e6569fc68682..34581879295b8 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -ef9f6e77b8ef968eee659ae797da4bdc07bbbde3 +6396b664118442f3c2eae7bf13732fcb27bda98f diff --git a/compiled/facebook-www/REVISION_TRANSFORMS b/compiled/facebook-www/REVISION_TRANSFORMS index 5e6569fc68682..34581879295b8 100644 --- a/compiled/facebook-www/REVISION_TRANSFORMS +++ b/compiled/facebook-www/REVISION_TRANSFORMS @@ -1 +1 @@ -ef9f6e77b8ef968eee659ae797da4bdc07bbbde3 +6396b664118442f3c2eae7bf13732fcb27bda98f diff --git a/compiled/facebook-www/React-dev.classic.js b/compiled/facebook-www/React-dev.classic.js index 6170097cf828c..63c628acfd3c7 100644 --- a/compiled/facebook-www/React-dev.classic.js +++ b/compiled/facebook-www/React-dev.classic.js @@ -27,7 +27,7 @@ if ( } "use strict"; -var ReactVersion = "18.3.0-www-classic-ef9f6e77b-20230209"; +var ReactVersion = "18.3.0-www-classic-6396b6641-20230209"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js index 739e3311c60df..27b452f7ebcea 100644 --- a/compiled/facebook-www/React-dev.modern.js +++ b/compiled/facebook-www/React-dev.modern.js @@ -27,7 +27,7 @@ if ( } "use strict"; -var ReactVersion = "18.3.0-www-modern-ef9f6e77b-20230209"; +var ReactVersion = "18.3.0-www-modern-6396b6641-20230209"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/React-prod.classic.js b/compiled/facebook-www/React-prod.classic.js index 1d5d3381647ce..93fd796d8360f 100644 --- a/compiled/facebook-www/React-prod.classic.js +++ b/compiled/facebook-www/React-prod.classic.js @@ -646,4 +646,4 @@ exports.useSyncExternalStore = function ( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-classic-ef9f6e77b-20230209"; +exports.version = "18.3.0-www-classic-6396b6641-20230209"; diff --git a/compiled/facebook-www/React-prod.modern.js b/compiled/facebook-www/React-prod.modern.js index ef3df69f4ff70..a166b9017b80e 100644 --- a/compiled/facebook-www/React-prod.modern.js +++ b/compiled/facebook-www/React-prod.modern.js @@ -638,4 +638,4 @@ exports.useSyncExternalStore = function ( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-modern-ef9f6e77b-20230209"; +exports.version = "18.3.0-www-modern-6396b6641-20230209"; diff --git a/compiled/facebook-www/React-profiling.classic.js b/compiled/facebook-www/React-profiling.classic.js index cbe57f0a1c4ef..a2e7528e4e59d 100644 --- a/compiled/facebook-www/React-profiling.classic.js +++ b/compiled/facebook-www/React-profiling.classic.js @@ -657,7 +657,7 @@ exports.useSyncExternalStore = function ( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-classic-ef9f6e77b-20230209"; +exports.version = "18.3.0-www-classic-6396b6641-20230209"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/React-profiling.modern.js b/compiled/facebook-www/React-profiling.modern.js index 8be873f2579e8..359043d97067a 100644 --- a/compiled/facebook-www/React-profiling.modern.js +++ b/compiled/facebook-www/React-profiling.modern.js @@ -649,7 +649,7 @@ exports.useSyncExternalStore = function ( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-modern-ef9f6e77b-20230209"; +exports.version = "18.3.0-www-modern-6396b6641-20230209"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js index 83354fb973a5e..55796de21efb9 100644 --- a/compiled/facebook-www/ReactART-dev.classic.js +++ b/compiled/facebook-www/ReactART-dev.classic.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-classic-ef9f6e77b-20230209"; +var ReactVersion = "18.3.0-www-classic-6396b6641-20230209"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -229,7 +229,7 @@ var OffscreenComponent = 22; var LegacyHiddenComponent = 23; var CacheComponent = 24; var TracingMarkerComponent = 25; -var HostResource = 26; +var HostHoistable = 26; var HostSingleton = 27; // ATTENTION @@ -431,7 +431,7 @@ function getComponentNameFromFiber(fiber) { case Fragment: return "Fragment"; - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: // Host component type is the display name (e.g. "div", "View") @@ -877,7 +877,7 @@ function findCurrentHostFiberImpl(node) { if ( tag === HostComponent || - tag === HostResource || + tag === HostHoistable || tag === HostSingleton || tag === HostText ) { @@ -4962,7 +4962,7 @@ function describeFiber(fiber) { var source = fiber._debugSource; switch (fiber.tag) { - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: return describeBuiltInComponentFrame(fiber.type); @@ -15856,7 +15856,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate( } break; - case HostResource: case HostSingleton: case HostComponent: pushHostContext(workInProgress); @@ -16202,7 +16201,7 @@ function beginWork(current, workInProgress, renderLanes) { case HostRoot: return updateHostRoot(current, workInProgress, renderLanes); - case HostResource: + case HostHoistable: // eslint-disable-next-line no-fallthrough @@ -17911,7 +17910,7 @@ function completeWork(current, workInProgress, renderLanes) { return null; } - case HostResource: + case HostHoistable: // eslint-disable-next-line-no-fallthrough case HostSingleton: @@ -18601,7 +18600,7 @@ function unwindWork(current, workInProgress, renderLanes) { return null; } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { // TODO: popHydrationState @@ -18732,7 +18731,7 @@ function unwindInterruptedWork(current, interruptedWork, renderLanes) { break; } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { popHostContext(interruptedWork); @@ -19178,7 +19177,7 @@ function commitBeforeMutationEffectsOnFiber(finishedWork) { } case HostComponent: - case HostResource: + case HostHoistable: case HostSingleton: case HostText: case HostPortal: @@ -19774,7 +19773,7 @@ function commitLayoutEffectOnFiber( break; } - case HostResource: + case HostHoistable: // eslint-disable-next-line-no-fallthrough case HostSingleton: @@ -20224,7 +20223,7 @@ function commitAttachRef(finishedWork) { var instanceToUse; switch (finishedWork.tag) { - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: instanceToUse = getPublicInstance(instance); @@ -20600,7 +20599,7 @@ function commitDeletionEffectsOnFiber( // that don't modify the stack. switch (deletedFiber.tag) { - case HostResource: + case HostHoistable: // eslint-disable-next-line no-fallthrough case HostSingleton: @@ -21106,7 +21105,7 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { return; } - case HostResource: + case HostHoistable: // eslint-disable-next-line-no-fallthrough case HostSingleton: @@ -21151,14 +21150,14 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { var oldProps = current !== null ? current.memoizedProps : newProps; var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components. - var updatePayload = finishedWork.updateQueue; + var _updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (updatePayload !== null) { + if (_updatePayload !== null) { try { commitUpdate( _instance2, - updatePayload, + _updatePayload, type, oldProps, newProps, @@ -21211,15 +21210,19 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { } case HostRoot: { - recursivelyTraverseMutationEffects(root, finishedWork); - commitReconciliationEffects(finishedWork); + { + recursivelyTraverseMutationEffects(root, finishedWork); + commitReconciliationEffects(finishedWork); + } return; } case HostPortal: { - recursivelyTraverseMutationEffects(root, finishedWork); - commitReconciliationEffects(finishedWork); + { + recursivelyTraverseMutationEffects(root, finishedWork); + commitReconciliationEffects(finishedWork); + } return; } @@ -21489,7 +21492,7 @@ function disappearLayoutEffects(finishedWork) { break; } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { // TODO (Offscreen) Check: flags & RefStatic @@ -21591,7 +21594,7 @@ function reappearLayoutEffects( // ... // } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { recursivelyTraverseReappearLayoutEffects( @@ -26748,7 +26751,7 @@ function findChildHostInstancesForFiberShallowly(fiber, hostInstances) { var foundHostInstances = false; while (true) { - if (node.tag === HostComponent || node.tag === HostResource || false) { + if (node.tag === HostComponent || node.tag === HostHoistable || false) { // We got a match. foundHostInstances = true; hostInstances.add(node.stateNode); // There may still be more, so keep searching. diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index c6205717ce61f..3e1d38dadc92e 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-modern-ef9f6e77b-20230209"; +var ReactVersion = "18.3.0-www-modern-6396b6641-20230209"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -229,7 +229,7 @@ var OffscreenComponent = 22; var LegacyHiddenComponent = 23; var CacheComponent = 24; var TracingMarkerComponent = 25; -var HostResource = 26; +var HostHoistable = 26; var HostSingleton = 27; // ATTENTION @@ -431,7 +431,7 @@ function getComponentNameFromFiber(fiber) { case Fragment: return "Fragment"; - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: // Host component type is the display name (e.g. "div", "View") @@ -874,7 +874,7 @@ function findCurrentHostFiberImpl(node) { if ( tag === HostComponent || - tag === HostResource || + tag === HostHoistable || tag === HostSingleton || tag === HostText ) { @@ -4724,7 +4724,7 @@ function describeFiber(fiber) { var source = fiber._debugSource; switch (fiber.tag) { - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: return describeBuiltInComponentFrame(fiber.type); @@ -15568,7 +15568,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate( } break; - case HostResource: case HostSingleton: case HostComponent: pushHostContext(workInProgress); @@ -15910,7 +15909,7 @@ function beginWork(current, workInProgress, renderLanes) { case HostRoot: return updateHostRoot(current, workInProgress, renderLanes); - case HostResource: + case HostHoistable: // eslint-disable-next-line no-fallthrough @@ -17614,7 +17613,7 @@ function completeWork(current, workInProgress, renderLanes) { return null; } - case HostResource: + case HostHoistable: // eslint-disable-next-line-no-fallthrough case HostSingleton: @@ -18295,7 +18294,7 @@ function unwindWork(current, workInProgress, renderLanes) { return null; } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { // TODO: popHydrationState @@ -18421,7 +18420,7 @@ function unwindInterruptedWork(current, interruptedWork, renderLanes) { break; } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { popHostContext(interruptedWork); @@ -18867,7 +18866,7 @@ function commitBeforeMutationEffectsOnFiber(finishedWork) { } case HostComponent: - case HostResource: + case HostHoistable: case HostSingleton: case HostText: case HostPortal: @@ -19463,7 +19462,7 @@ function commitLayoutEffectOnFiber( break; } - case HostResource: + case HostHoistable: // eslint-disable-next-line-no-fallthrough case HostSingleton: @@ -19913,7 +19912,7 @@ function commitAttachRef(finishedWork) { var instanceToUse; switch (finishedWork.tag) { - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: instanceToUse = getPublicInstance(instance); @@ -20289,7 +20288,7 @@ function commitDeletionEffectsOnFiber( // that don't modify the stack. switch (deletedFiber.tag) { - case HostResource: + case HostHoistable: // eslint-disable-next-line no-fallthrough case HostSingleton: @@ -20795,7 +20794,7 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { return; } - case HostResource: + case HostHoistable: // eslint-disable-next-line-no-fallthrough case HostSingleton: @@ -20840,14 +20839,14 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { var oldProps = current !== null ? current.memoizedProps : newProps; var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components. - var updatePayload = finishedWork.updateQueue; + var _updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (updatePayload !== null) { + if (_updatePayload !== null) { try { commitUpdate( _instance2, - updatePayload, + _updatePayload, type, oldProps, newProps, @@ -20900,15 +20899,19 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { } case HostRoot: { - recursivelyTraverseMutationEffects(root, finishedWork); - commitReconciliationEffects(finishedWork); + { + recursivelyTraverseMutationEffects(root, finishedWork); + commitReconciliationEffects(finishedWork); + } return; } case HostPortal: { - recursivelyTraverseMutationEffects(root, finishedWork); - commitReconciliationEffects(finishedWork); + { + recursivelyTraverseMutationEffects(root, finishedWork); + commitReconciliationEffects(finishedWork); + } return; } @@ -21178,7 +21181,7 @@ function disappearLayoutEffects(finishedWork) { break; } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { // TODO (Offscreen) Check: flags & RefStatic @@ -21280,7 +21283,7 @@ function reappearLayoutEffects( // ... // } - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: { recursivelyTraverseReappearLayoutEffects( @@ -26437,7 +26440,7 @@ function findChildHostInstancesForFiberShallowly(fiber, hostInstances) { var foundHostInstances = false; while (true) { - if (node.tag === HostComponent || node.tag === HostResource || false) { + if (node.tag === HostComponent || node.tag === HostHoistable || false) { // We got a match. foundHostInstances = true; hostInstances.add(node.stateNode); // There may still be more, so keep searching. diff --git a/compiled/facebook-www/ReactART-prod.classic.js b/compiled/facebook-www/ReactART-prod.classic.js index e7e3f4d19612f..b51778ade9b83 100644 --- a/compiled/facebook-www/ReactART-prod.classic.js +++ b/compiled/facebook-www/ReactART-prod.classic.js @@ -4700,7 +4700,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate( enableTransitionTracing && pushRootMarkerInstance(workInProgress); pushProvider(workInProgress, CacheContext, current.memoizedState.cache); break; - case 26: case 27: case 5: pushHostContext(workInProgress); @@ -9845,7 +9844,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-classic-ef9f6e77b-20230209", + version: "18.3.0-www-classic-6396b6641-20230209", rendererPackageName: "react-art" }; var internals$jscomp$inline_1318 = { @@ -9876,7 +9875,7 @@ var internals$jscomp$inline_1318 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-ef9f6e77b-20230209" + reconcilerVersion: "18.3.0-next-6396b6641-20230209" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1319 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index 6883114269378..d5c65cbe213fe 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -4455,7 +4455,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate( enableTransitionTracing && pushRootMarkerInstance(workInProgress); pushProvider(workInProgress, CacheContext, current.memoizedState.cache); break; - case 26: case 27: case 5: pushHostContext(workInProgress); @@ -9510,7 +9509,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-modern-ef9f6e77b-20230209", + version: "18.3.0-www-modern-6396b6641-20230209", rendererPackageName: "react-art" }; var internals$jscomp$inline_1309 = { @@ -9541,7 +9540,7 @@ var internals$jscomp$inline_1309 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-ef9f6e77b-20230209" + reconcilerVersion: "18.3.0-next-6396b6641-20230209" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1310 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index 2993b2b18e8db..dbfba89c24472 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -191,7 +191,7 @@ var OffscreenComponent = 22; var LegacyHiddenComponent = 23; var CacheComponent = 24; var TracingMarkerComponent = 25; -var HostResource = 26; +var HostHoistable = 26; var HostSingleton = 27; // ATTENTION @@ -393,7 +393,7 @@ function getComponentNameFromFiber(fiber) { case Fragment: return "Fragment"; - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: // Host component type is the display name (e.g. "div", "View") @@ -861,7 +861,7 @@ function findCurrentHostFiberImpl(node) { if ( tag === HostComponent || - tag === HostResource || + tag === HostHoistable || tag === HostSingleton || tag === HostText ) { @@ -896,7 +896,7 @@ function findCurrentHostFiberWithNoPortalsImpl(node) { if ( tag === HostComponent || - tag === HostResource || + tag === HostHoistable || tag === HostSingleton || tag === HostText ) { @@ -2386,7 +2386,7 @@ function describeFiber(fiber) { var source = fiber._debugSource; switch (fiber.tag) { - case HostResource: + case HostHoistable: case HostSingleton: case HostComponent: return describeBuiltInComponentFrame(fiber.type); @@ -8973,444 +8973,6 @@ var Internals = { } }; -function warnOnMissingHrefAndRel(pendingProps, currentProps) { - { - if (currentProps != null) { - var originalResourceName = - typeof currentProps.href === "string" - ? 'Resource with href "' + currentProps.href + '"' - : "Resource"; - var originalRelStatement = getValueDescriptorExpectingEnumForWarning( - currentProps.rel - ); - var pendingRel = getValueDescriptorExpectingEnumForWarning( - pendingProps.rel - ); - var pendingHref = getValueDescriptorExpectingEnumForWarning( - pendingProps.href - ); - - if (typeof pendingProps.rel !== "string") { - error( - 'A previously rendered as a %s with rel "%s" but was updated with an invalid rel: %s. When a link' + - " does not have a valid rel prop it is not represented in the DOM. If this is intentional, instead" + - " do not render the anymore.", - originalResourceName, - originalRelStatement, - pendingRel - ); - } else if (typeof pendingProps.href !== "string") { - error( - "A previously rendered as a %s but was updated with an invalid href prop: %s. When a link" + - " does not have a valid href prop it is not represented in the DOM. If this is intentional, instead" + - " do not render the anymore.", - originalResourceName, - pendingHref - ); - } - } else { - var _pendingRel = getValueDescriptorExpectingEnumForWarning( - pendingProps.rel - ); - - var _pendingHref = getValueDescriptorExpectingEnumForWarning( - pendingProps.href - ); - - if (typeof pendingProps.rel !== "string") { - error( - "A is rendering with an invalid rel: %s. When a link" + - " does not have a valid rel prop it is not represented in the DOM. If this is intentional, instead" + - " do not render the anymore.", - _pendingRel - ); - } else if (typeof pendingProps.href !== "string") { - error( - "A is rendering with an invalid href: %s. When a link" + - " does not have a valid href prop it is not represented in the DOM. If this is intentional, instead" + - " do not render the anymore.", - _pendingHref - ); - } - } - } -} -function validatePreloadResourceDifference( - originalProps, - originalImplicit, - latestProps, - latestImplicit -) { - { - var href = originalProps.href; - var originalWarningName = getResourceNameForWarning( - "preload", - originalProps, - originalImplicit - ); - var latestWarningName = getResourceNameForWarning( - "preload", - latestProps, - latestImplicit - ); - - if (latestProps.as !== originalProps.as) { - error( - 'A %s is using the same href "%s" as a %s. This is always an error and React will only keep the first preload' + - " for any given href, discarding subsequent instances. To fix, find where you are using this href in link" + - " tags or in calls to ReactDOM.preload() or ReactDOM.preinit() and either make the Resource types agree or" + - " update the hrefs to be distinct for different Resource types.", - latestWarningName, - href, - originalWarningName - ); - } else { - var missingProps = null; - var extraProps = null; - var differentProps = null; - - if (originalProps.media != null && latestProps.media == null) { - missingProps = missingProps || {}; - missingProps.media = originalProps.media; - } - - for (var propName in latestProps) { - var propValue = latestProps[propName]; - var originalValue = originalProps[propName]; - - if (propValue != null && propValue !== originalValue) { - if (originalValue == null) { - extraProps = extraProps || {}; - extraProps[propName] = propValue; - } else { - differentProps = differentProps || {}; - differentProps[propName] = { - original: originalValue, - latest: propValue - }; - } - } - } - - if (missingProps || extraProps || differentProps) { - warnDifferentProps( - href, - "href", - originalWarningName, - latestWarningName, - extraProps, - missingProps, - differentProps - ); - } - } - } -} -function validateStyleResourceDifference(originalProps, latestProps) { - { - var href = originalProps.href; // eslint-disable-next-line no-labels - - var originalWarningName = getResourceNameForWarning( - "style", - originalProps, - false - ); - var latestWarningName = getResourceNameForWarning( - "style", - latestProps, - false - ); - var missingProps = null; - var extraProps = null; - var differentProps = null; - - if (originalProps.media != null && latestProps.media == null) { - missingProps = missingProps || {}; - missingProps.media = originalProps.media; - } - - for (var propName in latestProps) { - var propValue = latestProps[propName]; - var originalValue = originalProps[propName]; - - if (propValue != null && propValue !== originalValue) { - propName = propName === "data-precedence" ? "precedence" : propName; - - if (originalValue == null) { - extraProps = extraProps || {}; - extraProps[propName] = propValue; - } else { - differentProps = differentProps || {}; - differentProps[propName] = { - original: originalValue, - latest: propValue - }; - } - } - } - - if (missingProps || extraProps || differentProps) { - warnDifferentProps( - href, - "href", - originalWarningName, - latestWarningName, - extraProps, - missingProps, - differentProps - ); - } - } -} -function validateScriptResourceDifference(originalProps, latestProps) { - { - var src = originalProps.src; // eslint-disable-next-line no-labels - - var originalWarningName = getResourceNameForWarning( - "script", - originalProps, - false - ); - var latestWarningName = getResourceNameForWarning( - "script", - latestProps, - false - ); - var extraProps = null; - var differentProps = null; - - for (var propName in latestProps) { - var propValue = latestProps[propName]; - var originalValue = originalProps[propName]; - - if (propValue != null && propValue !== originalValue) { - if (originalValue == null) { - extraProps = extraProps || {}; - extraProps[propName] = propValue; - } else { - differentProps = differentProps || {}; - differentProps[propName] = { - original: originalValue, - latest: propValue - }; - } - } - } - - if (extraProps || differentProps) { - warnDifferentProps( - src, - "src", - originalWarningName, - latestWarningName, - extraProps, - null, - differentProps - ); - } - } -} - -function warnDifferentProps( - url, - urlPropKey, - originalName, - latestName, - extraProps, - missingProps, - differentProps -) { - { - var juxtaposedNameStatement = - latestName === originalName - ? "an earlier instance of this Resource" - : "a " + originalName + " with the same " + urlPropKey; - var comparisonStatement = ""; - - if (missingProps !== null && typeof missingProps === "object") { - for (var propName in missingProps) { - comparisonStatement += - "\n " + - propName + - ': missing or null in latest props, "' + - missingProps[propName] + - '" in original props'; - } - } - - if (extraProps !== null && typeof extraProps === "object") { - for (var _propName in extraProps) { - comparisonStatement += - "\n " + - _propName + - ': "' + - extraProps[_propName] + - '" in latest props, missing or null in original props'; - } - } - - if (differentProps !== null && typeof differentProps === "object") { - for (var _propName2 in differentProps) { - comparisonStatement += - "\n " + - _propName2 + - ': "' + - differentProps[_propName2].latest + - '" in latest props, "' + - differentProps[_propName2].original + - '" in original props'; - } - } - - error( - 'A %s with %s "%s" has props that disagree with those found on %s. Resources always use the props' + - " that were provided the first time they are encountered so any differences will be ignored. Please" + - " update Resources that share an %s to have props that agree. The differences are described below.%s", - latestName, - urlPropKey, - url, - juxtaposedNameStatement, - urlPropKey, - comparisonStatement - ); - } -} - -function getResourceNameForWarning(type, props, implicit) { - { - switch (type) { - case "style": { - return "style Resource"; - } - - case "script": { - return "script Resource"; - } - - case "preload": { - if (implicit) { - return "preload for a " + props.as + " Resource"; - } - - return 'preload Resource (as "' + props.as + '")'; - } - } - } - - return "Resource"; -} - -function validateURLKeyedUpdatedProps( - pendingProps, - currentProps, - resourceType, - urlPropKey -) { - { - // This function should never be called if we don't have /srcs so we don't bother considering - // Whether they are null or undefined - if (pendingProps[urlPropKey] === currentProps[urlPropKey]) { - // If we have the same href/src we need all other props to be the same - var missingProps; - var extraProps; - var differentProps; - var allProps = Array.from( - new Set(Object.keys(currentProps).concat(Object.keys(pendingProps))) - ); - - for (var i = 0; i < allProps.length; i++) { - var propName = allProps[i]; - var pendingValue = pendingProps[propName]; - var currentValue = currentProps[propName]; - - if ( - pendingValue !== currentValue && - !(pendingValue == null && currentValue == null) - ) { - if (pendingValue == null) { - missingProps = missingProps || {}; - missingProps[propName] = currentValue; - } else if (currentValue == null) { - extraProps = extraProps || {}; - extraProps[propName] = pendingValue; - } else { - differentProps = differentProps || {}; - differentProps[propName] = { - original: currentValue, - latest: pendingValue - }; - } - } - } - - if (missingProps || extraProps || differentProps) { - var latestWarningName = getResourceNameForWarning( - resourceType, - currentProps, - false - ); - var comparisonStatement = ""; - - if (missingProps !== null && typeof missingProps === "object") { - for (var _propName3 in missingProps) { - // $FlowFixMe[incompatible-type] - comparisonStatement += - "\n " + - _propName3 + - ': missing or null in latest props, "' + - missingProps[_propName3] + - '" in original props'; - } - } - - if (extraProps !== null && typeof extraProps === "object") { - for (var _propName4 in extraProps) { - // $FlowFixMe[incompatible-type] - comparisonStatement += - "\n " + - _propName4 + - ': "' + - extraProps[_propName4] + - '" in latest props, missing or null in original props'; - } - } - - if (differentProps !== null && typeof differentProps === "object") { - for (var _propName5 in differentProps) { - // $FlowFixMe[incompatible-type] - comparisonStatement += - "\n " + - _propName5 + - ': "' + - differentProps[_propName5].latest + - '" in latest props, "' + - differentProps[_propName5].original + - '" in original props'; - } - } - - error( - 'A %s with %s "%s" received new props with different values from the props used' + - " when this Resource was first rendered. React will only use the props provided when" + - " this resource was first rendered until a new %s is provided. Unlike conventional" + - " DOM elements, Resources instances do not have a one to one correspondence with Elements" + - " in the DOM and as such, every instance of a Resource for a single Resource identifier" + - " (%s) must have props that agree with each other. The differences are described below.%s", - latestWarningName, - urlPropKey, - currentProps[urlPropKey], - urlPropKey, - urlPropKey, - comparisonStatement - ); - - return true; - } - } - } - - return false; -} function validateLinkPropsForStyleResource(props) { { // This should only be called when we know we are opting into Resource semantics (i.e. precedence is not null) @@ -9418,23 +8980,26 @@ function validateLinkPropsForStyleResource(props) { onLoad = props.onLoad, onError = props.onError, disabled = props.disabled; - var allProps = ["onLoad", "onError", "disabled"]; var includedProps = []; - if (onLoad) includedProps.push("onLoad"); - if (onError) includedProps.push("onError"); - if (disabled != null) includedProps.push("disabled"); - var allPropsUnionPhrase = propNamesListJoin(allProps, "or"); + if (onLoad) includedProps.push("`onLoad`"); + if (onError) includedProps.push("`onError`"); + if (disabled != null) includedProps.push("`disabled`"); var includedPropsPhrase = propNamesListJoin(includedProps, "and"); includedPropsPhrase += includedProps.length === 1 ? " prop" : " props"; + var withArticlePhrase = + includedProps.length === 1 + ? "an " + includedPropsPhrase + : "the " + includedPropsPhrase; if (includedProps.length) { error( - 'A link (rel="stylesheet") element with href "%s" has the precedence prop but also included the %s.' + - " When using %s React will opt out of Resource behavior. If you meant for this" + - " element to be treated as a Resource remove the %s. Otherwise remove the precedence prop.", + 'React encountered a with a `precedence` prop that' + + " also included %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.", href, - includedPropsPhrase, - allPropsUnionPhrase, + withArticlePhrase, includedPropsPhrase ); @@ -9467,34 +9032,6 @@ function propNamesListJoin(list, combinator) { } } -function validateLinkPropsForPreloadResource(linkProps) { - { - var href = linkProps.href, - as = linkProps.as; - - if (as === "font") { - var name = getResourceNameForWarning("preload", linkProps, false); - - if (!hasOwnProperty.call(linkProps, "crossOrigin")) { - error( - 'A %s with href "%s" did not specify the crossOrigin prop. Font preloads must always use' + - ' anonymouse CORS mode. To fix add an empty string, "anonymous", or any other string' + - ' value except "use-credentials" for the crossOrigin prop of all font preloads.', - name, - href - ); - } else if (linkProps.crossOrigin === "use-credentials") { - error( - 'A %s with href "%s" specified a crossOrigin value of "use-credentials". Font preloads must always use' + - ' anonymouse CORS mode. To fix use an empty string, "anonymous", or any other string' + - ' value except "use-credentials" for the crossOrigin prop of all font preloads.', - name, - href - ); - } - } - } -} function validatePreloadArguments(href, options) { { if (!href || typeof href !== "string") { @@ -9597,22 +9134,20 @@ function validatePreinitArguments(href, options) { } } } - function getValueDescriptorExpectingObjectForWarning(thing) { return thing === null - ? "null" + ? "`null`" : thing === undefined - ? "undefined" + ? "`undefined`" : thing === "" ? "an empty string" : 'something with type "' + typeof thing + '"'; } - function getValueDescriptorExpectingEnumForWarning(thing) { return thing === null - ? "null" + ? "`null`" : thing === undefined - ? "undefined" + ? "`undefined`" : thing === "" ? "an empty string" : typeof thing === "string" @@ -9753,10 +9288,6 @@ function popHostContext(fiber) { var Dispatcher = Internals.Dispatcher; // In the future this may need to change, especially when modules / scripts are supported -// Brief on purpose due to insertion by script when streaming late boundaries -// s = Status -// l = loaded -// e = errored // It is valid to preload even when we aren't actively rendering. For cases where Float functions are // called when there is no rendering we track the last used document. It is not safe to insert // arbitrary resources into the lastCurrentDocument b/c it may not actually be the document @@ -9784,7 +9315,7 @@ var ReactDOMClientDispatcher = { preinit: preinit }; // global maps of Resources -var preloadResources = new Map(); // getRootNode is missing from IE and old jsdom versions +var preloadPropsMap = new Map(); // getRootNode is missing from IE and old jsdom versions function getRootNode(container) { // $FlowFixMe[method-unbinding] @@ -9798,24 +9329,6 @@ function getRootNode(container) { function getCurrentResourceRoot() { var currentContainer = getCurrentRootHostContainer(); return currentContainer ? getRootNode(currentContainer) : null; -} // This resource type constraint can be loosened. It really is everything except PreloadResource -// because that is the only one that does not have an optional instance type. Expand as needed. - -function resetInstance(resource) { - resource.instance = undefined; -} - -function clearRootResources(rootContainer) { - var rootNode = getRootNode(rootContainer); - var resources = getResourcesFromRoot(rootNode); // We can't actually delete the resource cache because this function is called - // during commit after we have rendered. Instead we detatch any instances from - // the Resource object if they are going to be cleared - // Styles stay put - // Scripts get reset - - resources.scripts.forEach(resetInstance); // Head Resources get reset - - resources.head.forEach(resetInstance); // lastStructuredMeta stays put } // Preloads are somewhat special. Even if we don't have the Document // used by the root that is rendering a component trying to insert a preload // we can still seed the file cache by doing the preload on any document we have @@ -9839,6 +9352,11 @@ function getDocumentForPreloads() { function getDocumentFromRoot(root) { return root.ownerDocument || root; +} + +function getHoistableRoot(container) { + // Flow thinks getRootNode returns Node but we know it is actualy either a Document or ShadowRoot + return container.getRootNode(); } // -------------------------------------- // ReactDOM.Preload // -------------------------------------- @@ -9858,22 +9376,37 @@ function preload(href, options) { ownerDocument ) { var as = options.as; - var resource = preloadResources.get(href); + var limitedEscapedHref = + escapeSelectorAttributeValueInsideDoubleQuotes(href); + var preloadKey = + 'link[rel="preload"][as="' + as + '"][href="' + limitedEscapedHref + '"]'; + var key = preloadKey; - if (resource) { - { - var originallyImplicit = resource._dev_implicit_construction === true; - var latestProps = preloadPropsFromPreloadOptions(href, as, options); - validatePreloadResourceDifference( - resource.props, - originallyImplicit, - latestProps, - false + switch (as) { + case "style": + key = getStyleKey(href); + break; + + case "script": + key = getScriptKey(href); + break; + } + + if (!preloadPropsMap.has(key)) { + var preloadProps = preloadPropsFromPreloadOptions(href, as, options); + preloadPropsMap.set(key, preloadProps); + + if (null === ownerDocument.querySelector(preloadKey)) { + var preloadInstance = createElement( + "link", + preloadProps, + ownerDocument, + HTML_NAMESPACE ); + setInitialProperties(preloadInstance, "link", preloadProps); + markNodeAsResource(preloadInstance); + ownerDocument.head.appendChild(preloadInstance); } - } else { - var resourceProps = preloadPropsFromPreloadOptions(href, as, options); - createPreloadResource(ownerDocument, href, resourceProps); } } } @@ -9905,18 +9438,54 @@ function preinit(href, options) { var as = options.as; if (!resourceRoot) { - // We are going to emit a preload as a best effort fallback since this preinit - // was called outside of a render. Given the passive nature of this fallback - // we do not warn in dev when props disagree if there happens to already be a - // matching preload with this href - var preloadDocument = getDocumentForPreloads(); + if (as === "style" || as === "script") { + // We are going to emit a preload as a best effort fallback since this preinit + // was called outside of a render. Given the passive nature of this fallback + // we do not warn in dev when props disagree if there happens to already be a + // matching preload with this href + var preloadDocument = getDocumentForPreloads(); + + if (preloadDocument) { + var limitedEscapedHref = + escapeSelectorAttributeValueInsideDoubleQuotes(href); + var preloadKey = + 'link[rel="preload"][as="' + + as + + '"][href="' + + limitedEscapedHref + + '"]'; + var key = preloadKey; - if (preloadDocument) { - var preloadResource = preloadResources.get(href); + switch (as) { + case "style": + key = getStyleKey(href); + break; - if (!preloadResource) { - var preloadProps = preloadPropsFromPreinitOptions(href, as, options); - createPreloadResource(preloadDocument, href, preloadProps); + case "script": + key = getScriptKey(href); + break; + } + + if (!preloadPropsMap.has(key)) { + var preloadProps = preloadPropsFromPreinitOptions( + href, + as, + options + ); + preloadPropsMap.set(key, preloadProps); + + if (null === preloadDocument.querySelector(preloadKey)) { + var preloadInstance = createElement( + "link", + preloadProps, + preloadDocument, + HTML_NAMESPACE + ); + setInitialProperties(preloadInstance, "link", preloadProps); + markNodeAsResource(preloadInstance); + preloadDocument.head.appendChild(preloadInstance); + } + } } } @@ -9925,62 +9494,103 @@ function preinit(href, options) { switch (as) { case "style": { - var styleResources = getResourcesFromRoot(resourceRoot).styles; - var precedence = options.precedence || "default"; - var resource = styleResources.get(href); + var styles = getResourcesFromRoot(resourceRoot).hoistableStyles; + + var _key = getStyleKey(href); + + var precedence = options.precedence || "default"; // Check if this resource already exists + + var resource = styles.get(_key); if (resource) { - { - var latestProps = stylePropsFromPreinitOptions( - href, - precedence, - options - ); - validateStyleResourceDifference(resource.props, latestProps); - } - } else { - var resourceProps = stylePropsFromPreinitOptions( + // We can early return. The resource exists and there is nothing + // more to do + return; + } // Attempt to hydrate instance from DOM + + var instance = resourceRoot.querySelector( + getStylesheetSelectorFromKey(_key) + ); + + if (!instance) { + // Construct a new instance and insert it + var stylesheetProps = stylesheetPropsFromPreinitOptions( href, precedence, options ); - resource = createStyleResource( - styleResources, + + var _preloadProps = preloadPropsMap.get(_key); + + if (_preloadProps) { + adoptPreloadPropsForStylesheet(stylesheetProps, _preloadProps); + } + + instance = createElement( + "link", + stylesheetProps, resourceRoot, - href, - precedence, - resourceProps + HTML_NAMESPACE ); - } + markNodeAsResource(instance); + setInitialProperties(instance, "link", stylesheetProps); + insertStylesheet(instance, precedence, resourceRoot); + } // Construct a Resource and cache it - acquireResource(resource); + resource = { + type: "stylesheet", + instance: instance, + count: 1 + }; + styles.set(_key, resource); return; } case "script": { var src = href; - var scriptResources = getResourcesFromRoot(resourceRoot).scripts; + var scripts = getResourcesFromRoot(resourceRoot).hoistableScripts; - var _resource = scriptResources.get(src); + var _key2 = getScriptKey(src); // Check if this resource already exists + + var _resource = scripts.get(_key2); if (_resource) { - { - var _latestProps = scriptPropsFromPreinitOptions(src, options); + // We can early return. The resource exists and there is nothing + // more to do + return; + } // Attempt to hydrate instance from DOM - validateScriptResourceDifference(_resource.props, _latestProps); + var _instance = resourceRoot.querySelector( + getScriptSelectorFromKey(_key2) + ); + + if (!_instance) { + // Construct a new instance and insert it + var scriptProps = scriptPropsFromPreinitOptions(src, options); // Adopt certain preload props + + var _preloadProps2 = preloadPropsMap.get(_key2); + + if (_preloadProps2) { + adoptPreloadPropsForScript(scriptProps, _preloadProps2); } - } else { - var _resourceProps = scriptPropsFromPreinitOptions(src, options); - _resource = createScriptResource( - scriptResources, + _instance = createElement( + "script", + scriptProps, resourceRoot, - src, - _resourceProps + HTML_NAMESPACE ); - } - - acquireResource(_resource); + markNodeAsResource(_instance); + setInitialProperties(_instance, "link", scriptProps); + getDocumentFromRoot(resourceRoot).head.appendChild(_instance); + } // Construct a Resource and cache it + + _resource = { + type: "script", + instance: _instance, + count: 1 + }; + scripts.set(_key2, _resource); return; } } @@ -9997,7 +9607,7 @@ function preloadPropsFromPreinitOptions(href, as, options) { }; } -function stylePropsFromPreinitOptions(href, precedence, options) { +function stylesheetPropsFromPreinitOptions(href, precedence, options) { return { rel: "stylesheet", href: href, @@ -10016,12 +9626,9 @@ function scriptPropsFromPreinitOptions(src, options) { } // -------------------------------------- // Resources from render // -------------------------------------- +// This function is called in begin work and we should always have a currentDocument set -function getTitleKey(child) { - return "title:" + child; -} // This function is called in begin work and we should always have a currentDocument set - -function getResource(type, pendingProps, currentProps) { +function getResource(type, currentProps, pendingProps) { var resourceRoot = getCurrentResourceRoot(); if (!resourceRoot) { @@ -10031,386 +9638,111 @@ function getResource(type, pendingProps, currentProps) { } switch (type) { - case "base": { - var headRoot = getDocumentFromRoot(resourceRoot); - var headResources = getResourcesFromRoot(headRoot).head; - var target = pendingProps.target, - href = pendingProps.href; - var matcher = "base"; - matcher += - typeof href === "string" - ? '[href="' + - escapeSelectorAttributeValueInsideDoubleQuotes(href) + - '"]' - : ":not([href])"; - matcher += - typeof target === "string" - ? '[target="' + - escapeSelectorAttributeValueInsideDoubleQuotes(target) + - '"]' - : ":not([target])"; - var resource = headResources.get(matcher); - - if (!resource) { - resource = { - type: "base", - matcher: matcher, - props: assign({}, pendingProps), - count: 0, - instance: null, - root: headRoot - }; - headResources.set(matcher, resource); - } - - return resource; - } - - case "meta": { - var _matcher, propertyString, parentResource; - - var charSet = pendingProps.charSet, - content = pendingProps.content, - httpEquiv = pendingProps.httpEquiv, - name = pendingProps.name, - itemProp = pendingProps.itemProp, - property = pendingProps.property; - - var _headRoot = getDocumentFromRoot(resourceRoot); - - var _getResourcesFromRoot = getResourcesFromRoot(_headRoot), - _headResources = _getResourcesFromRoot.head, - lastStructuredMeta = _getResourcesFromRoot.lastStructuredMeta; - - if (typeof charSet === "string") { - _matcher = "meta[charset]"; - } else if (typeof content === "string") { - if (typeof httpEquiv === "string") { - _matcher = - 'meta[http-equiv="' + - escapeSelectorAttributeValueInsideDoubleQuotes(httpEquiv) + - '"][content="' + - escapeSelectorAttributeValueInsideDoubleQuotes(content) + - '"]'; - } else if (typeof property === "string") { - propertyString = property; - _matcher = - 'meta[property="' + - escapeSelectorAttributeValueInsideDoubleQuotes(property) + - '"][content="' + - escapeSelectorAttributeValueInsideDoubleQuotes(content) + - '"]'; - var parentPropertyPath = property.split(":").slice(0, -1).join(":"); - parentResource = lastStructuredMeta.get(parentPropertyPath); - - if (parentResource) { - // When using parentResource the matcher is not functional for locating - // the instance in the DOM but it still serves as a unique key. - _matcher = parentResource.matcher + _matcher; - } - } else if (typeof name === "string") { - _matcher = - 'meta[name="' + - escapeSelectorAttributeValueInsideDoubleQuotes(name) + - '"][content="' + - escapeSelectorAttributeValueInsideDoubleQuotes(content) + - '"]'; - } else if (typeof itemProp === "string") { - _matcher = - 'meta[itemprop="' + - escapeSelectorAttributeValueInsideDoubleQuotes(itemProp) + - '"][content="' + - escapeSelectorAttributeValueInsideDoubleQuotes(content) + - '"]'; - } - } - - if (_matcher) { - var _resource2 = _headResources.get(_matcher); - - if (!_resource2) { - _resource2 = { - type: "meta", - matcher: _matcher, - property: propertyString, - parentResource: parentResource, - props: assign({}, pendingProps), - count: 0, - instance: null, - root: _headRoot - }; - - _headResources.set(_matcher, _resource2); - } - - if (typeof _resource2.property === "string") { - // We cast because flow doesn't know that this resource must be a Meta resource - lastStructuredMeta.set(_resource2.property, _resource2); - } - - return _resource2; - } - + case "meta": + case "title": { return null; } - case "title": { - var children = pendingProps.children; - var child; - - if (Array.isArray(children)) { - child = children.length === 1 ? children[0] : null; - } else { - child = children; - } - + case "style": { if ( - typeof child !== "function" && - typeof child !== "symbol" && - child !== null && - child !== undefined + typeof pendingProps.precedence === "string" && + typeof pendingProps.href === "string" ) { - // eslint-disable-next-line react-internal/safe-string-coercion - var childString = "" + child; - - var _headRoot2 = getDocumentFromRoot(resourceRoot); - - var _headResources2 = getResourcesFromRoot(_headRoot2).head; - var key = getTitleKey(childString); - - var _resource3 = _headResources2.get(key); + var key = getStyleKey(pendingProps.href); + var styles = getResourcesFromRoot(resourceRoot).hoistableStyles; + var resource = styles.get(key); - if (!_resource3) { - var titleProps = titlePropsFromRawProps(childString, pendingProps); - _resource3 = { - type: "title", - props: titleProps, - count: 0, + if (!resource) { + resource = { + type: "style", instance: null, - root: _headRoot2 + count: 0 }; - - _headResources2.set(key, _resource3); + styles.set(key, resource); } - return _resource3; + return resource; } - return null; + return { + type: "void", + instance: null, + count: 0 + }; } case "link": { - var rel = pendingProps.rel; - - switch (rel) { - case "stylesheet": { - var styleResources = getResourcesFromRoot(resourceRoot).styles; - var didWarn; - - { - if (currentProps) { - didWarn = validateURLKeyedUpdatedProps( - pendingProps, - currentProps, - "style", - "href" - ); - } - - if (!didWarn) { - didWarn = validateLinkPropsForStyleResource(pendingProps); - } - } - - var precedence = pendingProps.precedence, - _href = pendingProps.href; - - if (typeof _href === "string" && typeof precedence === "string") { - // We've asserted all the specific types for StyleQualifyingProps - var styleRawProps = pendingProps; // We construct or get an existing resource for the style itself and return it - - var _resource4 = styleResources.get(_href); - - if (_resource4) { - { - if (!didWarn) { - var latestProps = stylePropsFromRawProps(styleRawProps); - - if (_resource4._dev_preload_props) { - adoptPreloadPropsForStyle( - latestProps, - _resource4._dev_preload_props - ); - } - - validateStyleResourceDifference( - _resource4.props, - latestProps - ); - } - } - } else { - var resourceProps = stylePropsFromRawProps(styleRawProps); - _resource4 = createStyleResource( - styleResources, - resourceRoot, - _href, - precedence, - resourceProps - ); - immediatelyPreloadStyleResource(_resource4); - } - - return _resource4; - } - - return null; - } - - case "preload": { - { - validateLinkPropsForPreloadResource(pendingProps); - } - - var _href2 = pendingProps.href; - - if (typeof _href2 === "string") { - // We've asserted all the specific types for PreloadQualifyingProps - var preloadRawProps = pendingProps; + if ( + pendingProps.rel === "stylesheet" && + typeof pendingProps.href === "string" && + typeof pendingProps.precedence === "string" + ) { + var qualifiedProps = pendingProps; - var _resource5 = preloadResources.get(_href2); + var _key3 = getStyleKey(qualifiedProps.href); - if (_resource5) { - { - var originallyImplicit = - _resource5._dev_implicit_construction === true; + var _styles = getResourcesFromRoot(resourceRoot).hoistableStyles; - var _latestProps2 = preloadPropsFromRawProps(preloadRawProps); + var _resource2 = _styles.get(_key3); - validatePreloadResourceDifference( - _resource5.props, - originallyImplicit, - _latestProps2, - false - ); - } - } else { - var _resourceProps2 = preloadPropsFromRawProps(preloadRawProps); + if (!_resource2) { + // We asserted this above but Flow can't figure out that the type satisfies + var ownerDocument = getDocumentFromRoot(resourceRoot); + _resource2 = { + type: "stylesheet", + instance: null, + count: 0 + }; - _resource5 = createPreloadResource( - getDocumentFromRoot(resourceRoot), - _href2, - _resourceProps2 - ); - } + _styles.set(_key3, _resource2); - return _resource5; + if (!preloadPropsMap.has(_key3)) { + preloadStylesheet( + ownerDocument, + _key3, + preloadPropsFromStylesheet(qualifiedProps) + ); } - - return null; } - default: { - var _href3 = pendingProps.href, - sizes = pendingProps.sizes, - media = pendingProps.media; - - if (typeof rel === "string" && typeof _href3 === "string") { - var sizeKey = "::sizes:" + (typeof sizes === "string" ? sizes : ""); - var mediaKey = - "::media:" + (typeof media === "string" ? media : ""); - - var _key = "rel:" + rel + "::href:" + _href3 + sizeKey + mediaKey; - - var _headRoot3 = getDocumentFromRoot(resourceRoot); - - var _headResources3 = getResourcesFromRoot(_headRoot3).head; - - var _resource6 = _headResources3.get(_key); - - if (!_resource6) { - _resource6 = { - type: "link", - props: assign({}, pendingProps), - count: 0, - instance: null, - root: _headRoot3 - }; - - _headResources3.set(_key, _resource6); - } - - return _resource6; - } - - { - warnOnMissingHrefAndRel(pendingProps, currentProps); - } - - return null; - } + return _resource2; } + + return null; } case "script": { - var scriptResources = getResourcesFromRoot(resourceRoot).scripts; - - var _didWarn; - - { - if (currentProps) { - _didWarn = validateURLKeyedUpdatedProps( - pendingProps, - currentProps, - "script", - "src" - ); - } - } + if (typeof pendingProps.src === "string" && pendingProps.async === true) { + var scriptProps = pendingProps; - var src = pendingProps.src, - async = pendingProps.async; + var _key4 = getScriptKey(scriptProps.src); - if (async && typeof src === "string") { - var scriptRawProps = pendingProps; - - var _resource7 = scriptResources.get(src); - - if (_resource7) { - { - if (!_didWarn) { - var _latestProps3 = scriptPropsFromRawProps(scriptRawProps); + var scripts = getResourcesFromRoot(resourceRoot).hoistableScripts; - if (_resource7._dev_preload_props) { - adoptPreloadPropsForScript( - _latestProps3, - _resource7._dev_preload_props - ); - } - - validateScriptResourceDifference(_resource7.props, _latestProps3); - } - } - } else { - var _resourceProps3 = scriptPropsFromRawProps(scriptRawProps); + var _resource3 = scripts.get(_key4); - _resource7 = createScriptResource( - scriptResources, - resourceRoot, - src, - _resourceProps3 - ); + if (!_resource3) { + _resource3 = { + type: "script", + instance: null, + count: 0 + }; + scripts.set(_key4, _resource3); } - return _resource7; + return _resource3; } - return null; + return { + type: "void", + instance: null, + count: 0 + }; } default: { throw new Error( - 'getResource encountered a resource type it did not expect: "' + + 'getResource encountered a type it did not expect: "' + type + '". this is a bug in React.' ); @@ -10418,210 +9750,64 @@ function getResource(type, pendingProps, currentProps) { } } -function preloadPropsFromRawProps(rawBorrowedProps) { - // $FlowFixMe[prop-missing] - recommended fix is to use object spread operator - return assign({}, rawBorrowedProps); +function styleTagPropsFromRawProps(rawProps) { + return assign({}, rawProps, { + "data-href": rawProps.href, + "data-precedence": rawProps.precedence, + href: null, + precedence: null + }); } -function titlePropsFromRawProps(child, rawProps) { - var props = assign({}, rawProps); - - props.children = child; - return props; +function getStyleKey(href) { + var limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes(href); + return 'href="' + limitedEscapedHref + '"'; } -function stylePropsFromRawProps(rawProps) { - // $FlowFixMe[prop-missing] - recommended fix is to use object spread operator - var props = assign({}, rawProps); - - props["data-precedence"] = rawProps.precedence; - props.precedence = null; - return props; +function getStyleTagSelectorFromKey(key) { + return "style[data-" + key + "]"; } -function scriptPropsFromRawProps(rawProps) { - // $FlowFixMe[prop-missing] - recommended fix is to use object spread operator - var props = assign({}, rawProps); - - return props; -} // -------------------------------------- -// Resource Reconciliation -// -------------------------------------- - -function acquireResource(resource) { - switch (resource.type) { - case "base": - case "title": - case "link": - case "meta": { - return acquireHeadResource(resource); - } - - case "style": { - return acquireStyleResource(resource); - } - - case "script": { - return acquireScriptResource(resource); - } - - case "preload": { - return resource.instance; - } - - default: { - throw new Error( - 'acquireResource encountered a resource type it did not expect: "' + - resource.type + - '". this is a bug in React.' - ); - } - } +function getStylesheetSelectorFromKey(key) { + return 'link[rel="stylesheet"][' + key + "]"; } -function releaseResource(resource) { - switch (resource.type) { - case "link": - case "title": - case "meta": { - return releaseHeadResource(resource); - } - case "style": { - resource.count--; - return; - } - } +function getPreloadStylesheetSelectorFromKey(key) { + return 'link[rel="preload"][as="style"][' + key + "]"; } -function releaseHeadResource(resource) { - if (--resource.count === 0) { - // the instance will have existed since we acquired it - var instance = resource.instance; - var parent = instance.parentNode; - - if (parent) { - parent.removeChild(instance); - } - - resource.instance = null; - } +function stylesheetPropsFromRawProps(rawProps) { + return assign({}, rawProps, { + "data-precedence": rawProps.precedence, + precedence: null + }); } -function createResourceInstance(type, props, ownerDocument) { - var element = createElement(type, props, ownerDocument, HTML_NAMESPACE); - setInitialProperties(element, type, props); - markNodeAsResource(element); - return element; -} +function preloadStylesheet(ownerDocument, key, preloadProps) { + preloadPropsMap.set(key, preloadProps); -function createStyleResource(styleResources, root, href, precedence, props) { - { - if (styleResources.has(href)) { - error( - "createStyleResource was called when a style Resource matching the same href already exists. This is a bug in React." + if (!ownerDocument.querySelector(getStylesheetSelectorFromKey(key))) { + // There is no matching stylesheet instance in the Document. + // We will insert a preload now to kick off loading because + // we expect this stylesheet to commit + if ( + null === + ownerDocument.querySelector(getPreloadStylesheetSelectorFromKey(key)) + ) { + var preloadInstance = createElement( + "link", + preloadProps, + ownerDocument, + HTML_NAMESPACE ); + setInitialProperties(preloadInstance, "link", preloadProps); + markNodeAsResource(preloadInstance); + ownerDocument.head.appendChild(preloadInstance); } } - - var limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes(href); - var existingEl = root.querySelector( - 'link[rel="stylesheet"][href="' + limitedEscapedHref + '"]' - ); - var resource = { - type: "style", - count: 0, - href: href, - precedence: precedence, - props: props, - hint: null, - preloaded: false, - loaded: false, - error: false, - root: root, - instance: null - }; - styleResources.set(href, resource); - - if (existingEl) { - // If we have an existing element in the DOM we don't need to preload this resource nor can we - // adopt props from any preload that might exist already for this resource. We do need to try - // to reify the Resource loading state the best we can. - var loadingState = existingEl._p; - - if (loadingState) { - switch (loadingState.s) { - case "l": { - resource.loaded = true; - break; - } - - case "e": { - resource.error = true; - break; - } - - default: { - attachLoadListeners(existingEl, resource); - } - } - } else { - // This is unfortunately just an assumption. The rationale here is that stylesheets without - // a loading state must have been flushed in the shell and would have blocked until loading - // or error. we can't know afterwards which happened for all types of stylesheets (cross origin) - // for instance) and the techniques for determining if a sheet has loaded that we do have still - // fail if the sheet loaded zero rules. At the moment we are going to just opt to assume the - // sheet is loaded if it was flushed in the shell - resource.loaded = true; - } - } else { - var hint = preloadResources.get(href); - - if (hint) { - // $FlowFixMe[incompatible-type]: found when upgrading Flow - resource.hint = 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 - - var preloadProps = hint.props; - adoptPreloadPropsForStyle(resource.props, hint.props); - - { - resource._dev_preload_props = preloadProps; - } - } - } - - return resource; } -function adoptPreloadPropsForStyle(styleProps, preloadProps) { - if (styleProps.crossOrigin == null) - styleProps.crossOrigin = preloadProps.crossOrigin; - if (styleProps.referrerPolicy == null) - styleProps.referrerPolicy = preloadProps.referrerPolicy; - if (styleProps.title == null) styleProps.title = preloadProps.title; -} - -function immediatelyPreloadStyleResource(resource) { - // This function must be called synchronously after creating a styleResource otherwise it may - // violate assumptions around the existence of a preload. The reason it is extracted out is we - // don't always want to preload a style, in particular when we are going to synchronously insert - // that style. We confirm the style resource has no preload already and then construct it. If - // we wait and call this later it is possible a preload will already exist for this href - if (resource.loaded === false && resource.hint === null) { - var href = resource.href, - props = resource.props; - var preloadProps = preloadPropsFromStyleProps(props); - resource.hint = createPreloadResource( - getDocumentFromRoot(resource.root), - href, - preloadProps - ); - } -} - -function preloadPropsFromStyleProps(props) { +function preloadPropsFromStylesheet(props) { return { rel: "preload", as: "style", @@ -10634,398 +9820,161 @@ function preloadPropsFromStyleProps(props) { }; } -function createScriptResource(scriptResources, root, src, props) { - { - if (scriptResources.has(src)) { - error( - "createScriptResource was called when a script Resource matching the same src already exists. This is a bug in React." - ); - } - } - +function getScriptKey(src) { var limitedEscapedSrc = escapeSelectorAttributeValueInsideDoubleQuotes(src); - var existingEl = root.querySelector( - 'script[async][src="' + limitedEscapedSrc + '"]' - ); - var resource = { - type: "script", - src: src, - props: props, - root: root, - instance: existingEl || null - }; - scriptResources.set(src, resource); - - if (!existingEl) { - var hint = preloadResources.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 - var preloadProps = hint.props; - adoptPreloadPropsForScript(props, hint.props); - - { - resource._dev_preload_props = preloadProps; - } - } - } else { - markNodeAsResource(existingEl); - } - - return resource; -} - -function adoptPreloadPropsForScript(scriptProps, preloadProps) { - if (scriptProps.crossOrigin == null) - scriptProps.crossOrigin = preloadProps.crossOrigin; - if (scriptProps.referrerPolicy == null) - scriptProps.referrerPolicy = preloadProps.referrerPolicy; - if (scriptProps.integrity == null) - scriptProps.referrerPolicy = preloadProps.integrity; + return '[src="' + limitedEscapedSrc + '"]'; } -function createPreloadResource(ownerDocument, href, props) { - var limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes(href); - var element = ownerDocument.querySelector( - 'link[rel="preload"][href="' + limitedEscapedHref + '"]' - ); - - if (!element) { - element = createResourceInstance("link", props, ownerDocument); - insertResourceInstanceBefore(ownerDocument, element, null); - } else { - markNodeAsResource(element); - } - - return { - type: "preload", - href: href, - ownerDocument: ownerDocument, - props: props, - instance: element - }; -} +function getScriptSelectorFromKey(key) { + return "script[async]" + key; +} // -------------------------------------- +// Hoistable Resource Reconciliation +// -------------------------------------- -function acquireHeadResource(resource) { +function acquireResource(hoistableRoot, resource, props) { resource.count++; - var instance = resource.instance; - if (!instance) { - var props = resource.props, - root = resource.root, - type = resource.type; - - switch (type) { - case "title": { - var titles = root.querySelectorAll("title"); - - for (var i = 0; i < titles.length; i++) { - if (titles[i].textContent === props.children) { - instance = resource.instance = titles[i]; - markNodeAsResource(instance); - return instance; - } - } + if (resource.instance === null) { + switch (resource.type) { + case "style": { + var qualifiedProps = props; + var key = getStyleKey(qualifiedProps.href); // Attempt to hydrate instance from DOM - instance = resource.instance = createResourceInstance( - type, - props, - root + var instance = hoistableRoot.querySelector( + getStyleTagSelectorFromKey(key) ); - var firstTitle = titles[0]; - insertResourceInstanceBefore( - root, - instance, - firstTitle && firstTitle.namespaceURI !== SVG_NAMESPACE - ? firstTitle - : null - ); - break; - } - case "meta": { - var insertBefore = null; - var metaResource = resource; - var matcher = metaResource.matcher, - property = metaResource.property, - parentResource = metaResource.parentResource; - - if (parentResource && typeof property === "string") { - // This resoruce is a structured meta type with a parent. - // Instead of using the matcher we just traverse forward - // siblings of the parent instance until we find a match - // or exhaust. - var parent = parentResource.instance; - - if (parent) { - var node = null; - var nextNode = (insertBefore = parent.nextSibling); - - while ((node = nextNode)) { - nextNode = node.nextSibling; - - if (node.nodeName === "META") { - var meta = node; - var propertyAttr = meta.getAttribute("property"); - - if (typeof propertyAttr !== "string") { - continue; - } else if ( - propertyAttr === property && - meta.getAttribute("content") === props.content - ) { - resource.instance = meta; - markNodeAsResource(meta); - return meta; - } else if (property.startsWith(propertyAttr + ":")) { - // This meta starts a new instance of a parent structure for this meta type - // We need to halt our search here because even if we find a later match it - // is for a different parent element - break; - } - } - } - } - } else if ((instance = root.querySelector(matcher))) { + if (instance) { resource.instance = instance; - markNodeAsResource(instance); return instance; } - instance = resource.instance = createResourceInstance( - type, - props, - root + var styleProps = styleTagPropsFromRawProps(props); + instance = createElement( + "style", + styleProps, + hoistableRoot, + HTML_NAMESPACE ); - insertResourceInstanceBefore(root, instance, insertBefore); - break; + markNodeAsResource(instance); + setInitialProperties(instance, "style", styleProps); + insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot); + resource.instance = instance; + return instance; } - case "link": { - var linkProps = props; - var limitedEscapedRel = escapeSelectorAttributeValueInsideDoubleQuotes( - linkProps.rel - ); - var limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes( - linkProps.href - ); - var selector = - 'link[rel="' + - limitedEscapedRel + - '"][href="' + - limitedEscapedHref + - '"]'; + case "stylesheet": { + // This typing is enforce by `getResource`. If we change the logic + // there for what qualifies as a stylesheet resource we need to ensure + // this cast still makes sense; + var _qualifiedProps = props; - if (typeof linkProps.sizes === "string") { - var limitedEscapedSizes = - escapeSelectorAttributeValueInsideDoubleQuotes(linkProps.sizes); - selector += '[sizes="' + limitedEscapedSizes + '"]'; - } + var _key5 = getStyleKey(_qualifiedProps.href); // Attempt to hydrate instance from DOM + + var _instance2 = hoistableRoot.querySelector( + getStylesheetSelectorFromKey(_key5) + ); - if (typeof linkProps.media === "string") { - var limitedEscapedMedia = - escapeSelectorAttributeValueInsideDoubleQuotes(linkProps.media); - selector += '[media="' + limitedEscapedMedia + '"]'; + if (_instance2) { + resource.instance = _instance2; + return _instance2; } - var existingEl = root.querySelector(selector); + var stylesheetProps = stylesheetPropsFromRawProps(props); + var preloadProps = preloadPropsMap.get(_key5); - if (existingEl) { - instance = resource.instance = existingEl; - markNodeAsResource(instance); - return instance; - } + if (preloadProps) { + adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps); + } // Construct and insert a new instance - instance = resource.instance = createResourceInstance( - type, - props, - root + _instance2 = createElement( + "link", + stylesheetProps, + hoistableRoot, + HTML_NAMESPACE ); - insertResourceInstanceBefore(root, instance, null); - return instance; + markNodeAsResource(_instance2); + var linkInstance = _instance2; + linkInstance._p = new Promise(function (resolve, reject) { + linkInstance.onload = resolve; + linkInstance.onerror = reject; + }).then( + function () { + return (linkInstance._p.s = "l"); + }, + function () { + return (linkInstance._p.s = "e"); + } + ); + setInitialProperties(_instance2, "link", stylesheetProps); + insertStylesheet(_instance2, _qualifiedProps.precedence, hoistableRoot); + resource.instance = _instance2; + return _instance2; } - case "base": { - var baseResource = resource; - var _matcher2 = baseResource.matcher; - var base = root.querySelector(_matcher2); - - if (base) { - instance = resource.instance = base; - markNodeAsResource(instance); - } else { - instance = resource.instance = createResourceInstance( - type, - props, - root - ); - insertResourceInstanceBefore( - root, - instance, - root.querySelector("base") - ); - } + case "script": { + // This typing is enforce by `getResource`. If we change the logic + // there for what qualifies as a stylesheet resource we need to ensure + // this cast still makes sense; + var borrowedScriptProps = props; - return instance; - } + var _key6 = getScriptKey(borrowedScriptProps.src); // Attempt to hydrate instance from DOM - default: { - throw new Error( - 'acquireHeadResource encountered a resource type it did not expect: "' + - type + - '". This is a bug in React.' + var _instance3 = hoistableRoot.querySelector( + getScriptSelectorFromKey(_key6) ); - } - } - } - return instance; -} + if (_instance3) { + resource.instance = _instance3; + return _instance3; + } -function acquireStyleResource(resource) { - var instance = resource.instance; + var scriptProps = borrowedScriptProps; - if (!instance) { - var props = resource.props, - root = resource.root, - precedence = resource.precedence; - var limitedEscapedHref = escapeSelectorAttributeValueInsideDoubleQuotes( - props.href - ); - var existingEl = root.querySelector( - 'link[rel="stylesheet"][data-precedence][href="' + - limitedEscapedHref + - '"]' - ); + var _preloadProps3 = preloadPropsMap.get(_key6); - if (existingEl) { - instance = resource.instance = existingEl; - markNodeAsResource(instance); - resource.preloaded = true; - var loadingState = existingEl._p; - - if (loadingState) { - // if an existingEl is found there should always be a loadingState because if - // the resource was flushed in the head it should have already been found when - // the resource was first created. Still defensively we gate this - switch (loadingState.s) { - case "l": { - resource.loaded = true; - resource.error = false; - break; - } + if (_preloadProps3) { + scriptProps = assign({}, borrowedScriptProps); + adoptPreloadPropsForScript(scriptProps, _preloadProps3); + } // Construct and insert a new instance - case "e": { - resource.error = true; - break; - } - - default: { - attachLoadListeners(existingEl, resource); - } - } - } else { - resource.loaded = true; + _instance3 = createElement( + "script", + scriptProps, + hoistableRoot, + HTML_NAMESPACE + ); + markNodeAsResource(_instance3); + setInitialProperties(_instance3, "link", scriptProps); + getDocumentFromRoot(hoistableRoot).head.appendChild(_instance3); + resource.instance = _instance3; + return _instance3; } - } else { - instance = resource.instance = createResourceInstance( - "link", - resource.props, - getDocumentFromRoot(root) - ); - attachLoadListeners(instance, resource); - insertStyleInstance(instance, precedence, root); - } - } - resource.count++; - return instance; -} - -function acquireScriptResource(resource) { - var instance = resource.instance; - - if (!instance) { - var props = resource.props, - root = resource.root; - var limitedEscapedSrc = escapeSelectorAttributeValueInsideDoubleQuotes( - props.src - ); - var existingEl = root.querySelector( - 'script[async][src="' + limitedEscapedSrc + '"]' - ); + case "void": { + return null; + } - if (existingEl) { - instance = resource.instance = existingEl; - markNodeAsResource(instance); - } else { - instance = resource.instance = createResourceInstance( - "script", - resource.props, - getDocumentFromRoot(root) - ); - insertResourceInstanceBefore(getDocumentFromRoot(root), instance, null); + default: { + throw new Error( + 'acquireResource encountered a resource type it did not expect: "' + + resource.type + + '". this is a bug in React.' + ); + } } } - return instance; + return resource.instance; } - -function attachLoadListeners(instance, resource) { - var listeners = {}; - listeners.load = onResourceLoad.bind( - null, - instance, - resource, - listeners, - loadAndErrorEventListenerOptions - ); - listeners.error = onResourceError.bind( - null, - instance, - resource, - listeners, - loadAndErrorEventListenerOptions - ); - instance.addEventListener( - "load", - listeners.load, - loadAndErrorEventListenerOptions - ); - instance.addEventListener( - "error", - listeners.error, - loadAndErrorEventListenerOptions - ); -} - -var loadAndErrorEventListenerOptions = { - passive: true -}; - -function onResourceLoad(instance, resource, listeners, listenerOptions) { - resource.loaded = true; - resource.error = false; - - for (var event in listeners) { - instance.removeEventListener(event, listeners[event], listenerOptions); - } -} - -function onResourceError(instance, resource, listeners, listenerOptions) { - resource.loaded = false; - resource.error = true; - - for (var event in listeners) { - instance.removeEventListener(event, listeners[event], listenerOptions); - } +function releaseResource(resource) { + resource.count--; } -function insertStyleInstance(instance, precedence, root) { - var nodes = root.querySelectorAll('link[rel="stylesheet"][data-precedence]'); +function insertStylesheet(instance, precedence, root) { + var nodes = root.querySelectorAll( + 'link[rel="stylesheet"][data-precedence],style[data-precedence]' + ); var last = nodes.length ? nodes[nodes.length - 1] : null; var prior = last; @@ -11047,38 +9996,209 @@ function insertStyleInstance(instance, precedence, root) { prior.parentNode.insertBefore(instance, prior.nextSibling); } else { var parent = root.nodeType === DOCUMENT_NODE ? root.head : root; - - if (parent) { - parent.insertBefore(instance, parent.firstChild); - } else { - throw new Error( - "While attempting to insert a Resource, React expected the Document to contain" + - " a head element but it was not found." - ); - } + parent.insertBefore(instance, parent.firstChild); } } -function insertResourceInstanceBefore(ownerDocument, instance, before) { - { - if (instance.tagName === "LINK" && instance.rel === "stylesheet") { - error( - "insertResourceInstanceBefore was called with a stylesheet. Stylesheets must be" + - " inserted with insertStyleInstance instead. This is a bug in React." - ); - } +function adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps) { + if (stylesheetProps.crossOrigin == null) + stylesheetProps.crossOrigin = preloadProps.crossOrigin; + if (stylesheetProps.referrerPolicy == null) + stylesheetProps.referrerPolicy = preloadProps.referrerPolicy; + if (stylesheetProps.title == null) stylesheetProps.title = preloadProps.title; +} + +function adoptPreloadPropsForScript(scriptProps, preloadProps) { + if (scriptProps.crossOrigin == null) + scriptProps.crossOrigin = preloadProps.crossOrigin; + if (scriptProps.referrerPolicy == null) + scriptProps.referrerPolicy = preloadProps.referrerPolicy; + if (scriptProps.integrity == null) + scriptProps.referrerPolicy = preloadProps.integrity; +} // -------------------------------------- +// Hoistable Element Reconciliation +// -------------------------------------- + +function hydrateHoistable(hoistableRoot, type, props, internalInstanceHandle) { + var ownerDocument = getDocumentFromRoot(hoistableRoot); + var nodes = ownerDocument.getElementsByTagName(type); + var children = props.children; + var child, childString; + + if (Array.isArray(children)) { + child = children.length === 1 ? children[0] : null; + } else { + child = children; } - var parent = (before && before.parentNode) || ownerDocument.head; + if ( + typeof child !== "function" && + typeof child !== "symbol" && + child !== null && + child !== undefined + ) { + { + checkPropStringCoercion(child, "children"); + } - if (parent) { - parent.insertBefore(instance, before); + childString = "" + child; } else { - throw new Error( - "While attempting to insert a Resource, React expected the Document to contain" + - " a head element but it was not found." - ); + childString = ""; } + + nodeLoop: for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + + if ( + isMarkedResource(node) || + node.namespaceURI === SVG_NAMESPACE || + node.textContent !== childString + ) { + continue; + } + + var checkedAttributes = 0; + + for (var propName in props) { + var propValue = props[propName]; + + if (!props.hasOwnProperty(propName)) { + continue; + } + + switch (propName) { + // Reserved props will never have an attribute partner + case "children": + case "defaultValue": + case "dangerouslySetInnerHTML": + case "defaultChecked": + case "innerHTML": + case "suppressContentEditableWarning": + case "suppressHydrationWarning": + case "style": + // we advance to the next prop + continue; + // Name remapped props used by hoistable tag types + + case "className": { + { + checkAttributeStringCoercion(propValue, propName); + } + + if (node.getAttribute("class") !== "" + propValue) continue nodeLoop; + break; + } + + case "httpEquiv": { + { + checkAttributeStringCoercion(propValue, propName); + } + + if (node.getAttribute("http-equiv") !== "" + propValue) + continue nodeLoop; + break; + } + // Booleanish props used by hoistable tag types + + case "contentEditable": + case "draggable": + case "spellCheck": { + { + checkAttributeStringCoercion(propValue, propName); + } + + if (node.getAttribute(propName) !== "" + propValue) continue nodeLoop; + break; + } + // Boolean props used by hoistable tag types + + case "async": + case "defer": + case "disabled": + case "hidden": + case "noModule": + case "scoped": + case "itemScope": + if (propValue !== node.hasAttribute(propName)) continue nodeLoop; + break; + // The following properties are left out because they do not apply to + // the current set of hoistable types. They may have special handling + // requirements if they end up applying to a hoistable type in the future + // case 'acceptCharset': + // case 'value': + // case 'allowFullScreen': + // case 'autoFocus': + // case 'autoPlay': + // case 'controls': + // case 'default': + // case 'disablePictureInPicture': + // case 'disableRemotePlayback': + // case 'formNoValidate': + // case 'loop': + // case 'noValidate': + // case 'open': + // case 'playsInline': + // case 'readOnly': + // case 'required': + // case 'reversed': + // case 'seamless': + // case 'multiple': + // case 'selected': + // case 'capture': + // case 'download': + // case 'cols': + // case 'rows': + // case 'size': + // case 'span': + // case 'rowSpan': + // case 'start': + + default: + if (isAttributeNameSafe(propName)) { + var attributeName = propName; + if (propValue == null && node.hasAttribute(attributeName)) + continue nodeLoop; + + { + checkAttributeStringCoercion(propValue, attributeName); + } + + if (node.getAttribute(attributeName) !== "" + propValue) + continue nodeLoop; + } + } + + checkedAttributes++; + } + + if (node.attributes.length !== checkedAttributes) { + // We didn't match ever attribute so we abandon this node + continue nodeLoop; + } // We found a matching instance. We can return early after marking it + + markNodeAsResource(node); + return node; + } // There is no matching instance to hydrate, we create it now + + var instance = createElement(type, props, ownerDocument, HTML_NAMESPACE); + setInitialProperties(instance, type, props); + precacheFiberNode(internalInstanceHandle, instance); + markNodeAsResource(instance); + ownerDocument.head.insertBefore( + instance, + type === "title" ? ownerDocument.querySelector("head > title") : null + ); + return instance; +} +function mountHoistable(hoistableRoot, type, instance) { + var ownerDocument = getDocumentFromRoot(hoistableRoot); + ownerDocument.head.insertBefore( + instance, + type === "title" ? ownerDocument.querySelector("head > title") : null + ); +} +function unmountHoistable(instance) { + instance.parentNode.removeChild(instance); } // When passing user input into querySelector(All) the embedded string must not alter // the semantics of the query. This escape function is safe to use when we know the // provided value is going to be wrapped in double quotes as part of an attribute selector @@ -11195,6 +10315,24 @@ function resetAfterCommit(containerInfo) { eventsEnabled = null; selectionInformation = null; } +function createHoistableInstance( + type, + props, + rootContainerInstance, + internalInstanceHandle +) { + var domElement = createElement( + type, + props, + rootContainerInstance, + HTML_NAMESPACE + ); + precacheFiberNode(internalInstanceHandle, domElement); + updateFiberProps(domElement, props); + setInitialProperties(domElement, type, props); + markNodeAsResource(domElement); + return domElement; +} function createInstance( type, props, @@ -11563,17 +10701,10 @@ function clearContainer(container) { var nodeType = container.nodeType; if (nodeType === DOCUMENT_NODE) { - clearRootResources(container); clearContainerSparingly(container); } else if (nodeType === ELEMENT_NODE) { switch (container.nodeName) { - case "HEAD": { - // If we are clearing document.head as a container we are essentially clearing everything - // that was hoisted to the head and should forget the instances that will no longer be in the DOM - clearRootResources(container); // fall through to clear child contents - } - // eslint-disable-next-line-no-fallthrough - + case "HEAD": case "HTML": case "BODY": clearContainerSparingly(container); @@ -11731,7 +10862,6 @@ function getNextHydratable(node) { // developer on how to fix. case "TITLE": case "META": - case "BASE": case "HTML": case "HEAD": case "BODY": { @@ -12097,7 +11227,7 @@ function requestPostPaintCallback(callback) { }); }); } // ------------------- -function isHostResourceType(type, props, hostContext) { +function isHostHoistableType(type, props, hostContext) { var outsideHostContainerContext; var namespace; @@ -12110,31 +11240,27 @@ function isHostResourceType(type, props, hostContext) { } switch (type) { - case "base": - case "meta": { - return true; - } - + case "meta": case "title": { return namespace !== SVG_NAMESPACE; } - case "link": { - var onLoad = props.onLoad, - onError = props.onError; - - if (onLoad || onError) { + case "style": { + if ( + typeof props.precedence !== "string" || + typeof props.href !== "string" || + props.href === "" || + namespace === SVG_NAMESPACE + ) { { if (outsideHostContainerContext) { error( - "Cannot render a with onLoad or onError listeners outside the main document." + - " Try removing onLoad={...} and onError={...} or moving it into the root tag or" + - " somewhere in the ." - ); - } else if (namespace === SVG_NAMESPACE) { - error( - "Cannot render a with onLoad or onError listeners as a descendent of ." + - " Try removing onLoad={...} and onError={...} or moving it above the ancestor." + "Cannot render a or consider adding a `precedence="default"` and `href="some unique resource identifier"`, or move the '); + +function flushResourceInPreamble(resource) { + if ((resource.state & (Flushed | Blocked)) === NoState) { + var chunks = resource.chunks; + + for (var i = 0; i < chunks.length; i++) { + writeChunk(this, chunks[i]); + } + + resource.state |= FlushedInPreamble; + } +} + +function flushResourceLate(resource) { + if ((resource.state & Flushed) === NoState) { + var chunks = resource.chunks; + + for (var i = 0; i < chunks.length; i++) { + writeChunk(this, chunks[i]); + } + + resource.state |= FlushedLate; + } +} + +var didFlush = false; + +function flushUnblockedStyle(resource, key, set) { + var chunks = resource.chunks; + + if (resource.state & Flushed) { + // In theory this should never happen because we clear from the + // Set on flush but to ensure correct semantics we don't emit + // anything if we are in this state. + set.delete(resource); + } else if (resource.state & Blocked); + else { + didFlush = true; // We can emit this style or stylesheet as is. + + if (resource.type === "stylesheet") { + // We still need to encode stylesheet chunks + // because unlike most Hoistables and Resources we do not eagerly encode + // them during render. This is because if we flush late we have to send a + // different encoding and we don't want to encode multiple times + pushLinkImpl(chunks, resource.props); + } + + for (var i = 0; i < chunks.length; i++) { + writeChunk(this, chunks[i]); + } + + resource.state |= FlushedInPreamble; + set.delete(resource); + } +} + +function flushUnblockedStyles(set, precedence) { + didFlush = false; + set.forEach(flushUnblockedStyle, this); + + if (!didFlush) { + // if we did not flush anything for this precedence slot we emit + // an empty '); -function writeInitialResources( +function preloadLateStyles(set, precedence) { + set.forEach(preloadLateStyle, this); + set.clear(); +} // We don't bother reporting backpressure at the moment because we expect to +// flush the entire preamble in a single pass. This probably should be modified +// in the future to be backpressure sensitive but that requires a larger refactor +// of the flushing code in Fizz. + +function writePreamble( destination, resources, responseState, willFlushAllSegments ) { - // Write initially discovered resources after the shell completes + // This function must be called exactly once on every request if (!willFlushAllSegments && responseState.externalRuntimeConfig) { // If the root segment is incomplete due to suspended tasks // (e.g. willFlushAllSegments = false) and we are using data @@ -6143,206 +5878,184 @@ function writeInitialResources( as: "script", integrity: integrity }); - } // $FlowFixMe[missing-local-annot] + } + + var htmlChunks = responseState.htmlChunks; + var headChunks = responseState.headChunks; + var i = 0; // Emit open tags before Hoistables and Resources + + if (htmlChunks) { + // We have an to emit as part of the preamble + for (i = 0; i < htmlChunks.length; i++) { + writeChunk(destination, htmlChunks[i]); + } - function flushLinkResource(resource) { - if (!resource.flushed) { - pushLinkImpl(target, resource.props, responseState); - resource.flushed = true; + if (headChunks) { + for (i = 0; i < headChunks.length; i++) { + writeChunk(destination, headChunks[i]); + } + } else { + // We did not render a head but we emitted an so we emit one now + writeChunk(destination, startChunkForTag("head")); + writeChunk(destination, endOfStartTag); } + } else if (headChunks) { + // We do not have an but we do have a + for (i = 0; i < headChunks.length; i++) { + writeChunk(destination, headChunks[i]); + } + } // Emit high priority Hoistables + + var charsetChunks = responseState.charsetChunks; + + for (i = 0; i < charsetChunks.length; i++) { + writeChunk(destination, charsetChunks[i]); } - var target = []; - var 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; + charsetChunks.length = 0; + var preconnectChunks = responseState.preconnectChunks; - if (charset) { - pushSelfClosing(target, charset.props, "meta", responseState); - charset.flushed = true; - resources.charset = null; + for (i = 0; i < preconnectChunks.length; i++) { + writeChunk(destination, preconnectChunks[i]); } - bases.forEach(function (r) { - pushSelfClosing(target, r.props, "base", responseState); - r.flushed = true; - }); - bases.clear(); - preconnects.forEach(function (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(function (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(function (p, precedence) { - if (p.size) { - p.forEach(function (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(function (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(function (r) { - switch (r.type) { - case "title": { - pushTitleImpl(target, r.props, responseState); - break; - } + preconnectChunks.length = 0; + resources.fontPreloads.forEach(flushResourceInPreamble, destination); + resources.fontPreloads.clear(); // Flush unblocked stylesheets by precedence - case "meta": { - pushSelfClosing(target, r.props, "meta", responseState); - break; - } + resources.precedences.forEach(flushUnblockedStyles, destination); // Flush preloads for Blocked stylesheets - case "link": { - pushLinkImpl(target, r.props, responseState); - break; + resources.precedences.forEach(preloadBlockedStyles, destination); + resources.usedStylesheets.forEach(function (resource) { + var key = getResourceKey(resource.props.as, resource.props.href); + + if (resources.stylesMap.has(key)); + else { + var chunks = resource.chunks; + + for (i = 0; i < chunks.length; i++) { + writeChunk(destination, chunks[i]); } } - - r.flushed = true; }); - headResources.clear(); - var i; - var r = true; + resources.usedStylesheets.clear(); + resources.scripts.forEach(flushResourceInPreamble, destination); + resources.scripts.clear(); + resources.usedScripts.forEach(flushResourceInPreamble, destination); + resources.usedScripts.clear(); + resources.explicitStylesheetPreloads.forEach( + flushResourceInPreamble, + destination + ); + resources.explicitStylesheetPreloads.clear(); + resources.explicitScriptPreloads.forEach( + flushResourceInPreamble, + destination + ); + resources.explicitScriptPreloads.clear(); + resources.explicitOtherPreloads.forEach(flushResourceInPreamble, destination); + resources.explicitOtherPreloads.clear(); // Write embedding preloadChunks - for (i = 0; i < target.length - 1; i++) { - writeChunk(destination, target[i]); + var preloadChunks = responseState.preloadChunks; + + for (i = 0; i < preloadChunks.length; i++) { + writeChunk(destination, preloadChunks[i]); } - if (i < target.length) { - r = writeChunkAndReturn(destination, target[i]); + preloadChunks.length = 0; // Write embedding hoistableChunks + + var hoistableChunks = responseState.hoistableChunks; + + for (i = 0; i < hoistableChunks.length; i++) { + writeChunk(destination, hoistableChunks[i]); } - return r; -} -function writeImmediateResources(destination, resources, responseState) { - // $FlowFixMe[missing-local-annot] - function flushLinkResource(resource) { - if (!resource.flushed) { - pushLinkImpl(target, resource.props, responseState); - resource.flushed = true; - } - } - - var target = []; - var 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(function (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(function (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(function (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(function (r) { - switch (r.type) { - case "title": { - pushTitleImpl(target, r.props, responseState); - break; - } + hoistableChunks.length = 0; // Flush closing head if necessary - case "meta": { - pushSelfClosing(target, r.props, "meta", responseState); - break; - } + if (htmlChunks && headChunks === null) { + // We have an rendered but no rendered. We however inserted + // a up above so we need to emit the now. This is safe because + // if the main content contained the it would also have provided a + // . This means that all the content inside is either or + // invalid HTML + writeChunk(destination, endTag1); + writeChunk(destination, stringToChunk("head")); + writeChunk(destination, endTag2); + } +} // We don't bother reporting backpressure at the moment because we expect to +// flush the entire preamble in a single pass. This probably should be modified +// in the future to be backpressure sensitive but that requires a larger refactor +// of the flushing code in Fizz. - case "link": { - pushLinkImpl(target, r.props, responseState); - break; +function writeHoistables(destination, resources, responseState) { + var i = 0; // Emit high priority Hoistables + // We omit charsetChunks because we have already sent the shell and if it wasn't + // already sent it is too late now. + + var preconnectChunks = responseState.preconnectChunks; + + for (i = 0; i < preconnectChunks.length; i++) { + writeChunk(destination, preconnectChunks[i]); + } + + preconnectChunks.length = 0; + resources.fontPreloads.forEach(flushResourceLate, destination); + resources.fontPreloads.clear(); // Preload any stylesheets. these will emit in a render instruction that follows this + // but we want to kick off preloading as soon as possible + + resources.precedences.forEach(preloadLateStyles, destination); + resources.usedStylesheets.forEach(function (resource) { + var key = getResourceKey(resource.props.as, resource.props.href); + + if (resources.stylesMap.has(key)); + else { + var chunks = resource.chunks; + + for (i = 0; i < chunks.length; i++) { + writeChunk(destination, chunks[i]); } } - - r.flushed = true; }); - headResources.clear(); - var i; - var r = true; + resources.usedStylesheets.clear(); + resources.scripts.forEach(flushResourceLate, destination); + resources.scripts.clear(); + resources.usedScripts.forEach(flushResourceLate, destination); + resources.usedScripts.clear(); + resources.explicitStylesheetPreloads.forEach(flushResourceLate, destination); + resources.explicitStylesheetPreloads.clear(); + resources.explicitScriptPreloads.forEach(flushResourceLate, destination); + resources.explicitScriptPreloads.clear(); + resources.explicitOtherPreloads.forEach(flushResourceLate, destination); + resources.explicitOtherPreloads.clear(); // Write embedding preloadChunks + + var preloadChunks = responseState.preloadChunks; + + for (i = 0; i < preloadChunks.length; i++) { + writeChunk(destination, preloadChunks[i]); + } + + preloadChunks.length = 0; // Write embedding hoistableChunks - for (i = 0; i < target.length - 1; i++) { - writeChunk(destination, target[i]); + var hoistableChunks = responseState.hoistableChunks; + + for (i = 0; i < hoistableChunks.length; i++) { + writeChunk(destination, hoistableChunks[i]); } - if (i < target.length) { - r = writeChunkAndReturn(destination, target[i]); + hoistableChunks.length = 0; +} +function writePostamble(destination, responseState) { + if (responseState.hasBody) { + writeChunk(destination, endTag1); + writeChunk(destination, stringToChunk("body")); + writeChunk(destination, endTag2); } - return r; + if (responseState.htmlChunks) { + writeChunk(destination, endTag1); + writeChunk(destination, stringToChunk("html")); + writeChunk(destination, endTag2); + } } function hasStyleResourceDependencies(boundaryResources) { @@ -6356,7 +6069,7 @@ function hasStyleResourceDependencies(boundaryResources) { if (!resource) break; // If every style Resource flushed in the shell we do not need to send // any dependencies - if (!resource.inShell) { + if ((resource.state & FlushedInPreamble) === NoState) { return true; } } @@ -6375,31 +6088,236 @@ function writeStyleResourceDependenciesInJS(destination, boundaryResources) { writeChunk(destination, arrayFirstOpenBracket); var nextArrayOpenBrackChunk = arrayFirstOpenBracket; boundaryResources.forEach(function (resource) { - if (resource.inShell); - else if (resource.flushed) { + if (resource.state & FlushedInPreamble); + else if (resource.state & Flushed) { + // We only need to emit the href because this resource flushed in an earlier + // boundary already which encoded the attributes necessary to construct + // the resource instance on the client. + writeChunk(destination, nextArrayOpenBrackChunk); + writeStyleResourceDependencyHrefOnlyInJS( + destination, + resource.type === "style" + ? resource.props["data-href"] + : resource.props.href + ); + writeChunk(destination, arrayCloseBracket); + nextArrayOpenBrackChunk = arraySubsequentOpenBracket; + } else if (resource.type === "stylesheet") { + // We need to emit the whole resource for insertion on the client + writeChunk(destination, nextArrayOpenBrackChunk); + writeStyleResourceDependencyInJS( + destination, + resource.props.href, + resource.props["data-precedence"], + resource.props + ); + writeChunk(destination, arrayCloseBracket); + nextArrayOpenBrackChunk = arraySubsequentOpenBracket; + resource.state |= FlushedLate; + } + }); + writeChunk(destination, arrayCloseBracket); +} +/* Helper functions */ + +function writeStyleResourceDependencyHrefOnlyInJS(destination, href) { + // We should actually enforce this earlier when the resource is created but for + // now we make sure we are actually dealing with a string here. + { + checkAttributeStringCoercion(href, "href"); + } + + var coercedHref = "" + href; + writeChunk( + destination, + stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)) + ); +} + +function writeStyleResourceDependencyInJS( + destination, + href, + precedence, + props +) { + { + checkAttributeStringCoercion(href, "href"); + } + + var coercedHref = "" + href; + sanitizeURL(coercedHref); + writeChunk( + destination, + stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)) + ); + + { + checkAttributeStringCoercion(precedence, "precedence"); + } + + var coercedPrecedence = "" + precedence; + writeChunk(destination, arrayInterstitial); + writeChunk( + destination, + stringToChunk(escapeJSObjectForInstructionScripts(coercedPrecedence)) + ); + + for (var propKey in props) { + if (hasOwnProperty.call(props, propKey)) { + var 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: + writeStyleResourceAttributeInJS(destination, propKey, propValue); + break; + } + } + } + + return null; +} + +function writeStyleResourceAttributeInJS(destination, name, value) { + // not null or undefined + var attributeName = name.toLowerCase(); + var 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": { + { + checkAttributeStringCoercion(value, attributeName); + } + + attributeValue = "" + value; + sanitizeURL(attributeValue); + 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; + } + + { + checkAttributeStringCoercion(value, attributeName); + } + + attributeValue = "" + value; + writeChunk(destination, arrayInterstitial); + writeChunk( + destination, + stringToChunk(escapeJSObjectForInstructionScripts(attributeName)) + ); + writeChunk(destination, arrayInterstitial); + writeChunk( + destination, + stringToChunk(escapeJSObjectForInstructionScripts(attributeValue)) + ); +} // This function writes a 2D array of strings to be embedded in an attribute +// value and read with JSON.parse in ReactDOMServerExternalRuntime.js +// E.g. +// [["JSON_escaped_string1", "JSON_escaped_string2"]] + +function writeStyleResourceDependenciesInAttr(destination, boundaryResources) { + writeChunk(destination, arrayFirstOpenBracket); + var nextArrayOpenBrackChunk = arrayFirstOpenBracket; + boundaryResources.forEach(function (resource) { + if (resource.state & FlushedInPreamble); + else if (resource.state & Flushed) { + // We only need to emit the href because this resource flushed in an earlier + // boundary already which encoded the attributes necessary to construct + // the resource instance on the client. writeChunk(destination, nextArrayOpenBrackChunk); - writeStyleResourceDependencyHrefOnlyInJS(destination, resource.href); + writeStyleResourceDependencyHrefOnlyInAttr( + destination, + resource.type === "style" + ? resource.props["data-href"] + : resource.props.href + ); writeChunk(destination, arrayCloseBracket); nextArrayOpenBrackChunk = arraySubsequentOpenBracket; - } else { + } else if (resource.type === "stylesheet") { + // We need to emit the whole resource for insertion on the client writeChunk(destination, nextArrayOpenBrackChunk); - writeStyleResourceDependencyInJS( + writeStyleResourceDependencyInAttr( destination, - resource.href, - resource.precedence, + resource.props.href, + resource.props["data-precedence"], resource.props ); writeChunk(destination, arrayCloseBracket); nextArrayOpenBrackChunk = arraySubsequentOpenBracket; - resource.flushed = true; - resource.hint.flushed = true; + resource.state |= FlushedLate; } }); writeChunk(destination, arrayCloseBracket); } /* Helper functions */ -function writeStyleResourceDependencyHrefOnlyInJS(destination, href) { +function writeStyleResourceDependencyHrefOnlyInAttr(destination, href) { // We should actually enforce this earlier when the resource is created but for // now we make sure we are actually dealing with a string here. { @@ -6409,11 +6327,11 @@ function writeStyleResourceDependencyHrefOnlyInJS(destination, href) { var coercedHref = "" + href; writeChunk( destination, - stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)) + stringToChunk(escapeTextForBrowser(JSON.stringify(coercedHref))) ); } -function writeStyleResourceDependencyInJS( +function writeStyleResourceDependencyInAttr( destination, href, precedence, @@ -6427,7 +6345,7 @@ function writeStyleResourceDependencyInJS( sanitizeURL(coercedHref); writeChunk( destination, - stringToChunk(escapeJSObjectForInstructionScripts(coercedHref)) + stringToChunk(escapeTextForBrowser(JSON.stringify(coercedHref))) ); { @@ -6438,7 +6356,7 @@ function writeStyleResourceDependencyInJS( writeChunk(destination, arrayInterstitial); writeChunk( destination, - stringToChunk(escapeJSObjectForInstructionScripts(coercedPrecedence)) + stringToChunk(escapeTextForBrowser(JSON.stringify(coercedPrecedence))) ); for (var propKey in props) { @@ -6467,7 +6385,7 @@ function writeStyleResourceDependencyInJS( // eslint-disable-next-line-no-fallthrough default: - writeStyleResourceAttributeInJS(destination, propKey, propValue); + writeStyleResourceAttributeInAttr(destination, propKey, propValue); break; } } @@ -6476,7 +6394,7 @@ function writeStyleResourceDependencyInJS( return null; } -function writeStyleResourceAttributeInJS(destination, name, value) { +function writeStyleResourceAttributeInAttr(destination, name, value) { // not null or undefined var attributeName = name.toLowerCase(); var attributeValue; @@ -6548,202 +6466,655 @@ function writeStyleResourceAttributeInJS(destination, name, value) { writeChunk(destination, arrayInterstitial); writeChunk( destination, - stringToChunk(escapeJSObjectForInstructionScripts(attributeName)) + stringToChunk(escapeTextForBrowser(JSON.stringify(attributeName))) ); writeChunk(destination, arrayInterstitial); writeChunk( destination, - stringToChunk(escapeJSObjectForInstructionScripts(attributeValue)) + stringToChunk(escapeTextForBrowser(JSON.stringify(attributeValue))) ); -} // This function writes a 2D array of strings to be embedded in an attribute -// value and read with JSON.parse in ReactDOMServerExternalRuntime.js -// E.g. -// [["JSON_escaped_string1", "JSON_escaped_string2"]] +} +/** + * Resources + */ -function writeStyleResourceDependenciesInAttr(destination, boundaryResources) { - writeChunk(destination, arrayFirstOpenBracket); - var nextArrayOpenBrackChunk = arrayFirstOpenBracket; - boundaryResources.forEach(function (resource) { - if (resource.inShell); - else if (resource.flushed) { - writeChunk(destination, nextArrayOpenBrackChunk); - writeStyleResourceDependencyHrefOnlyInAttr(destination, resource.href); - writeChunk(destination, arrayCloseBracket); - nextArrayOpenBrackChunk = arraySubsequentOpenBracket; - } else { - writeChunk(destination, nextArrayOpenBrackChunk); - writeStyleResourceDependencyInAttr( - destination, - resource.href, - resource.precedence, - resource.props +var NoState = + /* */ + 0; // These tags indicate whether the Resource was flushed and in which phase + +var FlushedInPreamble = + /* */ + 1; +var FlushedLate = + /* */ + 2; +var Flushed = + /* */ + 3; // This tag indicates whether this Resource is blocked from flushing. +// This currently is only used with stylesheets that are blocked by a Boundary + +var Blocked = + /* */ + 4; // This tag indicates whether this Resource has been preloaded. +// This generally only makes sense for Resources other than PreloadResource + +var PreloadFlushed = + /* */ + 8; // Dev extensions. +// Stylesheets and Scripts rendered with jsx +// Preloads, Stylesheets, and Scripts from ReactDOM.preload or ReactDOM.preinit +// Preloads created for normal components we rendered but know we can preload early such as +// sync Scripts and stylesheets without precedence or with onLoad/onError handlers +// @TODO add bootstrap script to implicit preloads + +function createResources() { + return { + // persistent + preloadsMap: new Map(), + stylesMap: new Map(), + scriptsMap: new Map(), + // cleared on flush + fontPreloads: new Set(), + // usedImagePreloads: new Set(), + precedences: new Map(), + usedStylesheets: new Set(), + scripts: new Set(), + usedScripts: new Set(), + explicitStylesheetPreloads: new Set(), + // explicitImagePreloads: new Set(), + explicitScriptPreloads: new Set(), + explicitOtherPreloads: new Set(), + // like a module global for currently rendering boundary + boundaryResources: null + }; +} +function createBoundaryResources() { + return new Set(); +} +function setCurrentlyRenderingBoundaryResourcesTarget( + resources, + boundaryResources +) { + resources.boundaryResources = boundaryResources; +} + +function getResourceKey(as, href) { + return "[" + as + "]" + href; +} + +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; + } + + var resources = currentResources; + + { + if (typeof href !== "string" || !href) { + error( + "ReactDOM.preload(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.", + getValueDescriptorExpectingObjectForWarning(href) ); - writeChunk(destination, arrayCloseBracket); - nextArrayOpenBrackChunk = arraySubsequentOpenBracket; - resource.flushed = true; - resource.hint.flushed = true; + } else if (options == null || typeof options !== "object") { + error( + "ReactDOM.preload(): Expected the `options` argument (second) to be an object with an `as` property describing the type of resource to be preloaded but encountered %s instead.", + getValueDescriptorExpectingEnumForWarning(options) + ); + } else if (typeof options.as !== "string") { + error( + 'ReactDOM.preload(): Expected the `as` property in the `options` argument (second) to contain a string value describing the type of resource to be preloaded but encountered %s instead. Values that are valid in for the `as` attribute of a `` tag are valid here.', + getValueDescriptorExpectingEnumForWarning(options.as) + ); + } + } + + if ( + typeof href === "string" && + href && + typeof options === "object" && + options !== null && + typeof options.as === "string" + ) { + var as = options.as; + var key = getResourceKey(as, href); + var resource = resources.preloadsMap.get(key); + + { + var devResource = getAsResourceDEV(resource); + + if (devResource) { + switch (devResource.__provenance) { + case "preload": { + var differenceDescription = describeDifferencesForPreloads( + options, + devResource.__originalOptions + ); + + if (differenceDescription) { + error( + 'ReactDOM.preload(): The options provided conflict with another call to `ReactDOM.preload("%s", { as: "%s", ...})`.' + + " React will always use the options it first encounters when preloading a resource for a given `href` and `as` type, and any later options will be ignored if different." + + " Try updating all calls to `ReactDOM.preload()` with the same `href` and `as` type to use the same options, or eliminate one of the calls.%s", + href, + as, + differenceDescription + ); + } + + break; + } + + case "implicit": { + var _differenceDescription3 = + describeDifferencesForPreloadOverImplicitPreload( + options, + devResource.__impliedProps + ); + + if (_differenceDescription3) { + var elementDescription = + as === "style" + ? '' + : as === "script" + ? "