[Flight] Add support BigInt support (#26479)
## Summary Adds support for sending `BigInt` to Flight and Flight Reply ## How did you test this change? - added tests
Sebastian Silbermann committed
Mar 29, 2023 at 18:23 UTC
fd0511c728e186905b7f0e71f072b4b247d6f29f
11 files changed
+51
-7
.eslintrc.js
+3
@@ -449,6 +449,9 @@ module.exports = {
449
$ReadOnlyArray: 'readonly',
450
$Shape: 'readonly',
451
AnimationFrameID: 'readonly',
452
+ // For Flow type annotation. Only `BigInt` is valid at runtime.
453
+ bigint: 'readonly',
454
+ BigInt: 'readonly',
455
Class: 'readonly',
456
ClientRect: 'readonly',
457
CopyInspectedElementPath: 'readonly',
packages/react-client/src/ReactFlightClient.js
+4
@@ -564,6 +564,10 @@ export function parseModelString(
564
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
565
return undefined;
566
}
567
+ case 'n': {
568
+ // BigInt
569
+ return BigInt(value.substring(2));
570
+ }
571
default: {
572
// We assume that anything else is a reference ID.
573
const id = parseInt(value.substring(1), 16);
packages/react-client/src/ReactFlightReplyClient.js
+5
-3
@@ -75,6 +75,10 @@ function serializeUndefined(): string {
75
return '$undefined';
76
}
77
78
+function serializeBigInt(n: bigint): string {
79
+ return '$n' + n.toString(10);
80
+}
81
+
82
function escapeStringValue(value: string): string {
83
if (value[0] === '$') {
84
// We need to escape $ prefixed strings since we use those to encode
@@ -264,9 +268,7 @@ export function processReply(
268
}
269
270
if (typeof value === 'bigint') {
267
- throw new Error(
268
- `BigInt (${value}) is not yet supported as an argument to a Server Function.`,
269
- );
271
+ return serializeBigInt(value);
272
}
273
274
throw new Error(
packages/react-client/src/__tests__/ReactFlight-test.js
+19
@@ -229,6 +229,25 @@ describe('ReactFlight', () => {
229
expect(ReactNoop).toMatchRenderedOutput(null);
230
});
231
232
+ it('can transport BigInt', async () => {
233
+ function ComponentClient({prop}) {
234
+ return `prop: ${prop} (${typeof prop})`;
235
+ }
236
+ const Component = clientReference(ComponentClient);
237
+
238
+ const model = <Component prop={90071992547409910000n} />;
239
+
240
+ const transport = ReactNoopFlightServer.render(model);
241
+
242
+ await act(async () => {
243
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
244
+ });
245
+
246
+ expect(ReactNoop).toMatchRenderedOutput(
247
+ 'prop: 90071992547409910000 (bigint)',
248
+ );
249
+ });
250
+
251
it('can render a lazy component as a shared component on the server', async () => {
252
function SharedComponent({text}) {
253
return (
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js
+7
@@ -75,4 +75,11 @@ describe('ReactFlightDOMReply', () => {
75
}
76
expect(items).toEqual(['A', 'B', 'C']);
77
});
78
+
79
+ it('can pass a BigInt as a reply', async () => {
80
+ const body = await ReactServerDOMClient.encodeReply(90071992547409910000n);
81
+ const n = await ReactServerDOMServer.decodeReply(body, webpackServerMap);
82
+
83
+ expect(n).toEqual(90071992547409910000n);
84
+ });
85
});
packages/react-server/src/ReactFlightReplyServer.js
+4
@@ -402,6 +402,10 @@ function parseModelString(
402
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
403
return undefined;
404
}
405
+ case 'n': {
406
+ // BigInt
407
+ return BigInt(value.substring(2));
408
+ }
409
default: {
410
// We assume that anything else is a reference ID.
411
const id = parseInt(value.substring(1), 16);
packages/react-server/src/ReactFlightServer.js
+5
-4
@@ -553,6 +553,10 @@ function serializeUndefined(): string {
553
return '$undefined';
554
}
555
556
+function serializeBigInt(n: bigint): string {
557
+ return '$n' + n.toString(10);
558
+}
559
+
560
function serializeClientReference(
561
request: Request,
562
parent:
@@ -931,10 +935,7 @@ export function resolveModelToJSON(
935
}
936
937
if (typeof value === 'bigint') {
934
- throw new Error(
935
- `BigInt (${value}) is not yet supported in Client Component props.` +
936
- describeObjectForErrorMessage(parent, key),
937
- );
938
+ return serializeBigInt(value);
939
}
940
941
throw new Error(
scripts/rollup/validate/eslintrc.esm.js
+1
@@ -7,6 +7,7 @@ module.exports = {
7
},
8
globals: {
9
// ES 6
10
+ BigInt: 'readonly',
11
Map: 'readonly',
12
Set: 'readonly',
13
Proxy: 'readonly',
scripts/rollup/validate/eslintrc.fb.js
+1
@@ -7,6 +7,7 @@ module.exports = {
7
},
8
globals: {
9
// ES6
10
+ BigInt: 'readonly',
11
Map: 'readonly',
12
Set: 'readonly',
13
Symbol: 'readonly',
scripts/rollup/validate/eslintrc.rn.js
+1
@@ -7,6 +7,7 @@ module.exports = {
7
},
8
globals: {
9
// ES6
10
+ BigInt: 'readonly',
11
Map: 'readonly',
12
Set: 'readonly',
13
Symbol: 'readonly',
scripts/rollup/validate/eslintrc.umd.js
+1
@@ -6,6 +6,7 @@ module.exports = {
6
},
7
globals: {
8
// ES6
9
+ BigInt: 'readonly',
10
Map: 'readonly',
11
Set: 'readonly',
12
Symbol: 'readonly',