Skip to content

[Flight] Track Debug Info from Synchronously Unwrapped Promises #33485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
58 changes: 58 additions & 0 deletions packages/react-client/src/__tests__/ReactFlight-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2991,6 +2991,64 @@ describe('ReactFlight', () => {
);
});

// @gate !__DEV__ || enableComponentPerformanceTrack
it('preserves debug info for server-to-server through use()', async () => {
function ThirdPartyComponent() {
return 'hi';
}

function ServerComponent({transport}) {
// This is a Server Component that receives other Server Components from a third party.
const text = ReactServer.use(ReactNoopFlightClient.read(transport));
return <div>{text.toUpperCase()}</div>;
}

const thirdPartyTransport = ReactNoopFlightServer.render(
<ThirdPartyComponent />,
{
environmentName: 'third-party',
},
);

const transport = ReactNoopFlightServer.render(
<ServerComponent transport={thirdPartyTransport} />,
);

await act(async () => {
const promise = ReactNoopFlightClient.read(transport);
expect(getDebugInfo(promise)).toEqual(
__DEV__
? [
{time: 16},
{
name: 'ServerComponent',
env: 'Server',
key: null,
stack: ' in Object.<anonymous> (at **)',
props: {
transport: expect.arrayContaining([]),
},
},
{time: 16},
{
name: 'ThirdPartyComponent',
env: 'third-party',
key: null,
stack: ' in Object.<anonymous> (at **)',
props: {},
},
{time: 16},
{time: 17},
]
: undefined,
);
const result = await promise;
ReactNoop.render(result);
});

expect(ReactNoop).toMatchRenderedOutput(<div>HI</div>);
});

it('preserves error stacks passed through server-to-server with source maps', async () => {
async function ServerComponent({transport}) {
// This is a Server Component that receives other Server Components from a third party.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ const deepProxyHandlers = {
// reference.
case 'defaultProps':
return undefined;
// React looks for debugInfo on thenables.
case '_debugInfo':
return undefined;
// Avoid this attempting to be serialized.
case 'toJSON':
return undefined;
Expand Down Expand Up @@ -210,6 +213,9 @@ function getReference(target: Function, name: string | symbol): $FlowFixMe {
// reference.
case 'defaultProps':
return undefined;
// React looks for debugInfo on thenables.
case '_debugInfo':
return undefined;
// Avoid this attempting to be serialized.
case 'toJSON':
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ const deepProxyHandlers = {
// reference.
case 'defaultProps':
return undefined;
// React looks for debugInfo on thenables.
case '_debugInfo':
return undefined;
// Avoid this attempting to be serialized.
case 'toJSON':
return undefined;
Expand Down Expand Up @@ -211,6 +214,9 @@ function getReference(target: Function, name: string | symbol): $FlowFixMe {
// reference.
case 'defaultProps':
return undefined;
// React looks for debugInfo on thenables.
case '_debugInfo':
return undefined;
// Avoid this attempting to be serialized.
case 'toJSON':
return undefined;
Expand Down
6 changes: 6 additions & 0 deletions packages/react-server/src/ReactFlightHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ export function getThenableStateAfterSuspending(): ThenableState {
return state;
}

export function getTrackedThenablesAfterRendering(): null | Array<
Thenable<any>,
> {
return thenableState;
}

export const HooksDispatcher: Dispatcher = {
readContext: (unsupportedContext: any),

Expand Down
Loading
Loading