Skip to content

Commit

Permalink
Add support for undefined in Replies to match client
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed Mar 10, 2023
1 parent ef8bdbe commit 8f2f516
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
15 changes: 10 additions & 5 deletions packages/react-client/src/ReactFlightReplyClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type ReactServerValue =
| number
| symbol
| null
| void
| Iterable<ReactServerValue>
| Array<ReactServerValue>
| ReactServerObject
Expand All @@ -69,6 +70,10 @@ function serializeSymbolReference(name: string): string {
return '$S' + name;
}

function serializeUndefined(): string {
return '$undefined';
}

function escapeStringValue(value: string): string {
if (value[0] === '$') {
// We need to escape $ prefixed strings since we use those to encode
Expand Down Expand Up @@ -208,14 +213,14 @@ export function processReply(
return escapeStringValue(value);
}

if (
typeof value === 'boolean' ||
typeof value === 'number' ||
typeof value === 'undefined'
) {
if (typeof value === 'boolean' || typeof value === 'number') {
return value;
}

if (typeof value === 'undefined') {
return serializeUndefined();
}

if (typeof value === 'function') {
const metaData = knownServerReferences.get(value);
if (metaData !== undefined) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

// Polyfills for test environment
global.ReadableStream =
require('web-streams-polyfill/ponyfill/es6').ReadableStream;
global.TextEncoder = require('util').TextEncoder;
global.TextDecoder = require('util').TextDecoder;

// let serverExports;
let webpackServerMap;
let act;
let ReactServerDOMServer;
let ReactServerDOMClient;

describe('ReactFlightDOMReply', () => {
beforeEach(() => {
jest.resetModules();
act = require('internal-test-utils').act;
const WebpackMock = require('./utils/WebpackMock');
// serverExports = WebpackMock.serverExports;
webpackServerMap = WebpackMock.webpackServerMap;
ReactServerDOMServer = require('react-server-dom-webpack/server.browser');
ReactServerDOMClient = require('react-server-dom-webpack/client');
});

it('can pass undefined as a reply', async () => {
const body = await ReactServerDOMClient.encodeReply(undefined);
const missing = await ReactServerDOMServer.decodeReply(
body,
webpackServerMap,
);
expect(missing).toBe(undefined);

const body2 = await ReactServerDOMClient.encodeReply({
array: [undefined, null, undefined],
prop: undefined,
});
const object = await ReactServerDOMServer.decodeReply(
body2,
webpackServerMap,
);
expect(object.array.length).toBe(3);
expect(object.array[0]).toBe(undefined);
expect(object.array[1]).toBe(null);
expect(object.array[3]).toBe(undefined);
expect(object.prop).toBe(undefined);
// These should really be true but our deserialization doesn't currently deal with it.
expect('3' in object.array).toBe(false);
expect('prop' in object).toBe(false);
});
});
5 changes: 5 additions & 0 deletions packages/react-server/src/ReactFlightReplyServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ function parseModelString(
key,
);
}
case 'u': {
// matches "$undefined"
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
return undefined;
}
default: {
// We assume that anything else is a reference ID.
const id = parseInt(value.substring(1), 16);
Expand Down

0 comments on commit 8f2f516

Please sign in to comment.