From 8aafbcf115e67df899ddbb180ef025c7c260a3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 9 Jul 2024 14:22:50 -0400 Subject: [PATCH] [Flight] Fully support serializing Map/Set in console logs (#30295) Currently we serialize Map/Set through our regular flow and not the console serialization. The console one is more forgiving than the regular one. --- .../src/__tests__/ReactFlight-test.js | 9 ++++++- .../react-server/src/ReactFlightServer.js | 26 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 77bc717874..af56b8c1a2 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -2628,7 +2628,7 @@ describe('ReactFlight', () => { return 'hello'; } function ServerComponent() { - console.log('hi', {prop: 123, fn: foo}); + console.log('hi', {prop: 123, fn: foo, map: new Map([['foo', foo]])}); throw new Error('err'); } @@ -2670,6 +2670,13 @@ describe('ReactFlight', () => { expect(typeof loggedFn).toBe('function'); expect(loggedFn).not.toBe(foo); expect(loggedFn.toString()).toBe(foo.toString()); + + const loggedMap = mockConsoleLog.mock.calls[0][1].map; + expect(loggedMap instanceof Map).toBe(true); + const loggedFn2 = loggedMap.get('foo'); + expect(typeof loggedFn2).toBe('function'); + expect(loggedFn2).not.toBe(foo); + expect(loggedFn2.toString()).toBe(foo.toString()); }); it('uses the server component debug info as the element owner in DEV', async () => { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index dd279943b6..491757f32c 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -2021,6 +2021,28 @@ function serializeSet(request: Request, set: Set): string { return '$W' + id.toString(16); } +function serializeConsoleMap( + request: Request, + counter: {objectCount: number}, + map: Map, +): string { + // Like serializeMap but for renderConsoleValue. + const entries = Array.from(map); + const id = outlineConsoleValue(request, counter, entries); + return '$Q' + id.toString(16); +} + +function serializeConsoleSet( + request: Request, + counter: {objectCount: number}, + set: Set, +): string { + // Like serializeMap but for renderConsoleValue. + const entries = Array.from(set); + const id = outlineConsoleValue(request, counter, entries); + return '$W' + id.toString(16); +} + function serializeIterator( request: Request, iterator: Iterator, @@ -3220,10 +3242,10 @@ function renderConsoleValue( } if (value instanceof Map) { - return serializeMap(request, value); + return serializeConsoleMap(request, counter, value); } if (value instanceof Set) { - return serializeSet(request, value); + return serializeConsoleSet(request, counter, value); } // TODO: FormData is not available in old Node. Remove the typeof later. if (typeof FormData === 'function' && value instanceof FormData) {