diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 7228a6b25d..ddd7b88823 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -93c10dfa6b0848c12189b773b59c77d74cad2a1a +d4f58c3b8123f1654ca1b06692c3165928bef8df diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js index 8a7054cad2..1bc735e5f4 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-f87ae691"; +var ReactVersion = "18.3.0-www-modern-abf2779e"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js index 45ef05d919..364f12aed2 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-600fa423"; +var ReactVersion = "18.3.0-www-classic-3bfe2b4e"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -5432,6 +5432,165 @@ function checkPropStringCoercion(value, propName) { } } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -5490,6 +5649,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -5909,6 +6079,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5972,6 +6149,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -6045,6 +6234,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -6634,7 +6836,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6647,6 +6849,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -6707,6 +6910,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -6735,11 +6963,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -7075,163 +7329,6 @@ function warnAboutMultipleRenderersDEV(mutableSource) { } } // Eager reads the version of a mutable source and stores it on the root. -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -24132,6 +24229,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -24777,15 +24875,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index 94aac601f0..7b253024c5 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-ecb0bb1c"; +var ReactVersion = "18.3.0-www-modern-4c8eb7b9"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -5188,6 +5188,165 @@ function checkPropStringCoercion(value, propName) { } } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -5246,6 +5405,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -5665,6 +5835,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5728,6 +5905,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5801,6 +5990,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -6390,7 +6592,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6403,6 +6605,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -6463,6 +6666,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -6491,11 +6719,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -6831,163 +7085,6 @@ function warnAboutMultipleRenderersDEV(mutableSource) { } } // Eager reads the version of a mutable source and stores it on the root. -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -23792,6 +23889,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -24437,15 +24535,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, diff --git a/compiled/facebook-www/ReactART-prod.classic.js b/compiled/facebook-www/ReactART-prod.classic.js index fbbb1e6567..57f7b37859 100644 --- a/compiled/facebook-www/ReactART-prod.classic.js +++ b/compiled/facebook-www/ReactART-prod.classic.js @@ -1417,6 +1417,62 @@ function describeFiber(fiber) { return ""; } } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -1653,6 +1709,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -1686,6 +1744,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -1738,6 +1803,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -1906,7 +1979,7 @@ function createChildReconciler(shouldTrackSideEffects) { }); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -2051,6 +2124,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -2072,6 +2152,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -2155,55 +2251,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionSecondary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -8080,6 +8128,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -8331,9 +8381,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -8347,6 +8395,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -9791,19 +9841,19 @@ var slice = Array.prototype.slice, }; return Text; })(React.Component), - devToolsConfig$jscomp$inline_1129 = { + devToolsConfig$jscomp$inline_1128 = { findFiberByHostInstance: function () { return null; }, bundleType: 0, - version: "18.3.0-www-classic-4b2fd9fd", + version: "18.3.0-www-classic-812a2cd2", rendererPackageName: "react-art" }; -var internals$jscomp$inline_1302 = { - bundleType: devToolsConfig$jscomp$inline_1129.bundleType, - version: devToolsConfig$jscomp$inline_1129.version, - rendererPackageName: devToolsConfig$jscomp$inline_1129.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1129.rendererConfig, +var internals$jscomp$inline_1303 = { + bundleType: devToolsConfig$jscomp$inline_1128.bundleType, + version: devToolsConfig$jscomp$inline_1128.version, + rendererPackageName: devToolsConfig$jscomp$inline_1128.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1128.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -9820,26 +9870,26 @@ var internals$jscomp$inline_1302 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1129.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1128.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-classic-4b2fd9fd" + reconcilerVersion: "18.3.0-www-classic-812a2cd2" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1303 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1304 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1303.isDisabled && - hook$jscomp$inline_1303.supportsFiber + !hook$jscomp$inline_1304.isDisabled && + hook$jscomp$inline_1304.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1303.inject( - internals$jscomp$inline_1302 + (rendererID = hook$jscomp$inline_1304.inject( + internals$jscomp$inline_1303 )), - (injectedHook = hook$jscomp$inline_1303); + (injectedHook = hook$jscomp$inline_1304); } catch (err) {} } var Path = Mode$1.Path; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index ba2b1741ab..0869eeefa6 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -1223,6 +1223,62 @@ function describeFiber(fiber) { return ""; } } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -1459,6 +1515,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -1492,6 +1550,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -1544,6 +1609,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -1712,7 +1785,7 @@ function createChildReconciler(shouldTrackSideEffects) { }); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -1857,6 +1930,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -1878,6 +1958,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -1961,55 +2057,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionSecondary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -7812,6 +7860,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -8063,9 +8113,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -8079,6 +8127,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -9456,19 +9506,19 @@ var slice = Array.prototype.slice, }; return Text; })(React.Component), - devToolsConfig$jscomp$inline_1109 = { + devToolsConfig$jscomp$inline_1108 = { findFiberByHostInstance: function () { return null; }, bundleType: 0, - version: "18.3.0-www-modern-b4fc501b", + version: "18.3.0-www-modern-a5ab7ceb", rendererPackageName: "react-art" }; -var internals$jscomp$inline_1282 = { - bundleType: devToolsConfig$jscomp$inline_1109.bundleType, - version: devToolsConfig$jscomp$inline_1109.version, - rendererPackageName: devToolsConfig$jscomp$inline_1109.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1109.rendererConfig, +var internals$jscomp$inline_1283 = { + bundleType: devToolsConfig$jscomp$inline_1108.bundleType, + version: devToolsConfig$jscomp$inline_1108.version, + rendererPackageName: devToolsConfig$jscomp$inline_1108.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1108.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -9485,26 +9535,26 @@ var internals$jscomp$inline_1282 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1109.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1108.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-b4fc501b" + reconcilerVersion: "18.3.0-www-modern-a5ab7ceb" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1283 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1284 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1283.isDisabled && - hook$jscomp$inline_1283.supportsFiber + !hook$jscomp$inline_1284.isDisabled && + hook$jscomp$inline_1284.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1283.inject( - internals$jscomp$inline_1282 + (rendererID = hook$jscomp$inline_1284.inject( + internals$jscomp$inline_1283 )), - (injectedHook = hook$jscomp$inline_1283); + (injectedHook = hook$jscomp$inline_1284); } catch (err) {} } var Path = Mode$1.Path; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index 13da6c872a..9dc72cdb6e 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -19345,6 +19345,165 @@ var ReactStrictModeWarnings = { }; } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -19403,6 +19562,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -19822,6 +19992,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -19885,6 +20062,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -19958,6 +20147,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -20577,7 +20779,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -20590,6 +20792,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -20650,6 +20853,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -20678,11 +20906,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -21033,163 +21287,6 @@ function registerMutableSourceForHydration(root, mutableSource) { } } -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -38989,6 +39086,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -39636,15 +39734,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, @@ -42441,7 +42538,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-fce88849"; +var ReactVersion = "18.3.0-www-classic-0d360851"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOM-dev.modern.js b/compiled/facebook-www/ReactDOM-dev.modern.js index 9d95a1aae0..2dcc41ab5d 100644 --- a/compiled/facebook-www/ReactDOM-dev.modern.js +++ b/compiled/facebook-www/ReactDOM-dev.modern.js @@ -19045,6 +19045,165 @@ var ReactStrictModeWarnings = { }; } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -19103,6 +19262,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -19522,6 +19692,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -19585,6 +19762,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -19658,6 +19847,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -20277,7 +20479,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -20290,6 +20492,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -20350,6 +20553,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -20378,11 +20606,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -20733,163 +20987,6 @@ function registerMutableSourceForHydration(root, mutableSource) { } } -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -38593,6 +38690,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -39240,15 +39338,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, @@ -42045,7 +42142,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-b4fc501b"; +var ReactVersion = "18.3.0-www-modern-a5ab7ceb"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOM-prod.classic.js b/compiled/facebook-www/ReactDOM-prod.classic.js index 3bd82bbfa1..1a3504411b 100644 --- a/compiled/facebook-www/ReactDOM-prod.classic.js +++ b/compiled/facebook-www/ReactDOM-prod.classic.js @@ -5833,6 +5833,62 @@ function commitCallbacks(updateQueue, context) { ) callCallback(callbacks[updateQueue], context); } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -6069,6 +6125,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6102,6 +6160,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6154,6 +6219,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6332,7 +6405,7 @@ function createChildReconciler(shouldTrackSideEffects) { isHydrating && pushTreeFork(returnFiber, newIdx); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6477,6 +6550,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -6498,6 +6578,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -6585,55 +6681,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionPrimary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -13388,6 +13436,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -13647,9 +13697,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -13663,6 +13711,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -15619,17 +15669,17 @@ Internals.Events = [ restoreStateIfNeeded, batchedUpdates ]; -var devToolsConfig$jscomp$inline_1762 = { +var devToolsConfig$jscomp$inline_1761 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-classic-f123ce48", + version: "18.3.0-www-classic-596b7b05", rendererPackageName: "react-dom" }; -var internals$jscomp$inline_2155 = { - bundleType: devToolsConfig$jscomp$inline_1762.bundleType, - version: devToolsConfig$jscomp$inline_1762.version, - rendererPackageName: devToolsConfig$jscomp$inline_1762.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1762.rendererConfig, +var internals$jscomp$inline_2156 = { + bundleType: devToolsConfig$jscomp$inline_1761.bundleType, + version: devToolsConfig$jscomp$inline_1761.version, + rendererPackageName: devToolsConfig$jscomp$inline_1761.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1761.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -15645,26 +15695,26 @@ var internals$jscomp$inline_2155 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1762.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1761.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-classic-f123ce48" + reconcilerVersion: "18.3.0-www-classic-596b7b05" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_2156 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_2157 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_2156.isDisabled && - hook$jscomp$inline_2156.supportsFiber + !hook$jscomp$inline_2157.isDisabled && + hook$jscomp$inline_2157.supportsFiber ) try { - (rendererID = hook$jscomp$inline_2156.inject( - internals$jscomp$inline_2155 + (rendererID = hook$jscomp$inline_2157.inject( + internals$jscomp$inline_2156 )), - (injectedHook = hook$jscomp$inline_2156); + (injectedHook = hook$jscomp$inline_2157); } catch (err) {} } assign(Internals, { @@ -15908,4 +15958,4 @@ exports.unstable_renderSubtreeIntoContainer = function ( ); }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-www-classic-f123ce48"; +exports.version = "18.3.0-www-classic-596b7b05"; diff --git a/compiled/facebook-www/ReactDOM-prod.modern.js b/compiled/facebook-www/ReactDOM-prod.modern.js index 8097d605c6..44fdf13ce7 100644 --- a/compiled/facebook-www/ReactDOM-prod.modern.js +++ b/compiled/facebook-www/ReactDOM-prod.modern.js @@ -5639,6 +5639,62 @@ function commitCallbacks(updateQueue, context) { ) callCallback(callbacks[updateQueue], context); } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -5875,6 +5931,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -5908,6 +5966,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -5960,6 +6025,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6138,7 +6211,7 @@ function createChildReconciler(shouldTrackSideEffects) { isHydrating && pushTreeFork(returnFiber, newIdx); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6283,6 +6356,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -6304,6 +6384,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -6391,55 +6487,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionPrimary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -13133,6 +13181,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -13392,9 +13442,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -13408,6 +13456,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -15146,17 +15196,17 @@ Internals.Events = [ restoreStateIfNeeded, batchedUpdates ]; -var devToolsConfig$jscomp$inline_1721 = { +var devToolsConfig$jscomp$inline_1720 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-modern-78c5facb", + version: "18.3.0-www-modern-05aee9c9", rendererPackageName: "react-dom" }; -var internals$jscomp$inline_2119 = { - bundleType: devToolsConfig$jscomp$inline_1721.bundleType, - version: devToolsConfig$jscomp$inline_1721.version, - rendererPackageName: devToolsConfig$jscomp$inline_1721.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1721.rendererConfig, +var internals$jscomp$inline_2120 = { + bundleType: devToolsConfig$jscomp$inline_1720.bundleType, + version: devToolsConfig$jscomp$inline_1720.version, + rendererPackageName: devToolsConfig$jscomp$inline_1720.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1720.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -15173,26 +15223,26 @@ var internals$jscomp$inline_2119 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1721.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1720.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-78c5facb" + reconcilerVersion: "18.3.0-www-modern-05aee9c9" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_2120 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_2121 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_2120.isDisabled && - hook$jscomp$inline_2120.supportsFiber + !hook$jscomp$inline_2121.isDisabled && + hook$jscomp$inline_2121.supportsFiber ) try { - (rendererID = hook$jscomp$inline_2120.inject( - internals$jscomp$inline_2119 + (rendererID = hook$jscomp$inline_2121.inject( + internals$jscomp$inline_2120 )), - (injectedHook = hook$jscomp$inline_2120); + (injectedHook = hook$jscomp$inline_2121); } catch (err) {} } exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; @@ -15365,4 +15415,4 @@ exports.unstable_flushControlled = function (fn) { } }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-www-modern-78c5facb"; +exports.version = "18.3.0-www-modern-05aee9c9"; diff --git a/compiled/facebook-www/ReactDOM-profiling.classic.js b/compiled/facebook-www/ReactDOM-profiling.classic.js index 7b8512d9f8..fdd506363b 100644 --- a/compiled/facebook-www/ReactDOM-profiling.classic.js +++ b/compiled/facebook-www/ReactDOM-profiling.classic.js @@ -5981,6 +5981,62 @@ function commitCallbacks(updateQueue, context) { ) callCallback(callbacks[updateQueue], context); } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -6217,6 +6273,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6250,6 +6308,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6302,6 +6367,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6480,7 +6553,7 @@ function createChildReconciler(shouldTrackSideEffects) { isHydrating && pushTreeFork(returnFiber, newIdx); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6625,6 +6698,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -6646,6 +6726,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -6733,55 +6829,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionPrimary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -13998,6 +14046,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -14324,9 +14374,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -14341,6 +14389,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -16396,10 +16446,10 @@ Internals.Events = [ restoreStateIfNeeded, batchedUpdates ]; -var devToolsConfig$jscomp$inline_1842 = { +var devToolsConfig$jscomp$inline_1841 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-classic-68e013e5", + version: "18.3.0-www-classic-38b85b85", rendererPackageName: "react-dom" }; (function (internals) { @@ -16417,10 +16467,10 @@ var devToolsConfig$jscomp$inline_1842 = { } catch (err) {} return hook.checkDCE ? !0 : !1; })({ - bundleType: devToolsConfig$jscomp$inline_1842.bundleType, - version: devToolsConfig$jscomp$inline_1842.version, - rendererPackageName: devToolsConfig$jscomp$inline_1842.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1842.rendererConfig, + bundleType: devToolsConfig$jscomp$inline_1841.bundleType, + version: devToolsConfig$jscomp$inline_1841.version, + rendererPackageName: devToolsConfig$jscomp$inline_1841.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1841.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -16436,14 +16486,14 @@ var devToolsConfig$jscomp$inline_1842 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1842.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1841.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-classic-68e013e5" + reconcilerVersion: "18.3.0-www-classic-38b85b85" }); assign(Internals, { ReactBrowserEventEmitter: { @@ -16686,7 +16736,7 @@ exports.unstable_renderSubtreeIntoContainer = function ( ); }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-www-classic-68e013e5"; +exports.version = "18.3.0-www-classic-38b85b85"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/ReactDOM-profiling.modern.js b/compiled/facebook-www/ReactDOM-profiling.modern.js index b77f42778c..c7f0539b7d 100644 --- a/compiled/facebook-www/ReactDOM-profiling.modern.js +++ b/compiled/facebook-www/ReactDOM-profiling.modern.js @@ -5783,6 +5783,62 @@ function commitCallbacks(updateQueue, context) { ) callCallback(callbacks[updateQueue], context); } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -6019,6 +6075,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6052,6 +6110,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6104,6 +6169,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -6282,7 +6355,7 @@ function createChildReconciler(shouldTrackSideEffects) { isHydrating && pushTreeFork(returnFiber, newIdx); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6427,6 +6500,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -6448,6 +6528,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -6535,55 +6631,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionPrimary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -13733,6 +13781,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -14059,9 +14109,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -14076,6 +14124,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -15913,10 +15963,10 @@ Internals.Events = [ restoreStateIfNeeded, batchedUpdates ]; -var devToolsConfig$jscomp$inline_1801 = { +var devToolsConfig$jscomp$inline_1800 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-modern-ab6eebf8", + version: "18.3.0-www-modern-f86b50a5", rendererPackageName: "react-dom" }; (function (internals) { @@ -15934,10 +15984,10 @@ var devToolsConfig$jscomp$inline_1801 = { } catch (err) {} return hook.checkDCE ? !0 : !1; })({ - bundleType: devToolsConfig$jscomp$inline_1801.bundleType, - version: devToolsConfig$jscomp$inline_1801.version, - rendererPackageName: devToolsConfig$jscomp$inline_1801.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1801.rendererConfig, + bundleType: devToolsConfig$jscomp$inline_1800.bundleType, + version: devToolsConfig$jscomp$inline_1800.version, + rendererPackageName: devToolsConfig$jscomp$inline_1800.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1800.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -15954,14 +16004,14 @@ var devToolsConfig$jscomp$inline_1801 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1801.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1800.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-ab6eebf8" + reconcilerVersion: "18.3.0-www-modern-f86b50a5" }); exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; exports.createPortal = function (children, container) { @@ -16133,7 +16183,7 @@ exports.unstable_flushControlled = function (fn) { } }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-www-modern-ab6eebf8"; +exports.version = "18.3.0-www-modern-f86b50a5"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index 458615f282..c9cd5651a7 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-classic-2aeddbc8"; +var ReactVersion = "18.3.0-www-classic-119eea22"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -9887,16 +9887,8 @@ function use(usable) { // $FlowFixMe[method-unbinding] if (typeof usable.then === "function") { // This is a thenable. - var thenable = usable; // Track the position of the thenable within this fiber. - - var index = thenableIndexCounter; - thenableIndexCounter += 1; - - if (thenableState === null) { - thenableState = createThenableState(); - } - - return trackUsedThenable(thenableState, thenable, index); + var thenable = usable; + return unwrapThenable(thenable); } else if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE @@ -9909,6 +9901,17 @@ function use(usable) { throw new Error("An unsupported type was passed to use(): " + String(usable)); } +function unwrapThenable(thenable) { + var index = thenableIndexCounter; + thenableIndexCounter += 1; + + if (thenableState === null) { + thenableState = createThenableState(); + } + + return trackUsedThenable(thenableState, thenable, index); +} + function unsupportedRefresh() { throw new Error("Cache cannot be refreshed during server rendering."); } @@ -11143,7 +11146,27 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) { return; } - } // $FlowFixMe[method-unbinding] + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + + var maybeUsable = node; + + if (typeof maybeUsable.then === "function") { + var thenable = maybeUsable; + return renderNodeDestructiveImpl( + request, + task, + null, + unwrapThenable(thenable) + ); + } var childString = Object.prototype.toString.call(node); throw new Error( diff --git a/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index 3d9197c23b..764da5d4d4 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-modern-5ee4e417"; +var ReactVersion = "18.3.0-www-modern-6461d8a7"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -9646,16 +9646,8 @@ function use(usable) { // $FlowFixMe[method-unbinding] if (typeof usable.then === "function") { // This is a thenable. - var thenable = usable; // Track the position of the thenable within this fiber. - - var index = thenableIndexCounter; - thenableIndexCounter += 1; - - if (thenableState === null) { - thenableState = createThenableState(); - } - - return trackUsedThenable(thenableState, thenable, index); + var thenable = usable; + return unwrapThenable(thenable); } else if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE @@ -9668,6 +9660,17 @@ function use(usable) { throw new Error("An unsupported type was passed to use(): " + String(usable)); } +function unwrapThenable(thenable) { + var index = thenableIndexCounter; + thenableIndexCounter += 1; + + if (thenableState === null) { + thenableState = createThenableState(); + } + + return trackUsedThenable(thenableState, thenable, index); +} + function unsupportedRefresh() { throw new Error("Cache cannot be refreshed during server rendering."); } @@ -10891,7 +10894,27 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) { return; } - } // $FlowFixMe[method-unbinding] + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + + var maybeUsable = node; + + if (typeof maybeUsable.then === "function") { + var thenable = maybeUsable; + return renderNodeDestructiveImpl( + request, + task, + null, + unwrapThenable(thenable) + ); + } var childString = Object.prototype.toString.call(node); throw new Error( diff --git a/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index 9a3bd11c81..b372c22def 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -2368,6 +2368,12 @@ function throwOnUseEffectEventCall() { function unsupportedStartTransition() { throw Error(formatProdErrorMessage(394)); } +function unwrapThenable(thenable) { + var index = thenableIndexCounter; + thenableIndexCounter += 1; + null === thenableState && (thenableState = []); + return trackUsedThenable(thenableState, thenable, index); +} function unsupportedRefresh() { throw Error(formatProdErrorMessage(393)); } @@ -2451,12 +2457,7 @@ var HooksDispatcher = { }, use: function (usable) { if (null !== usable && "object" === typeof usable) { - if ("function" === typeof usable.then) { - var index = thenableIndexCounter; - thenableIndexCounter += 1; - null === thenableState && (thenableState = []); - return trackUsedThenable(thenableState, usable, index); - } + if ("function" === typeof usable.then) return unwrapThenable(usable); if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE @@ -3037,6 +3038,13 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) { } return; } + if ("function" === typeof node.then) + return renderNodeDestructiveImpl( + request, + task, + null, + unwrapThenable(node) + ); request = Object.prototype.toString.call(node); throw Error( formatProdErrorMessage( @@ -3779,4 +3787,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-classic-b6382309"; +exports.version = "18.3.0-www-classic-fdc4fa79"; diff --git a/compiled/facebook-www/ReactDOMServer-prod.modern.js b/compiled/facebook-www/ReactDOMServer-prod.modern.js index 6f9becc3f6..2e883c91d7 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServer-prod.modern.js @@ -2302,6 +2302,12 @@ function throwOnUseEffectEventCall() { function unsupportedStartTransition() { throw Error(formatProdErrorMessage(394)); } +function unwrapThenable(thenable) { + var index = thenableIndexCounter; + thenableIndexCounter += 1; + null === thenableState && (thenableState = []); + return trackUsedThenable(thenableState, thenable, index); +} function unsupportedRefresh() { throw Error(formatProdErrorMessage(393)); } @@ -2385,12 +2391,7 @@ var HooksDispatcher = { }, use: function (usable) { if (null !== usable && "object" === typeof usable) { - if ("function" === typeof usable.then) { - var index = thenableIndexCounter; - thenableIndexCounter += 1; - null === thenableState && (thenableState = []); - return trackUsedThenable(thenableState, usable, index); - } + if ("function" === typeof usable.then) return unwrapThenable(usable); if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE @@ -2935,6 +2936,13 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) { } return; } + if ("function" === typeof node.then) + return renderNodeDestructiveImpl( + request, + task, + null, + unwrapThenable(node) + ); request = Object.prototype.toString.call(node); throw Error( formatProdErrorMessage( @@ -3677,4 +3685,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-modern-b0c73d05"; +exports.version = "18.3.0-www-modern-a0a327e9"; diff --git a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js index 5ea67e1d0e..9bbfbfd207 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js @@ -9538,16 +9538,8 @@ function use(usable) { // $FlowFixMe[method-unbinding] if (typeof usable.then === "function") { // This is a thenable. - var thenable = usable; // Track the position of the thenable within this fiber. - - var index = thenableIndexCounter; - thenableIndexCounter += 1; - - if (thenableState === null) { - thenableState = createThenableState(); - } - - return trackUsedThenable(thenableState, thenable, index); + var thenable = usable; + return unwrapThenable(thenable); } else if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE @@ -9560,6 +9552,17 @@ function use(usable) { throw new Error("An unsupported type was passed to use(): " + String(usable)); } +function unwrapThenable(thenable) { + var index = thenableIndexCounter; + thenableIndexCounter += 1; + + if (thenableState === null) { + thenableState = createThenableState(); + } + + return trackUsedThenable(thenableState, thenable, index); +} + function unsupportedRefresh() { throw new Error("Cache cannot be refreshed during server rendering."); } @@ -10777,7 +10780,27 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) { return; } - } // $FlowFixMe[method-unbinding] + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + + var maybeUsable = node; + + if (typeof maybeUsable.then === "function") { + var thenable = maybeUsable; + return renderNodeDestructiveImpl( + request, + task, + null, + unwrapThenable(thenable) + ); + } var childString = Object.prototype.toString.call(node); throw new Error( diff --git a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js index f0eee4b9eb..020f1eaaa2 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js @@ -2334,6 +2334,12 @@ function throwOnUseEffectEventCall() { function unsupportedStartTransition() { throw Error("startTransition cannot be called during server rendering."); } +function unwrapThenable(thenable) { + var index = thenableIndexCounter; + thenableIndexCounter += 1; + null === thenableState && (thenableState = []); + return trackUsedThenable(thenableState, thenable, index); +} function unsupportedRefresh() { throw Error("Cache cannot be refreshed during server rendering."); } @@ -2422,12 +2428,7 @@ var HooksDispatcher = { }, use: function (usable) { if (null !== usable && "object" === typeof usable) { - if ("function" === typeof usable.then) { - var index = thenableIndexCounter; - thenableIndexCounter += 1; - null === thenableState && (thenableState = []); - return trackUsedThenable(thenableState, usable, index); - } + if ("function" === typeof usable.then) return unwrapThenable(usable); if ( usable.$$typeof === REACT_CONTEXT_TYPE || usable.$$typeof === REACT_SERVER_CONTEXT_TYPE @@ -2903,6 +2904,13 @@ function renderNodeDestructiveImpl(request, task, prevThenableState, node) { } return; } + if ("function" === typeof node.then) + return renderNodeDestructiveImpl( + request, + task, + null, + unwrapThenable(node) + ); request = Object.prototype.toString.call(node); throw Error( "Objects are not valid as a React child (found: " + diff --git a/compiled/facebook-www/ReactDOMTesting-dev.classic.js b/compiled/facebook-www/ReactDOMTesting-dev.classic.js index 0100b73e94..4fbc13c20f 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.classic.js @@ -11209,6 +11209,165 @@ var ReactStrictModeWarnings = { }; } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -11267,6 +11426,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -11686,6 +11856,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -11749,6 +11926,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -11822,6 +12011,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -12441,7 +12643,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -12454,6 +12656,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -12514,6 +12717,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -12542,11 +12770,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -12897,163 +13151,6 @@ function registerMutableSourceForHydration(root, mutableSource) { } } -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -28650,6 +28747,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -29175,15 +29273,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, @@ -31487,7 +31584,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-61c3f42d"; +var ReactVersion = "18.3.0-www-classic-9aa080ba"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOMTesting-dev.modern.js b/compiled/facebook-www/ReactDOMTesting-dev.modern.js index f522a9f617..513aa0bea2 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.modern.js @@ -17995,6 +17995,165 @@ var ReactStrictModeWarnings = { }; } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -18053,6 +18212,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -18472,6 +18642,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -18535,6 +18712,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -18608,6 +18797,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -19227,7 +19429,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -19240,6 +19442,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -19300,6 +19503,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -19328,11 +19556,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -19683,163 +19937,6 @@ function registerMutableSourceForHydration(root, mutableSource) { } } -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -35224,6 +35321,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -35749,15 +35847,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, @@ -38061,7 +38158,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-c92cd6e4"; +var ReactVersion = "18.3.0-www-modern-5abc7f64"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOMTesting-prod.classic.js b/compiled/facebook-www/ReactDOMTesting-prod.classic.js index 832c4fee66..6aeb7bb7ba 100644 --- a/compiled/facebook-www/ReactDOMTesting-prod.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-prod.classic.js @@ -2748,6 +2748,62 @@ function shallowEqual(objA, objB) { } return !0; } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -2984,6 +3040,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -3017,6 +3075,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -3069,6 +3134,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -3247,7 +3320,7 @@ function createChildReconciler(shouldTrackSideEffects) { isHydrating && pushTreeFork(returnFiber, newIdx); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -3392,6 +3465,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -3413,6 +3493,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -3500,55 +3596,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionPrimary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -9498,6 +9546,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -9756,9 +9806,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -9772,6 +9820,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -11632,17 +11682,17 @@ Internals.Events = [ restoreStateIfNeeded, batchedUpdates$1 ]; -var devToolsConfig$jscomp$inline_1553 = { +var devToolsConfig$jscomp$inline_1552 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-classic-600fa423", + version: "18.3.0-www-classic-3bfe2b4e", rendererPackageName: "react-dom" }; -var internals$jscomp$inline_2105 = { - bundleType: devToolsConfig$jscomp$inline_1553.bundleType, - version: devToolsConfig$jscomp$inline_1553.version, - rendererPackageName: devToolsConfig$jscomp$inline_1553.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1553.rendererConfig, +var internals$jscomp$inline_2106 = { + bundleType: devToolsConfig$jscomp$inline_1552.bundleType, + version: devToolsConfig$jscomp$inline_1552.version, + rendererPackageName: devToolsConfig$jscomp$inline_1552.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1552.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -11658,26 +11708,26 @@ var internals$jscomp$inline_2105 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1553.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1552.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-classic-600fa423" + reconcilerVersion: "18.3.0-www-classic-3bfe2b4e" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_2106 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_2107 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_2106.isDisabled && - hook$jscomp$inline_2106.supportsFiber + !hook$jscomp$inline_2107.isDisabled && + hook$jscomp$inline_2107.supportsFiber ) try { - (rendererID = hook$jscomp$inline_2106.inject( - internals$jscomp$inline_2105 + (rendererID = hook$jscomp$inline_2107.inject( + internals$jscomp$inline_2106 )), - (injectedHook = hook$jscomp$inline_2106); + (injectedHook = hook$jscomp$inline_2107); } catch (err) {} } var Dispatcher = Internals.Dispatcher, @@ -13034,14 +13084,14 @@ var isInputEventSupported = !1; if (canUseDOM) { var JSCompiler_inline_result$jscomp$296; if (canUseDOM) { - var isSupported$jscomp$inline_1628 = "oninput" in document; - if (!isSupported$jscomp$inline_1628) { - var element$jscomp$inline_1629 = document.createElement("div"); - element$jscomp$inline_1629.setAttribute("oninput", "return;"); - isSupported$jscomp$inline_1628 = - "function" === typeof element$jscomp$inline_1629.oninput; + var isSupported$jscomp$inline_1627 = "oninput" in document; + if (!isSupported$jscomp$inline_1627) { + var element$jscomp$inline_1628 = document.createElement("div"); + element$jscomp$inline_1628.setAttribute("oninput", "return;"); + isSupported$jscomp$inline_1627 = + "function" === typeof element$jscomp$inline_1628.oninput; } - JSCompiler_inline_result$jscomp$296 = isSupported$jscomp$inline_1628; + JSCompiler_inline_result$jscomp$296 = isSupported$jscomp$inline_1627; } else JSCompiler_inline_result$jscomp$296 = !1; isInputEventSupported = JSCompiler_inline_result$jscomp$296 && @@ -13182,20 +13232,20 @@ function registerSimpleEvent(domEventName, reactName) { registerTwoPhaseEvent(reactName, [domEventName]); } for ( - var i$jscomp$inline_1641 = 0; - i$jscomp$inline_1641 < simpleEventPluginEvents.length; - i$jscomp$inline_1641++ + var i$jscomp$inline_1640 = 0; + i$jscomp$inline_1640 < simpleEventPluginEvents.length; + i$jscomp$inline_1640++ ) { - var eventName$jscomp$inline_1642 = - simpleEventPluginEvents[i$jscomp$inline_1641], - domEventName$jscomp$inline_1643 = - eventName$jscomp$inline_1642.toLowerCase(), - capitalizedEvent$jscomp$inline_1644 = - eventName$jscomp$inline_1642[0].toUpperCase() + - eventName$jscomp$inline_1642.slice(1); + var eventName$jscomp$inline_1641 = + simpleEventPluginEvents[i$jscomp$inline_1640], + domEventName$jscomp$inline_1642 = + eventName$jscomp$inline_1641.toLowerCase(), + capitalizedEvent$jscomp$inline_1643 = + eventName$jscomp$inline_1641[0].toUpperCase() + + eventName$jscomp$inline_1641.slice(1); registerSimpleEvent( - domEventName$jscomp$inline_1643, - "on" + capitalizedEvent$jscomp$inline_1644 + domEventName$jscomp$inline_1642, + "on" + capitalizedEvent$jscomp$inline_1643 ); } registerSimpleEvent(ANIMATION_END, "onAnimationEnd"); @@ -14914,4 +14964,4 @@ exports.unstable_renderSubtreeIntoContainer = function ( ); }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-www-classic-600fa423"; +exports.version = "18.3.0-www-classic-3bfe2b4e"; diff --git a/compiled/facebook-www/ReactDOMTesting-prod.modern.js b/compiled/facebook-www/ReactDOMTesting-prod.modern.js index e9e4e0e27a..9ba5c8fa4e 100644 --- a/compiled/facebook-www/ReactDOMTesting-prod.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-prod.modern.js @@ -5478,6 +5478,62 @@ function commitCallbacks(updateQueue, context) { ) callCallback(callbacks[updateQueue], context); } +var SuspenseException = Error(formatProdErrorMessage(460)); +function isThenableResolved(thenable) { + thenable = thenable.status; + return "fulfilled" === thenable || "rejected" === thenable; +} +function noop() {} +function trackUsedThenable(thenableState, thenable, index) { + index = thenableState[index]; + void 0 === index + ? thenableState.push(thenable) + : index !== thenable && (thenable.then(noop, noop), (thenable = index)); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + "string" === typeof thenable.status + ? thenable.then(noop, noop) + : ((thenableState = thenable), + (thenableState.status = "pending"), + thenableState.then( + function (fulfilledValue) { + if ("pending" === thenable.status) { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if ("pending" === thenable.status) { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + )); + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } + suspendedThenable = thenable; + throw SuspenseException; + } +} +var suspendedThenable = null, + thenableState$1 = null, + thenableIndexCounter$1 = 0; +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + null === thenableState$1 && (thenableState$1 = []); + return trackUsedThenable(thenableState$1, thenable, index); +} function coerceRef(returnFiber, current, element) { returnFiber = element.ref; if ( @@ -5714,6 +5770,8 @@ function createChildReconciler(shouldTrackSideEffects) { (newChild.return = returnFiber), newChild ); + if ("function" === typeof newChild.then) + return createChild(returnFiber, unwrapThenable(newChild), lanes); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -5747,6 +5805,13 @@ function createChildReconciler(shouldTrackSideEffects) { return null !== key ? null : updateFragment(returnFiber, oldFiber, newChild, lanes, null); + if ("function" === typeof newChild.then) + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -5799,6 +5864,14 @@ function createChildReconciler(shouldTrackSideEffects) { (existingChildren = existingChildren.get(newIdx) || null), updateFragment(returnFiber, existingChildren, newChild, lanes, null) ); + if ("function" === typeof newChild.then) + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return null; @@ -5977,7 +6050,7 @@ function createChildReconciler(shouldTrackSideEffects) { isHydrating && pushTreeFork(returnFiber, newIdx); return iteratorFn; } - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -6122,6 +6195,13 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + if ("function" === typeof newChild.then) + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(newChild), + lanes + ); throwOnInvalidObjectType(returnFiber, newChild); } return ("string" === typeof newChild && "" !== newChild) || @@ -6143,6 +6223,22 @@ function createChildReconciler(shouldTrackSideEffects) { placeSingleChild(returnFiber)) : deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + thenableIndexCounter$1 = 0; + returnFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; + return returnFiber; + } return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(!0), @@ -6230,55 +6326,7 @@ function resetWorkInProgressVersions() { workInProgressSources[i]._workInProgressVersionPrimary = null; workInProgressSources.length = 0; } -var SuspenseException = Error(formatProdErrorMessage(460)); -function isThenableResolved(thenable) { - thenable = thenable.status; - return "fulfilled" === thenable || "rejected" === thenable; -} -function noop() {} -function trackUsedThenable(thenableState, thenable, index) { - index = thenableState[index]; - void 0 === index - ? thenableState.push(thenable) - : index !== thenable && (thenable.then(noop, noop), (thenable = index)); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - default: - "string" === typeof thenable.status - ? thenable.then(noop, noop) - : ((thenableState = thenable), - (thenableState.status = "pending"), - thenableState.then( - function (fulfilledValue) { - if ("pending" === thenable.status) { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if ("pending" === thenable.status) { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - )); - switch (thenable.status) { - case "fulfilled": - return thenable.value; - case "rejected": - throw thenable.reason; - } - suspendedThenable = thenable; - throw SuspenseException; - } -} -var suspendedThenable = null, - ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, +var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, renderLanes$1 = 0, currentlyRenderingFiber$1 = null, @@ -12137,6 +12185,8 @@ function resetWorkInProgressStack() { else resetContextDependencies(), resetHooksOnUnwind(), + (thenableState$1 = null), + (thenableIndexCounter$1 = 0), (interruptedWork = workInProgress); for (; null !== interruptedWork; ) unwindInterruptedWork(interruptedWork.alternate, interruptedWork), @@ -12395,9 +12445,7 @@ function replaySuspendedUnitOfWork(unitOfWork) { ); break; default: - resetContextDependencies(), - resetHooksOnUnwind(), - unwindInterruptedWork(current, unitOfWork), + unwindInterruptedWork(current, unitOfWork), (unitOfWork = workInProgress = resetWorkInProgress(unitOfWork, renderLanes)), (current = beginWork(current, unitOfWork, renderLanes)); @@ -12411,6 +12459,8 @@ function replaySuspendedUnitOfWork(unitOfWork) { function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) { resetContextDependencies(); resetHooksOnUnwind(); + thenableState$1 = null; + thenableIndexCounter$1 = 0; var returnFiber = unitOfWork.return; if (null === returnFiber || null === workInProgressRoot) (workInProgressRootExitStatus = 1), @@ -14008,17 +14058,17 @@ Internals.Events = [ restoreStateIfNeeded, batchedUpdates ]; -var devToolsConfig$jscomp$inline_1673 = { +var devToolsConfig$jscomp$inline_1672 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-modern-f87ae691", + version: "18.3.0-www-modern-abf2779e", rendererPackageName: "react-dom" }; -var internals$jscomp$inline_2079 = { - bundleType: devToolsConfig$jscomp$inline_1673.bundleType, - version: devToolsConfig$jscomp$inline_1673.version, - rendererPackageName: devToolsConfig$jscomp$inline_1673.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1673.rendererConfig, +var internals$jscomp$inline_2080 = { + bundleType: devToolsConfig$jscomp$inline_1672.bundleType, + version: devToolsConfig$jscomp$inline_1672.version, + rendererPackageName: devToolsConfig$jscomp$inline_1672.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1672.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -14035,26 +14085,26 @@ var internals$jscomp$inline_2079 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1673.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1672.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-f87ae691" + reconcilerVersion: "18.3.0-www-modern-abf2779e" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_2080 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_2081 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_2080.isDisabled && - hook$jscomp$inline_2080.supportsFiber + !hook$jscomp$inline_2081.isDisabled && + hook$jscomp$inline_2081.supportsFiber ) try { - (rendererID = hook$jscomp$inline_2080.inject( - internals$jscomp$inline_2079 + (rendererID = hook$jscomp$inline_2081.inject( + internals$jscomp$inline_2080 )), - (injectedHook = hook$jscomp$inline_2080); + (injectedHook = hook$jscomp$inline_2081); } catch (err) {} } exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; @@ -14370,4 +14420,4 @@ exports.unstable_flushControlled = function (fn) { } }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-www-modern-f87ae691"; +exports.version = "18.3.0-www-modern-abf2779e"; diff --git a/compiled/facebook-www/ReactTestRenderer-dev.classic.js b/compiled/facebook-www/ReactTestRenderer-dev.classic.js index f829caa783..62e2fdfaee 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.classic.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.classic.js @@ -4401,6 +4401,165 @@ function checkPropStringCoercion(value, propName) { } } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -4459,6 +4618,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -4878,6 +5048,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -4941,6 +5118,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5014,6 +5203,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5603,7 +5805,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -5616,6 +5818,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -5676,6 +5879,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5704,11 +5932,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -6044,163 +6298,6 @@ function warnAboutMultipleRenderersDEV(mutableSource) { } } // Eager reads the version of a mutable source and stores it on the root. -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -20839,6 +20936,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -21378,15 +21476,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, @@ -23794,7 +23891,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-2505f0d2"; +var ReactVersion = "18.3.0-www-classic-ed61d5cd"; // Might add PROFILE later. diff --git a/compiled/facebook-www/ReactTestRenderer-dev.modern.js b/compiled/facebook-www/ReactTestRenderer-dev.modern.js index a8ef377a9e..9cdbb2c307 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.modern.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.modern.js @@ -4401,6 +4401,165 @@ function checkPropStringCoercion(value, propName) { } } +var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we +// detect this is caught by userspace, we'll log a warning in development. + +var SuspenseException = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + "detail of `use` to interrupt the current render. You must either " + + "rethrow it immediately, or move the `use` call outside of the " + + "`try/catch` block. Capturing without rethrowing will lead to " + + "unexpected behavior.\n\n" + + "To handle async errors, wrap your component in an error boundary, or " + + "call the promise's `.catch` method and pass the result to `use`" +); +function createThenableState() { + // The ThenableState is created the first time a component suspends. If it + // suspends again, we'll reuse the same state. + return []; +} +function isThenableResolved(thenable) { + var status = thenable.status; + return status === "fulfilled" || status === "rejected"; +} + +function noop() {} + +function trackUsedThenable(thenableState, thenable, index) { + if (ReactCurrentActQueue$2.current !== null) { + ReactCurrentActQueue$2.didUsePromise = true; + } + + var previous = thenableState[index]; + + if (previous === undefined) { + thenableState.push(thenable); + } else { + if (previous !== thenable) { + // Reuse the previous thenable, and drop the new one. We can assume + // they represent the same value, because components are idempotent. + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + thenable = previous; + } + } // We use an expando to track the status and result of a thenable so that we + // can synchronously unwrap the value. Think of this as an extension of the + // Promise API, or a custom interface that is a superset of Thenable. + // + // If the thenable doesn't have a status, set it to "pending" and attach + // a listener that will update its status and result when it resolves. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledValue = thenable.value; + return fulfilledValue; + } + + case "rejected": { + var rejectedError = thenable.reason; + throw rejectedError; + } + + default: { + if (typeof thenable.status === "string") { + // Only instrument the thenable if the status if not defined. If + // it's defined, but an unknown value, assume it's been instrumented by + // some custom userspace implementation. We treat it as "pending". + // Attach a dummy listener, to ensure that any lazy initialization can + // happen. Flight lazily parses JSON when the value is actually awaited. + thenable.then(noop, noop); + } else { + var pendingThenable = thenable; + pendingThenable.status = "pending"; + pendingThenable.then( + function (fulfilledValue) { + if (thenable.status === "pending") { + var fulfilledThenable = thenable; + fulfilledThenable.status = "fulfilled"; + fulfilledThenable.value = fulfilledValue; + } + }, + function (error) { + if (thenable.status === "pending") { + var rejectedThenable = thenable; + rejectedThenable.status = "rejected"; + rejectedThenable.reason = error; + } + } + ); + } // Check one more time in case the thenable resolved synchronously. + + switch (thenable.status) { + case "fulfilled": { + var fulfilledThenable = thenable; + return fulfilledThenable.value; + } + + case "rejected": { + var rejectedThenable = thenable; + throw rejectedThenable.reason; + } + } // Suspend. + // + // Throwing here is an implementation detail that allows us to unwind the + // call stack. But we shouldn't allow it to leak into userspace. Throw an + // opaque placeholder value instead of the actual thenable. If it doesn't + // get captured by the work loop, log a warning, because that means + // something in userspace must have caught it. + + suspendedThenable = thenable; + + { + needsToResetSuspendedThenableDEV = true; + } + + throw SuspenseException; + } + } +} // This is used to track the actual thenable that suspended so it can be +// passed to the rest of the Suspense implementation — which, for historical +// reasons, expects to receive a thenable. + +var suspendedThenable = null; +var needsToResetSuspendedThenableDEV = false; +function getSuspendedThenable() { + // This is called right after `use` suspends by throwing an exception. `use` + // throws an opaque value instead of the thenable itself so that it can't be + // caught in userspace. Then the work loop accesses the actual thenable using + // this function. + if (suspendedThenable === null) { + throw new Error( + "Expected a suspended thenable. This is a bug in React. Please file " + + "an issue." + ); + } + + var thenable = suspendedThenable; + suspendedThenable = null; + + { + needsToResetSuspendedThenableDEV = false; + } + + return thenable; +} +function checkIfUseWrappedInTryCatch() { + { + // This was set right before SuspenseException was thrown, and it should + // have been cleared when the exception was handled. If it wasn't, + // it must have been caught by userspace. + if (needsToResetSuspendedThenableDEV) { + needsToResetSuspendedThenableDEV = false; + return true; + } + } + + return false; +} + +var thenableState$1 = null; +var thenableIndexCounter$1 = 0; var didWarnAboutMaps; var didWarnAboutGenerators; var didWarnAboutStringRefs; @@ -4459,6 +4618,17 @@ function isReactClass(type) { return type.prototype && type.prototype.isReactComponent; } +function unwrapThenable(thenable) { + var index = thenableIndexCounter$1; + thenableIndexCounter$1 += 1; + + if (thenableState$1 === null) { + thenableState$1 = createThenableState(); + } + + return trackUsedThenable(thenableState$1, thenable, index); +} + function coerceRef(returnFiber, current, element) { var mixedRef = element.ref; @@ -4878,6 +5048,13 @@ function createChildReconciler(shouldTrackSideEffects) { _created3.return = returnFiber; return _created3; + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return createChild(returnFiber, unwrapThenable(thenable), lanes); } throwOnInvalidObjectType(returnFiber, newChild); @@ -4941,6 +5118,18 @@ function createChildReconciler(shouldTrackSideEffects) { } return updateFragment(returnFiber, oldFiber, newChild, lanes, null); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateSlot( + returnFiber, + oldFiber, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5014,6 +5203,19 @@ function createChildReconciler(shouldTrackSideEffects) { lanes, null ); + } // Usable node types + // + // Unwrap the inner value and recursively call this function again. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return updateFromMap( + existingChildren, + returnFiber, + newIdx, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5603,7 +5805,7 @@ function createChildReconciler(shouldTrackSideEffects) { // itself. They will be added to the side-effect list as we pass through the // children and the parent. - function reconcileChildFibers( + function reconcileChildFibersImpl( returnFiber, currentFirstChild, newChild, @@ -5616,6 +5818,7 @@ function createChildReconciler(shouldTrackSideEffects) { // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. + // TODO: Let's use recursion like we do for Usable nodes? var isUnkeyedTopLevelFragment = typeof newChild === "object" && newChild !== null && @@ -5676,6 +5879,31 @@ function createChildReconciler(shouldTrackSideEffects) { newChild, lanes ); + } // Usables are a valid React node type. When React encounters a Usable in + // a child position, it unwraps it using the same algorithm as `use`. For + // example, for promises, React will throw an exception to unwind the + // stack, then replay the component once the promise resolves. + // + // A difference from `use` is that React will keep unwrapping the value + // until it reaches a non-Usable type. + // + // e.g. Usable>> should resolve to T + // + // The structure is a bit unfortunate. Ideally, we shouldn't need to + // replay the entire begin phase of the parent fiber in order to reconcile + // the children again. This would require a somewhat significant refactor, + // because reconcilation happens deep within the begin phase, and + // depending on the type of work, not always at the end. We should + // consider as an future improvement. + + if (typeof newChild.then === "function") { + var thenable = newChild; + return reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + unwrapThenable(thenable), + lanes + ); } throwOnInvalidObjectType(returnFiber, newChild); @@ -5704,11 +5932,37 @@ function createChildReconciler(shouldTrackSideEffects) { return deleteRemainingChildren(returnFiber, currentFirstChild); } + function reconcileChildFibers( + returnFiber, + currentFirstChild, + newChild, + lanes + ) { + // This indirection only exists so we can reset `thenableState` at the end. + // It should get inlined by Closure. + thenableIndexCounter$1 = 0; + var firstChildFiber = reconcileChildFibersImpl( + returnFiber, + currentFirstChild, + newChild, + lanes + ); + thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets + // set at the beginning. + + return firstChildFiber; + } + return reconcileChildFibers; } var reconcileChildFibers = createChildReconciler(true); var mountChildFibers = createChildReconciler(false); +function resetChildReconcilerOnUnwind() { + // On unwind, clear any pending thenables that were used. + thenableState$1 = null; + thenableIndexCounter$1 = 0; +} function cloneChildFibers(current, workInProgress) { if (current !== null && workInProgress.child !== current.child) { throw new Error("Resuming work not yet implemented."); @@ -6044,163 +6298,6 @@ function warnAboutMultipleRenderersDEV(mutableSource) { } } // Eager reads the version of a mutable source and stores it on the root. -var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we -// detect this is caught by userspace, we'll log a warning in development. - -var SuspenseException = new Error( - "Suspense Exception: This is not a real error! It's an implementation " + - "detail of `use` to interrupt the current render. You must either " + - "rethrow it immediately, or move the `use` call outside of the " + - "`try/catch` block. Capturing without rethrowing will lead to " + - "unexpected behavior.\n\n" + - "To handle async errors, wrap your component in an error boundary, or " + - "call the promise's `.catch` method and pass the result to `use`" -); -function createThenableState() { - // The ThenableState is created the first time a component suspends. If it - // suspends again, we'll reuse the same state. - return []; -} -function isThenableResolved(thenable) { - var status = thenable.status; - return status === "fulfilled" || status === "rejected"; -} - -function noop() {} - -function trackUsedThenable(thenableState, thenable, index) { - if (ReactCurrentActQueue$2.current !== null) { - ReactCurrentActQueue$2.didUsePromise = true; - } - - var previous = thenableState[index]; - - if (previous === undefined) { - thenableState.push(thenable); - } else { - if (previous !== thenable) { - // Reuse the previous thenable, and drop the new one. We can assume - // they represent the same value, because components are idempotent. - // Avoid an unhandled rejection errors for the Promises that we'll - // intentionally ignore. - thenable.then(noop, noop); - thenable = previous; - } - } // We use an expando to track the status and result of a thenable so that we - // can synchronously unwrap the value. Think of this as an extension of the - // Promise API, or a custom interface that is a superset of Thenable. - // - // If the thenable doesn't have a status, set it to "pending" and attach - // a listener that will update its status and result when it resolves. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledValue = thenable.value; - return fulfilledValue; - } - - case "rejected": { - var rejectedError = thenable.reason; - throw rejectedError; - } - - default: { - if (typeof thenable.status === "string") { - // Only instrument the thenable if the status if not defined. If - // it's defined, but an unknown value, assume it's been instrumented by - // some custom userspace implementation. We treat it as "pending". - // Attach a dummy listener, to ensure that any lazy initialization can - // happen. Flight lazily parses JSON when the value is actually awaited. - thenable.then(noop, noop); - } else { - var pendingThenable = thenable; - pendingThenable.status = "pending"; - pendingThenable.then( - function (fulfilledValue) { - if (thenable.status === "pending") { - var fulfilledThenable = thenable; - fulfilledThenable.status = "fulfilled"; - fulfilledThenable.value = fulfilledValue; - } - }, - function (error) { - if (thenable.status === "pending") { - var rejectedThenable = thenable; - rejectedThenable.status = "rejected"; - rejectedThenable.reason = error; - } - } - ); - } // Check one more time in case the thenable resolved synchronously. - - switch (thenable.status) { - case "fulfilled": { - var fulfilledThenable = thenable; - return fulfilledThenable.value; - } - - case "rejected": { - var rejectedThenable = thenable; - throw rejectedThenable.reason; - } - } // Suspend. - // - // Throwing here is an implementation detail that allows us to unwind the - // call stack. But we shouldn't allow it to leak into userspace. Throw an - // opaque placeholder value instead of the actual thenable. If it doesn't - // get captured by the work loop, log a warning, because that means - // something in userspace must have caught it. - - suspendedThenable = thenable; - - { - needsToResetSuspendedThenableDEV = true; - } - - throw SuspenseException; - } - } -} // This is used to track the actual thenable that suspended so it can be -// passed to the rest of the Suspense implementation — which, for historical -// reasons, expects to receive a thenable. - -var suspendedThenable = null; -var needsToResetSuspendedThenableDEV = false; -function getSuspendedThenable() { - // This is called right after `use` suspends by throwing an exception. `use` - // throws an opaque value instead of the thenable itself so that it can't be - // caught in userspace. Then the work loop accesses the actual thenable using - // this function. - if (suspendedThenable === null) { - throw new Error( - "Expected a suspended thenable. This is a bug in React. Please file " + - "an issue." - ); - } - - var thenable = suspendedThenable; - suspendedThenable = null; - - { - needsToResetSuspendedThenableDEV = false; - } - - return thenable; -} -function checkIfUseWrappedInTryCatch() { - { - // This was set right before SuspenseException was thrown, and it should - // have been cleared when the exception was handled. If it wasn't, - // it must have been caught by userspace. - if (needsToResetSuspendedThenableDEV) { - needsToResetSuspendedThenableDEV = false; - return true; - } - } - - return false; -} - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; var didWarnAboutMismatchedHooksForComponent; @@ -20839,6 +20936,7 @@ function resetSuspendedWorkLoopOnUnwind() { // Reset module-level state that was set during the render phase. resetContextDependencies(); resetHooksOnUnwind(); + resetChildReconcilerOnUnwind(); } function handleThrow(root, thrownValue) { @@ -21378,15 +21476,14 @@ function replaySuspendedUnitOfWork(unitOfWork) { } default: { - { - error( - "Unexpected type of work: %s, Currently only function " + - "components are replayed after suspending. This is a bug in React.", - unitOfWork.tag - ); - } - - resetSuspendedWorkLoopOnUnwind(); + // Other types besides function components are reset completely before + // being replayed. Currently this only happens when a Usable type is + // reconciled — the reconciler will suspend. + // + // We reset the fiber back to its original state; however, this isn't + // a full "unwind" because we're going to reuse the promises that were + // reconciled previously. So it's intentional that we don't call + // resetSuspendedWorkLoopOnUnwind here. unwindInterruptedWork(current, unitOfWork); unitOfWork = workInProgress = resetWorkInProgress( unitOfWork, @@ -23794,7 +23891,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-f87ae691"; +var ReactVersion = "18.3.0-www-modern-abf2779e"; // Might add PROFILE later. diff --git a/compiled/facebook-www/WARNINGS b/compiled/facebook-www/WARNINGS index 55dc5aec7b..ab647cc267 100644 --- a/compiled/facebook-www/WARNINGS +++ b/compiled/facebook-www/WARNINGS @@ -311,7 +311,6 @@ "Unexpected ref object provided for %s. Use either a ref-setter function or React.createRef()." "Unexpected return value from a callback ref in %s. A callback ref should not return a function." "Unexpected type for suspenseCallback." -"Unexpected type of work: %s, Currently only function components are replayed after suspending. This is a bug in React." "Unexpectedly popped too many stack frames. This is a bug in React." "Unknown ARIA attribute `%s`. Did you mean `%s`?" "Unknown event handler property `%s`. It will be ignored."