Repository: facebook/react
Pull request: [Flight] Make byteLengthOfChunk Optional

We use this to encode the binary length of a large string without escaping it. This is really kind of optional though. This lets a Server that can't encode strings but just pass them along able to emit RSC - albeit a less optimal format.

The only build we have that does that today is react-html but the FB version of Flight had a similar constraint.

It's still possible to support binary data as long as byteLengthOfBinaryChunk is implemented which doesn't require a text encoder. Many streams (including Node streams) support binary OR string chunks.

Changed files:
filename: packages/react-dom-bindings/src/server/ReactDOMLegacyServerStreamConfig.js
patch: @@ -66,9 +66,9 @@ export function typedArrayToBinaryChunk(
   throw new Error('Not implemented.');
 }
 
-export function byteLengthOfChunk(chunk: Chunk | PrecomputedChunk): number {
-  throw new Error('Not implemented.');
-}
+export const byteLengthOfChunk:
+  | null
+  | ((chunk: Chunk | PrecomputedChunk) => number) = null;
 
 export function byteLengthOfBinaryChunk(chunk: BinaryChunk): number {
   throw new Error('Not implemented.');

filename: packages/react-html/src/__tests__/ReactHTMLClient-test.js
patch: @@ -38,6 +38,17 @@ if (!__EXPERIMENTAL__) {
       expect(html).toBe('<div>hello world</div>');
     });
 
+    it('should be able to render a large string', async () => {
+      function Component() {
+        return <div>{'hello '.repeat(200)}world</div>;
+      }
+
+      const html = await ReactHTML.renderToMarkup(
+        React.createElement(Component),
+      );
+      expect(html).toBe('<div>' + ('hello '.repeat(200) + 'world') + '</div>');
+    });
+
     it('should prefix html tags with a doctype', async () => {
       const html = await ReactHTML.renderToMarkup(
         <html>

filename: packages/react-html/src/__tests__/ReactHTMLServer-test.js
patch: @@ -61,6 +61,18 @@ if (!__EXPERIMENTAL__) {
       expect(html).toBe('<div>hello world</div>');
     });
 
+    it('should be able to render a large string', async () => {
+      function Component() {
+        // We can't use JSX because that's client-JSX in our tests.
+        return React.createElement('div', null, 'hello '.repeat(200) + 'world');
+      }
+
+      const html = await ReactHTML.renderToMarkup(
+        React.createElement(Component),
+      );
+      expect(html).toBe('<div>' + ('hello '.repeat(200) + 'world') + '</div>');
+    });
+
     it('should prefix html tags with a doctype', async () => {
       const html = await ReactHTML.renderToMarkup(
         // We can't use JSX because that's client-JSX in our tests.

filename: packages/react-server/src/ReactFlightServer.js
patch: @@ -2541,7 +2541,7 @@ function renderModelDestructive(
         return serializeDateFromDateJSON(value);
       }
     }
-    if (value.length >= 1024) {
+    if (value.length >= 1024 && byteLengthOfChunk !== null) {
       // For large strings, we encode them outside the JSON payload so that we
       // don't have to double encode and double parse the strings. This can also
       // be more compact in case the string has a lot of escaped characters.
@@ -2892,6 +2892,12 @@ function emitTypedArrayChunk(
 }
 
 function emitTextChunk(request: Request, id: number, text: string): void {
+  if (byteLengthOfChunk === null) {
+    // eslint-disable-next-line react-internal/prod-error-codes
+    throw new Error(
+      'Existence of byteLengthOfChunk should have already been checked. This is a bug in React.',
+    );
+  }
   request.pendingChunks++; // Extra chunk for the header.
   const textChunk = stringToChunk(text);
   const binaryLength = byteLengthOfChunk(textChunk);
@@ -3289,7 +3295,7 @@ function emitChunk(
   const id = task.id;
   // For certain types we have special types, we typically outlined them but
   // we can emit them directly for this row instead of through an indirection.
-  if (typeof value === 'string') {
+  if (typeof value === 'string' && byteLengthOfChunk !== null) {
     if (enableTaint) {
       const tainted = TaintRegistryValues.get(value);
       if (tainted !== undefined) {