|
7 | 7 | * @noflow |
8 | 8 | * @nolint |
9 | 9 | * @preventMunge |
10 | | - * @generated SignedSource<<adf921170475fb87731c699acf7c0c8f>> |
| 10 | + * @generated SignedSource<<4a19698954f795c20e290576071bf6b0>> |
11 | 11 | */ |
12 | 12 |
|
13 | 13 | 'use strict'; |
@@ -4118,6 +4118,7 @@ function trackUsedThenable(thenableState, thenable, index) { |
4118 | 4118 |
|
4119 | 4119 | case "rejected": { |
4120 | 4120 | var rejectedError = thenable.reason; |
| 4121 | + checkIfUseWrappedInAsyncCatch(rejectedError); |
4121 | 4122 | throw rejectedError; |
4122 | 4123 | } |
4123 | 4124 |
|
@@ -4173,18 +4174,20 @@ function trackUsedThenable(thenableState, thenable, index) { |
4173 | 4174 | rejectedThenable.reason = error; |
4174 | 4175 | } |
4175 | 4176 | } |
4176 | | - ); |
4177 | | - } // Check one more time in case the thenable resolved synchronously. |
| 4177 | + ); // Check one more time in case the thenable resolved synchronously. |
4178 | 4178 |
|
4179 | | - switch (thenable.status) { |
4180 | | - case "fulfilled": { |
4181 | | - var fulfilledThenable = thenable; |
4182 | | - return fulfilledThenable.value; |
4183 | | - } |
| 4179 | + switch (thenable.status) { |
| 4180 | + case "fulfilled": { |
| 4181 | + var fulfilledThenable = thenable; |
| 4182 | + return fulfilledThenable.value; |
| 4183 | + } |
4184 | 4184 |
|
4185 | | - case "rejected": { |
4186 | | - var rejectedThenable = thenable; |
4187 | | - throw rejectedThenable.reason; |
| 4185 | + case "rejected": { |
| 4186 | + var rejectedThenable = thenable; |
| 4187 | + var _rejectedError = rejectedThenable.reason; |
| 4188 | + checkIfUseWrappedInAsyncCatch(_rejectedError); |
| 4189 | + throw _rejectedError; |
| 4190 | + } |
4188 | 4191 | } |
4189 | 4192 | } // Suspend. |
4190 | 4193 | // |
@@ -4243,6 +4246,22 @@ function checkIfUseWrappedInTryCatch() { |
4243 | 4246 |
|
4244 | 4247 | return false; |
4245 | 4248 | } |
| 4249 | +function checkIfUseWrappedInAsyncCatch(rejectedReason) { |
| 4250 | + // This check runs in prod, too, because it prevents a more confusing |
| 4251 | + // downstream error, where SuspenseException is caught by a promise and |
| 4252 | + // thrown asynchronously. |
| 4253 | + // TODO: Another way to prevent SuspenseException from leaking into an async |
| 4254 | + // execution context is to check the dispatcher every time `use` is called, |
| 4255 | + // or some equivalent. That might be preferable for other reasons, too, since |
| 4256 | + // it matches how we prevent similar mistakes for other hooks. |
| 4257 | + if (rejectedReason === SuspenseException) { |
| 4258 | + throw new Error( |
| 4259 | + "Hooks are not supported inside an async component. This " + |
| 4260 | + "error is often caused by accidentally adding `'use client'` " + |
| 4261 | + "to a module that was originally written for the server." |
| 4262 | + ); |
| 4263 | + } |
| 4264 | +} |
4246 | 4265 |
|
4247 | 4266 | var thenableState$1 = null; |
4248 | 4267 | var thenableIndexCounter$1 = 0; |
@@ -6527,10 +6546,12 @@ var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, |
6527 | 6546 | var didWarnAboutMismatchedHooksForComponent; |
6528 | 6547 | var didWarnUncachedGetSnapshot; |
6529 | 6548 | var didWarnAboutUseWrappedInTryCatch; |
| 6549 | +var didWarnAboutAsyncClientComponent; |
6530 | 6550 |
|
6531 | 6551 | { |
6532 | 6552 | didWarnAboutMismatchedHooksForComponent = new Set(); |
6533 | 6553 | didWarnAboutUseWrappedInTryCatch = new Set(); |
| 6554 | + didWarnAboutAsyncClientComponent = new Set(); |
6534 | 6555 | } // The effect "instance" is a shared object that remains the same for the entire |
6535 | 6556 | // lifetime of an effect. In Rust terms, a RefCell. We use it to store the |
6536 | 6557 | // "destroy" function that is returned from an effect, because that is stateful. |
@@ -6671,6 +6692,57 @@ function warnOnHookMismatchInDev(currentHookName) { |
6671 | 6692 | } |
6672 | 6693 | } |
6673 | 6694 |
|
| 6695 | +function warnIfAsyncClientComponent(Component, componentDoesIncludeHooks) { |
| 6696 | + { |
| 6697 | + // This dev-only check only works for detecting native async functions, |
| 6698 | + // not transpiled ones. There's also a prod check that we use to prevent |
| 6699 | + // async client components from crashing the app; the prod one works even |
| 6700 | + // for transpiled async functions. Neither mechanism is completely |
| 6701 | + // bulletproof but together they cover the most common cases. |
| 6702 | + var isAsyncFunction = // $FlowIgnore[method-unbinding] |
| 6703 | + Object.prototype.toString.call(Component) === "[object AsyncFunction]"; |
| 6704 | + |
| 6705 | + if (isAsyncFunction) { |
| 6706 | + // Encountered an async Client Component. This is not yet supported, |
| 6707 | + // except in certain constrained cases, like during a route navigation. |
| 6708 | + var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1); |
| 6709 | + |
| 6710 | + if (!didWarnAboutAsyncClientComponent.has(componentName)) { |
| 6711 | + didWarnAboutAsyncClientComponent.add(componentName); // Check if this is a sync update. We use the "root" render lanes here |
| 6712 | + // because the "subtree" render lanes may include additional entangled |
| 6713 | + // lanes related to revealing previously hidden content. |
| 6714 | + |
| 6715 | + var root = getWorkInProgressRoot(); |
| 6716 | + var rootRenderLanes = getWorkInProgressRootRenderLanes(); |
| 6717 | + |
| 6718 | + if (root !== null && includesBlockingLane(root, rootRenderLanes)) { |
| 6719 | + error( |
| 6720 | + "async/await is not yet supported in Client Components, only " + |
| 6721 | + "Server Components. This error is often caused by accidentally " + |
| 6722 | + "adding `'use client'` to a module that was originally written " + |
| 6723 | + "for the server." |
| 6724 | + ); |
| 6725 | + } else { |
| 6726 | + // This is a concurrent (Transition, Retry, etc) render. We don't |
| 6727 | + // warn in these cases. |
| 6728 | + // |
| 6729 | + // However, Async Components are forbidden to include hooks, even |
| 6730 | + // during a transition, so let's check for that here. |
| 6731 | + // |
| 6732 | + // TODO: Add a corresponding warning to Server Components runtime. |
| 6733 | + if (componentDoesIncludeHooks) { |
| 6734 | + error( |
| 6735 | + "Hooks are not supported inside an async component. This " + |
| 6736 | + "error is often caused by accidentally adding `'use client'` " + |
| 6737 | + "to a module that was originally written for the server." |
| 6738 | + ); |
| 6739 | + } |
| 6740 | + } |
| 6741 | + } |
| 6742 | + } |
| 6743 | + } |
| 6744 | +} |
| 6745 | + |
6674 | 6746 | function throwInvalidHookError() { |
6675 | 6747 | throw new Error( |
6676 | 6748 | "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for" + |
@@ -6823,18 +6895,20 @@ function renderWithHooks( |
6823 | 6895 | ); |
6824 | 6896 | } |
6825 | 6897 |
|
6826 | | - finishRenderingHooks(current, workInProgress); |
| 6898 | + finishRenderingHooks(current, workInProgress, Component); |
6827 | 6899 | return children; |
6828 | 6900 | } |
6829 | 6901 |
|
6830 | | -function finishRenderingHooks(current, workInProgress) { |
6831 | | - // We can assume the previous dispatcher is always this one, since we set it |
6832 | | - // at the beginning of the render phase and there's no re-entrance. |
6833 | | - ReactCurrentDispatcher$1.current = ContextOnlyDispatcher; |
6834 | | - |
| 6902 | +function finishRenderingHooks(current, workInProgress, Component) { |
6835 | 6903 | { |
6836 | 6904 | workInProgress._debugHookTypes = hookTypesDev; |
6837 | | - } // This check uses currentHook so that it works the same in DEV and prod bundles. |
| 6905 | + var componentDoesIncludeHooks = |
| 6906 | + workInProgressHook !== null || thenableIndexCounter !== 0; |
| 6907 | + warnIfAsyncClientComponent(Component, componentDoesIncludeHooks); |
| 6908 | + } // We can assume the previous dispatcher is always this one, since we set it |
| 6909 | + // at the beginning of the render phase and there's no re-entrance. |
| 6910 | + |
| 6911 | + ReactCurrentDispatcher$1.current = ContextOnlyDispatcher; // This check uses currentHook so that it works the same in DEV and prod bundles. |
6838 | 6912 | // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles. |
6839 | 6913 |
|
6840 | 6914 | var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null; |
@@ -6885,7 +6959,12 @@ function finishRenderingHooks(current, workInProgress) { |
6885 | 6959 | var componentName = |
6886 | 6960 | getComponentNameFromFiber(workInProgress) || "Unknown"; |
6887 | 6961 |
|
6888 | | - if (!didWarnAboutUseWrappedInTryCatch.has(componentName)) { |
| 6962 | + if ( |
| 6963 | + !didWarnAboutUseWrappedInTryCatch.has(componentName) && // This warning also fires if you suspend with `use` inside an |
| 6964 | + // async component. Since we warn for that above, we'll silence this |
| 6965 | + // second warning by checking here. |
| 6966 | + !didWarnAboutAsyncClientComponent.has(componentName) |
| 6967 | + ) { |
6889 | 6968 | didWarnAboutUseWrappedInTryCatch.add(componentName); |
6890 | 6969 |
|
6891 | 6970 | error( |
@@ -6925,7 +7004,7 @@ function replaySuspendedComponentWithHooks( |
6925 | 7004 | props, |
6926 | 7005 | secondArg |
6927 | 7006 | ); |
6928 | | - finishRenderingHooks(current, workInProgress); |
| 7007 | + finishRenderingHooks(current, workInProgress, Component); |
6929 | 7008 | return children; |
6930 | 7009 | } |
6931 | 7010 |
|
@@ -23903,7 +23982,7 @@ function createFiberRoot( |
23903 | 23982 | return root; |
23904 | 23983 | } |
23905 | 23984 |
|
23906 | | -var ReactVersion = "18.3.0-canary-47385f8fa-20230630"; |
| 23985 | +var ReactVersion = "18.3.0-canary-5c8dabf88-20230701"; |
23907 | 23986 |
|
23908 | 23987 | // Might add PROFILE later. |
23909 | 23988 |
|
|
0 commit comments