[Flight] Serialize weird numbers (#26623)

This commit is contained in:
Sophie Alpert 2023-04-14 09:28:48 -07:00 committed by GitHub
parent 39a3b72c6d
commit ab2385fa38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 2 deletions

View File

@ -559,6 +559,22 @@ export function parseModelString(
throw chunk.reason;
}
}
case 'I': {
// $Infinity
return Infinity;
}
case '-': {
// $-0 or $-Infinity
if (value[2] === '0') {
return -0;
} else {
return -Infinity;
}
}
case 'N': {
// $NaN
return NaN;
}
case 'u': {
// matches "$undefined"
// Special encoding for `undefined` which can't be serialized as JSON otherwise.

View File

@ -71,6 +71,24 @@ function serializeSymbolReference(name: string): string {
return '$S' + name;
}
function serializeNumber(number: number): string | number {
if (Number.isFinite(number)) {
if (number === 0 && 1 / number === -Infinity) {
return '$-0';
} else {
return number;
}
} else {
if (number === Infinity) {
return '$Infinity';
} else if (number === -Infinity) {
return '$-Infinity';
} else {
return '$NaN';
}
}
}
function serializeUndefined(): string {
return '$undefined';
}
@ -224,10 +242,14 @@ export function processReply(
return escapeStringValue(value);
}
if (typeof value === 'boolean' || typeof value === 'number') {
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return serializeNumber(value);
}
if (typeof value === 'undefined') {
return serializeUndefined();
}

View File

@ -263,6 +263,30 @@ describe('ReactFlight', () => {
expect(ReactNoop).toMatchRenderedOutput(null);
});
it('can transport weird numbers', async () => {
const nums = [0, -0, Infinity, -Infinity, NaN];
function ComponentClient({prop}) {
expect(prop).not.toBe(nums);
expect(prop).toEqual(nums);
expect(prop.every((p, i) => Object.is(p, nums[i]))).toBe(true);
return `prop: ${prop}`;
}
const Component = clientReference(ComponentClient);
const model = <Component prop={nums} />;
const transport = ReactNoopFlightServer.render(model);
await act(async () => {
ReactNoop.render(await ReactNoopFlightClient.read(transport));
});
expect(ReactNoop).toMatchRenderedOutput(
// already checked -0 with expects above
'prop: 0,0,Infinity,-Infinity,NaN',
);
});
it('can transport BigInt', async () => {
function ComponentClient({prop}) {
return `prop: ${prop} (${typeof prop})`;

View File

@ -76,6 +76,18 @@ describe('ReactFlightDOMReply', () => {
expect(items).toEqual(['A', 'B', 'C']);
});
it('can pass weird numbers as a reply', async () => {
const nums = [0, -0, Infinity, -Infinity, NaN];
const body = await ReactServerDOMClient.encodeReply(nums);
const nums2 = await ReactServerDOMServer.decodeReply(
body,
webpackServerMap,
);
expect(nums).toEqual(nums2);
expect(nums.every((n, i) => Object.is(n, nums2[i]))).toBe(true);
});
it('can pass a BigInt as a reply', async () => {
const body = await ReactServerDOMClient.encodeReply(90071992547409910000n);
const n = await ReactServerDOMServer.decodeReply(body, webpackServerMap);

View File

@ -397,6 +397,22 @@ function parseModelString(
key,
);
}
case 'I': {
// $Infinity
return Infinity;
}
case '-': {
// $-0 or $-Infinity
if (value[2] === '0') {
return -0;
} else {
return -Infinity;
}
}
case 'N': {
// $NaN
return NaN;
}
case 'u': {
// matches "$undefined"
// Special encoding for `undefined` which can't be serialized as JSON otherwise.

View File

@ -549,6 +549,24 @@ function serializeProviderReference(name: string): string {
return '$P' + name;
}
function serializeNumber(number: number): string | number {
if (Number.isFinite(number)) {
if (number === 0 && 1 / number === -Infinity) {
return '$-0';
} else {
return number;
}
} else {
if (number === Infinity) {
return '$Infinity';
} else if (number === -Infinity) {
return '$-Infinity';
} else {
return '$NaN';
}
}
}
function serializeUndefined(): string {
return '$undefined';
}
@ -877,10 +895,14 @@ export function resolveModelToJSON(
return escapeStringValue(value);
}
if (typeof value === 'boolean' || typeof value === 'number') {
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return serializeNumber(value);
}
if (typeof value === 'undefined') {
return serializeUndefined();
}