[Flight] Serialize weird numbers (#26623)
This commit is contained in:
parent
39a3b72c6d
commit
ab2385fa38
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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})`;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue