From 609d0cc3c8d484cd16378f4ba0e2e9eedbf48358 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Wed, 26 Jun 2024 19:47:39 +0200 Subject: [PATCH] Add new error message for awaiting the client export (#29853) ## Summary `Cannot access .then on server` is not an ideal message when you try to await or do promise chain to the properties of client reference. The below example will let `.then` get accessed by native code while handling the promise chain but the access is not clearly visible in user code. ``` import('./client-module').then((mod) => mod.Component) ``` This PR chnage the error message of module reference proxy '.then' property to show more kinds of usage, then it can be pretty clearly for helping users to avoid the bad usage ## How did you test this change? Unit test --- .../src/ReactFlightTurbopackReferences.js | 5 +++++ .../src/ReactFlightWebpackReferences.js | 5 +++++ .../src/__tests__/ReactFlightDOM-test.js | 14 ++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/packages/react-server-dom-turbopack/src/ReactFlightTurbopackReferences.js b/packages/react-server-dom-turbopack/src/ReactFlightTurbopackReferences.js index 26ee500ce5..ecf6a35dfa 100644 --- a/packages/react-server-dom-turbopack/src/ReactFlightTurbopackReferences.js +++ b/packages/react-server-dom-turbopack/src/ReactFlightTurbopackReferences.js @@ -133,6 +133,11 @@ const deepProxyHandlers = { `Instead, you can export a Client Component wrapper ` + `that itself renders a Client Context Provider.`, ); + case 'then': + throw new Error( + `Cannot await or return from a thenable. ` + + `You cannot await a client module from a server component.`, + ); } // eslint-disable-next-line react-internal/safe-string-coercion const expression = String(target.name) + '.' + String(name); diff --git a/packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js b/packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js index 78e8fa3594..6d14f41206 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js +++ b/packages/react-server-dom-webpack/src/ReactFlightWebpackReferences.js @@ -141,6 +141,11 @@ const deepProxyHandlers = { `Instead, you can export a Client Component wrapper ` + `that itself renders a Client Context Provider.`, ); + case 'then': + throw new Error( + `Cannot await or return from a thenable. ` + + `You cannot await a client module from a server component.`, + ); } // eslint-disable-next-line react-internal/safe-string-coercion const expression = String(target.name) + '.' + String(name); diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js index 3bf8e02e0f..96c63f63c6 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js @@ -623,6 +623,20 @@ describe('ReactFlightDOM', () => { ); }); + it('throws when await a client module prop of client exports', async () => { + const ClientModule = clientExports({ + Component: {deep: 'thing'}, + }); + async function awaitExport() { + const mod = await ClientModule; + return await Promise.resolve(mod.Component); + } + await expect(awaitExport()).rejects.toThrowError( + `Cannot await or return from a thenable. ` + + `You cannot await a client module from a server component.`, + ); + }); + it('throws when accessing a symbol prop from client exports', () => { const symbol = Symbol('test'); const ClientModule = clientExports({