Skip to content

Commit d05a7b9

Browse files
committed
Don't replay consoles written inside onError/onPostpone.
These aren't conceptually part of the request's render so we exit the request context for those.
1 parent c054691 commit d05a7b9

21 files changed

+91
-30
lines changed

packages/react-client/src/__tests__/ReactFlight-test.js

+41
Original file line numberDiff line numberDiff line change
@@ -1978,4 +1978,45 @@ describe('ReactFlight', () => {
19781978
</div>,
19791979
);
19801980
});
1981+
1982+
// @gate enableServerComponentLogs && __DEV__
1983+
it('replays logs, but not onError logs', async () => {
1984+
function foo() {
1985+
return 'hello';
1986+
}
1987+
function ServerComponent() {
1988+
console.log('hi', {prop: 123, fn: foo});
1989+
throw new Error('err');
1990+
}
1991+
1992+
let transport;
1993+
expect(() => {
1994+
// Reset the modules so that we get a new overridden console on top of the
1995+
// one installed by expect. This ensures that we still emit console.error
1996+
// calls.
1997+
jest.resetModules();
1998+
jest.mock('react', () => require('react/react.react-server'));
1999+
ReactServer = require('react');
2000+
ReactNoopFlightServer = require('react-noop-renderer/flight-server');
2001+
transport = ReactNoopFlightServer.render({root: <ServerComponent />});
2002+
}).toErrorDev('err');
2003+
2004+
const log = console.log;
2005+
try {
2006+
console.log = jest.fn();
2007+
// The error should not actually get logged because we're not awaiting the root
2008+
// so it's not thrown but the server log also shouldn't be replayed.
2009+
await ReactNoopFlightClient.read(transport);
2010+
2011+
expect(console.log).toHaveBeenCalledTimes(1);
2012+
expect(console.log.mock.calls[0][0]).toBe('hi');
2013+
expect(console.log.mock.calls[0][1].prop).toBe(123);
2014+
const loggedFn = console.log.mock.calls[0][1].fn;
2015+
expect(typeof loggedFn).toBe('function');
2016+
expect(loggedFn).not.toBe(foo);
2017+
expect(loggedFn.toString()).toBe(foo.toString());
2018+
} finally {
2019+
console.log = log;
2020+
}
2021+
});
19812022
});

packages/react-server/src/ReactFlightServer.js

+27-4
Original file line numberDiff line numberDiff line change
@@ -1689,13 +1689,36 @@ function renderModelDestructive(
16891689
}
16901690

16911691
function logPostpone(request: Request, reason: string): void {
1692-
const onPostpone = request.onPostpone;
1693-
onPostpone(reason);
1692+
const prevRequest = currentRequest;
1693+
currentRequest = null;
1694+
try {
1695+
const onPostpone = request.onPostpone;
1696+
if (supportsRequestStorage) {
1697+
// Exit the request context while running callbacks.
1698+
requestStorage.run(undefined, onPostpone, reason);
1699+
} else {
1700+
onPostpone(reason);
1701+
}
1702+
} finally {
1703+
currentRequest = prevRequest;
1704+
}
16941705
}
16951706

16961707
function logRecoverableError(request: Request, error: mixed): string {
1697-
const onError = request.onError;
1698-
const errorDigest = onError(error);
1708+
const prevRequest = currentRequest;
1709+
currentRequest = null;
1710+
let errorDigest;
1711+
try {
1712+
const onError = request.onError;
1713+
if (supportsRequestStorage) {
1714+
// Exit the request context while running callbacks.
1715+
errorDigest = requestStorage.run(undefined, onError, error);
1716+
} else {
1717+
errorDigest = onError(error);
1718+
}
1719+
} finally {
1720+
currentRequest = prevRequest;
1721+
}
16991722
if (errorDigest != null && typeof errorDigest !== 'string') {
17001723
// eslint-disable-next-line react-internal/prod-error-codes
17011724
throw new Error(

packages/react-server/src/ReactServerStreamConfigFB.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export opaque type BinaryChunk = string;
2121
export function flushBuffered(destination: Destination) {}
2222

2323
export const supportsRequestStorage = false;
24-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
24+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
2525

2626
export function beginWriting(destination: Destination) {}
2727

packages/react-server/src/forks/ReactFizzConfig.custom.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export type {TransitionStatus};
3838
export const isPrimaryRenderer = false;
3939

4040
export const supportsRequestStorage = false;
41-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
41+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
4242

4343
export const resetResumableState = $$$config.resetResumableState;
4444
export const completeResumableState = $$$config.completeResumableState;

packages/react-server/src/forks/ReactFizzConfig.dom-edge.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,5 @@ export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
1212

1313
// For now, we get this from the global scope, but this will likely move to a module.
1414
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
15-
export const requestStorage: AsyncLocalStorage<Request> = supportsRequestStorage
16-
? new AsyncLocalStorage()
17-
: (null: any);
15+
export const requestStorage: AsyncLocalStorage<Request | void> =
16+
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);

packages/react-server/src/forks/ReactFizzConfig.dom-legacy.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ import type {Request} from 'react-server/src/ReactFizzServer';
1111
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
1212

1313
export const supportsRequestStorage = false;
14-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
14+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);

packages/react-server/src/forks/ReactFizzConfig.dom-node.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ import type {Request} from 'react-server/src/ReactFizzServer';
1414
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
1515

1616
export const supportsRequestStorage = true;
17-
export const requestStorage: AsyncLocalStorage<Request> =
17+
export const requestStorage: AsyncLocalStorage<Request | void> =
1818
new AsyncLocalStorage();

packages/react-server/src/forks/ReactFizzConfig.dom.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ import type {Request} from 'react-server/src/ReactFizzServer';
1111
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
1212

1313
export const supportsRequestStorage = false;
14-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
14+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);

