Skip to content

Commit 270582a

Browse files
committed
[Flight] Serialize weird numbers
1 parent d121c67 commit 270582a

File tree

6 files changed

+114
-2
lines changed

6 files changed

+114
-2
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,22 @@ export function parseModelString(
559559
throw chunk.reason;
560560
}
561561
}
562+
case 'I': {
563+
// $Infinity
564+
return Infinity;
565+
}
566+
case '-': {
567+
// $-0 or $-Infinity
568+
if (value === '$-0') {
569+
return -0;
570+
} else {
571+
return -Infinity;
572+
}
573+
}
574+
case 'N': {
575+
// $NaN
576+
return NaN;
577+
}
562578
case 'u': {
563579
// matches "$undefined"
564580
// Special encoding for `undefined` which can't be serialized as JSON otherwise.

packages/react-client/src/ReactFlightReplyClient.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ function serializeSymbolReference(name: string): string {
7171
return '$S' + name;
7272
}
7373

74+
function serializeNumber(number: number): string | number {
75+
if (Number.isFinite(number)) {
76+
if (1 / number === -Infinity) {
77+
return '$-0';
78+
} else {
79+
return number;
80+
}
81+
} else {
82+
if (number === Infinity) {
83+
return '$Infinity';
84+
} else if (number === -Infinity) {
85+
return '$-Infinity';
86+
} else {
87+
return '$NaN';
88+
}
89+
}
90+
}
91+
7492
function serializeUndefined(): string {
7593
return '$undefined';
7694
}
@@ -224,10 +242,14 @@ export function processReply(
224242
return escapeStringValue(value);
225243
}
226244

227-
if (typeof value === 'boolean' || typeof value === 'number') {
245+
if (typeof value === 'boolean') {
228246
return value;
229247
}
230248

249+
if (typeof value === 'number') {
250+
return serializeNumber(value);
251+
}
252+
231253
if (typeof value === 'undefined') {
232254
return serializeUndefined();
233255
}

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,30 @@ describe('ReactFlight', () => {
263263
expect(ReactNoop).toMatchRenderedOutput(null);
264264
});
265265

266+
it('can transport weird numbers', async () => {
267+
const nums = [0, -0, Infinity, -Infinity, NaN];
268+
function ComponentClient({prop}) {
269+
expect(prop).not.toBe(nums);
270+
expect(prop).toEqual(nums);
271+
expect(prop.every((p, i) => Object.is(p, nums[i]))).toBe(true);
272+
return `prop: ${prop}`;
273+
}
274+
const Component = clientReference(ComponentClient);
275+
276+
const model = <Component prop={nums} />;
277+
278+
const transport = ReactNoopFlightServer.render(model);
279+
280+
await act(async () => {
281+
ReactNoop.render(await ReactNoopFlightClient.read(transport));
282+
});
283+
284+
expect(ReactNoop).toMatchRenderedOutput(
285+
// already checked -0 with expects above
286+
'prop: 0,0,Infinity,-Infinity,NaN',
287+
);
288+
});
289+
266290
it('can transport BigInt', async () => {
267291
function ComponentClient({prop}) {
268292
return `prop: ${prop} (${typeof prop})`;

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ describe('ReactFlightDOMReply', () => {
7676
expect(items).toEqual(['A', 'B', 'C']);
7777
});
7878

79+
it('can pass weird numbers as a reply', async () => {
80+
const nums = [0, -0, Infinity, -Infinity, NaN];
81+
const body = await ReactServerDOMClient.encodeReply(nums);
82+
const nums2 = await ReactServerDOMServer.decodeReply(
83+
body,
84+
webpackServerMap,
85+
);
86+
87+
expect(nums).toEqual(nums2);
88+
expect(nums.every((n, i) => Object.is(n, nums2[i]))).toBe(true);
89+
});
90+
7991
it('can pass a BigInt as a reply', async () => {
8092
const body = await ReactServerDOMClient.encodeReply(90071992547409910000n);
8193
const n = await ReactServerDOMServer.decodeReply(body, webpackServerMap);

packages/react-server/src/ReactFlightReplyServer.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,22 @@ function parseModelString(
397397
key,
398398
);
399399
}
400+
case 'I': {
401+
// $Infinity
402+
return Infinity;
403+
}
404+
case '-': {
405+
// $-0 or $-Infinity
406+
if (value === '$-0') {
407+
return -0;
408+
} else {
409+
return -Infinity;
410+
}
411+
}
412+
case 'N': {
413+
// $NaN
414+
return NaN;
415+
}
400416
case 'u': {
401417
// matches "$undefined"
402418
// Special encoding for `undefined` which can't be serialized as JSON otherwise.

packages/react-server/src/ReactFlightServer.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,24 @@ function serializeProviderReference(name: string): string {
549549
return '$P' + name;
550550
}
551551

552+
function serializeNumber(number: number): string | number {
553+
if (Number.isFinite(number)) {
554+
if (1 / number === -Infinity) {
555+
return '$-0';
556+
} else {
557+
return number;
558+
}
559+
} else {
560+
if (number === Infinity) {
561+
return '$Infinity';
562+
} else if (number === -Infinity) {
563+
return '$-Infinity';
564+
} else {
565+
return '$NaN';
566+
}
567+
}
568+
}
569+
552570
function serializeUndefined(): string {
553571
return '$undefined';
554572
}
@@ -877,10 +895,14 @@ export function resolveModelToJSON(
877895
return escapeStringValue(value);
878896
}
879897

880-
if (typeof value === 'boolean' || typeof value === 'number') {
898+
if (typeof value === 'boolean') {
881899
return value;
882900
}
883901

902+
if (typeof value === 'number') {
903+
return serializeNumber(value);
904+
}
905+
884906
if (typeof value === 'undefined') {
885907
return serializeUndefined();
886908
}

0 commit comments

Comments
 (0)