packages/react-server/src/forks/ReactFlightServerConfig.custom.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const isPrimaryRenderer = false;
2323
export const prepareHostDispatcher = () => {};
2424

2525
export const supportsRequestStorage = false;
26-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
26+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
2727

2828
export function createHints(): any {
2929
return null;

packages/react-server/src/forks/ReactFlightServerConfig.dom-browser-esm.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export * from 'react-server-dom-esm/src/ReactFlightServerConfigESMBundler';
1414
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1515

1616
export const supportsRequestStorage = true;
17-
export const requestStorage: AsyncLocalStorage<Request> =
17+
export const requestStorage: AsyncLocalStorage<Request | void> =
1818
new AsyncLocalStorage();
1919

2020
export * from '../ReactFlightServerConfigDebugNoop';

packages/react-server/src/forks/ReactFlightServerConfig.dom-browser-turbopack.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export * from 'react-server-dom-turbopack/src/ReactFlightServerConfigTurbopackBu
1313
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1414

1515
export const supportsRequestStorage = false;
16-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
1717

1818
export * from '../ReactFlightServerConfigDebugNoop';

packages/react-server/src/forks/ReactFlightServerConfig.dom-browser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export * from 'react-server-dom-webpack/src/ReactFlightServerConfigWebpackBundle
1313
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1414

1515
export const supportsRequestStorage = false;
16-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
1717

1818
export * from '../ReactFlightServerConfigDebugNoop';

packages/react-server/src/forks/ReactFlightServerConfig.dom-bun.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export * from '../ReactFlightServerConfigBundlerCustom';
1313
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1414

1515
export const supportsRequestStorage = false;
16-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
1717

1818
export * from '../ReactFlightServerConfigDebugNoop';

packages/react-server/src/forks/ReactFlightServerConfig.dom-edge-turbopack.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1313

1414
// For now, we get this from the global scope, but this will likely move to a module.
1515
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
16-
export const requestStorage: AsyncLocalStorage<Request> = supportsRequestStorage
17-
? new AsyncLocalStorage()
18-
: (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> =
17+
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);
1918

2019
// We use the Node version but get access to async_hooks from a global.
2120
import type {HookCallbacks, AsyncHook} from 'async_hooks';

packages/react-server/src/forks/ReactFlightServerConfig.dom-edge.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1313

1414
// For now, we get this from the global scope, but this will likely move to a module.
1515
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
16-
export const requestStorage: AsyncLocalStorage<Request> = supportsRequestStorage
17-
? new AsyncLocalStorage()
18-
: (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> =
17+
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);
1918

2019
// We use the Node version but get access to async_hooks from a global.
2120
import type {HookCallbacks, AsyncHook} from 'async_hooks';

packages/react-server/src/forks/ReactFlightServerConfig.dom-fb-experimental.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export * from 'react-server-dom-fb/src/ReactFlightServerConfigFBBundler';
1313
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1414

1515
export const supportsRequestStorage = false;
16-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
1717

1818
export * from '../ReactFlightServerConfigDebugNoop';

packages/react-server/src/forks/ReactFlightServerConfig.dom-legacy.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export * from '../ReactFlightServerConfigBundlerCustom';
1313
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1414

1515
export const supportsRequestStorage = false;
16-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
16+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
1717

1818
export * from '../ReactFlightServerConfigDebugNoop';

packages/react-server/src/forks/ReactFlightServerConfig.dom-node-esm.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export * from 'react-server-dom-esm/src/ReactFlightServerConfigESMBundler';
1414
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1515

1616
export const supportsRequestStorage = true;
17-
export const requestStorage: AsyncLocalStorage<Request> =
17+
export const requestStorage: AsyncLocalStorage<Request | void> =
1818
new AsyncLocalStorage();
1919

2020
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';

packages/react-server/src/forks/ReactFlightServerConfig.dom-node-turbopack.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export * from 'react-server-dom-turbopack/src/ReactFlightServerConfigTurbopackBu
1515
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1616

1717
export const supportsRequestStorage = true;
18-
export const requestStorage: AsyncLocalStorage<Request> =
18+
export const requestStorage: AsyncLocalStorage<Request | void> =
1919
new AsyncLocalStorage();
2020

2121
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';

packages/react-server/src/forks/ReactFlightServerConfig.dom-node.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export * from 'react-server-dom-webpack/src/ReactFlightServerConfigWebpackBundle
1515
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1616

1717
export const supportsRequestStorage = true;
18-
export const requestStorage: AsyncLocalStorage<Request> =
18+
export const requestStorage: AsyncLocalStorage<Request | void> =
1919
new AsyncLocalStorage();
2020

2121
export {createHook as createAsyncHook, executionAsyncId} from 'async_hooks';

scripts/flow/environment.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ declare module 'async_hooks' {
286286
declare class AsyncLocalStorage<T> {
287287
disable(): void;
288288
getStore(): T | void;
289-
run(store: T, callback: (...args: any[]) => void, ...args: any[]): void;
289+
run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R;
290290
enterWith(store: T): void;
291291
}
292292
declare interface AsyncResource {}
@@ -316,7 +316,7 @@ declare module 'async_hooks' {
316316
declare class AsyncLocalStorage<T> {
317317
disable(): void;
318318
getStore(): T | void;
319-
run(store: T, callback: (...args: any[]) => void, ...args: any[]): void;
319+
run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R;
320320
enterWith(store: T): void;
321321
}
322322

0 commit comments

Comments
 (0)