@samitouri / QOS-React-2 / commits / f739642745

[Fizz] Always load the external runtime if one is provided (#33091)

Because we now decided whether to outline in the flushing phase, when we're writing the preamble we don't yet know if we will make that decision so we don't know if it's safe to omit the external runtime. However, if you are providing an external runtime it's probably a pretty safe bet you're streaming something dynamically that's likely to need it so we can always include it. The main thing is that this makes it hard to test it because it affects our tests in ways it wouldn't otherwise so we have to add a bunch of conditions.

Sebastian Markbåge committed May 1, 2025 at 18:14 UTC f739642745577a8e4dcb9753836ac3589b9c590a
5 files changed +26 -24
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+1 -6
@@ -5089,15 +5089,10 @@ export function writePreambleStart(
5089 destination: Destination,
5090 resumableState: ResumableState,
5091 renderState: RenderState,
5092 - willFlushAllSegments: boolean,
5092 skipExpect?: boolean, // Used as an override by ReactFizzConfigMarkup
5093 ): void {
5094 // This function must be called exactly once on every request
5096 - if (
5097 - enableFizzExternalRuntime &&
5098 - !willFlushAllSegments &&
5099 - renderState.externalRuntimeScript
5100 - ) {
5095 + if (enableFizzExternalRuntime && renderState.externalRuntimeScript) {
5096 // If the root segment is incomplete due to suspended tasks
5097 // (e.g. willFlushAllSegments = false) and we are using data
5098 // streaming format, ensure the external runtime is sent.
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+21 -8
@@ -3587,6 +3587,9 @@ describe('ReactDOMFizzServer', () => {
3587 '<script type="importmap">' +
3588 JSON.stringify(importMap) +
3589 '</script><script async="" src="foo"></script>' +
3590 + (gate(flags => flags.shouldUseFizzExternalRuntime)
3591 + ? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
3592 + : '') +
3593 '<link rel="expect" href="#«R»" blocking="render">',
3594 );
3595 });
@@ -4501,7 +4504,8 @@ describe('ReactDOMFizzServer', () => {
4504 expect(document.getElementsByTagName('script').length).toEqual(1);
4505 });
4506
4504 - it('does not send the external runtime for static pages', async () => {
4507 + // @gate shouldUseFizzExternalRuntime
4508 + it('does (unfortunately) send the external runtime for static pages', async () => {
4509 await act(() => {
4510 const {pipe} = renderToPipeableStream(
4511 <html>
@@ -4515,11 +4519,11 @@ describe('ReactDOMFizzServer', () => {
4519 });
4520
4521 // no scripts should be sent
4518 - expect(document.getElementsByTagName('script').length).toEqual(0);
4522 + expect(document.getElementsByTagName('script').length).toEqual(1);
4523
4524 // the html should be as-is
4525 expect(document.documentElement.innerHTML).toEqual(
4522 - '<head><link rel="expect" href="#«R»" blocking="render"></head><body><p>hello world!</p><template id="«R»"></template></body>',
4526 + '<head><script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script><link rel="expect" href="#«R»" blocking="render"></head><body><p>hello world!</p><template id="«R»"></template></body>',
4527 );
4528 });
4529
@@ -5317,7 +5321,9 @@ describe('ReactDOMFizzServer', () => {
5321 });
5322
5323 expect(container.innerHTML).toEqual(
5320 - '<div>hello<b>world, <!-- -->Foo</b>!</div>',
5324 + (gate(flags => flags.shouldUseFizzExternalRuntime)
5325 + ? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
5326 + : '') + '<div>hello<b>world, <!-- -->Foo</b>!</div>',
5327 );
5328 const errors = [];
5329 ReactDOMClient.hydrateRoot(container, <App name="Foo" />, {
@@ -5518,7 +5524,7 @@ describe('ReactDOMFizzServer', () => {
5524 pipe(writable);
5525 });
5526
5521 - expect(container.firstElementChild.outerHTML).toEqual(
5527 + expect(container.lastElementChild.outerHTML).toEqual(
5528 '<div>hello<b>world<!-- --></b></div>',
5529 );
5530
@@ -5556,7 +5562,7 @@ describe('ReactDOMFizzServer', () => {
5562 pipe(writable);
5563 });
5564
5559 - expect(container.firstElementChild.outerHTML).toEqual(
5565 + expect(container.lastElementChild.outerHTML).toEqual(
5566 '<div>hello<b>world</b></div>',
5567 );
5568
@@ -5696,7 +5702,10 @@ describe('ReactDOMFizzServer', () => {
5702 });
5703
5704 expect(container.innerHTML).toEqual(
5699 - '<div><!--$-->hello<!-- -->world<!-- --><!--/$--><!--$-->world<!-- --><!--/$--><!--$-->hello<!-- -->world<!-- --><br><!--/$--><!--$-->world<!-- --><br><!--/$--></div>',
5705 + (gate(flags => flags.shouldUseFizzExternalRuntime)
5706 + ? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
5707 + : '') +
5708 + '<div><!--$-->hello<!-- -->world<!-- --><!--/$--><!--$-->world<!-- --><!--/$--><!--$-->hello<!-- -->world<!-- --><br><!--/$--><!--$-->world<!-- --><br><!--/$--></div>',
5709 );
5710
5711 const errors = [];
@@ -6499,7 +6508,11 @@ describe('ReactDOMFizzServer', () => {
6508 });
6509
6510 expect(document.documentElement.outerHTML).toEqual(
6502 - '<html><head><link rel="expect" href="#«R»" blocking="render"></head><body><script>try { foo() } catch (e) {} ;</script><template id="«R»"></template></body></html>',
6511 + '<html><head>' +
6512 + (gate(flags => flags.shouldUseFizzExternalRuntime)
6513 + ? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
6514 + : '') +
6515 + '<link rel="expect" href="#«R»" blocking="render"></head><body><script>try { foo() } catch (e) {} ;</script><template id="«R»"></template></body></html>',
6516 );
6517 });
6518
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+3
@@ -701,6 +701,9 @@ describe('ReactDOMFloat', () => {
701 });
702 expect(chunks).toEqual([
703 '<!DOCTYPE html><html><head><script async="" src="foo"></script>' +
704 + (gate(flags => flags.shouldUseFizzExternalRuntime)
705 + ? '<script src="react-dom/unstable_server-external-runtime" async=""></script>'
706 + : '') +
707 '<link rel="expect" href="#«R»" blocking="render"/><title>foo</title></head>' +
708 '<body>bar<template id="«R»"></template>',
709 '</body></html>',
packages/react-markup/src/ReactFizzConfigMarkup.js
-2
@@ -208,14 +208,12 @@ export function writePreambleStart(
208 destination: Destination,
209 resumableState: ResumableState,
210 renderState: RenderState,
211 - willFlushAllSegments: boolean,
211 skipExpect?: boolean, // Used as an override by ReactFizzConfigMarkup
212 ): void {
213 return writePreambleStartImpl(
214 destination,
215 resumableState,
216 renderState,
218 - willFlushAllSegments,
217 true, // skipExpect
218 );
219 }
packages/react-server/src/ReactFizzServer.js
+1 -8
@@ -4835,14 +4835,7 @@ function flushPreamble(
4835 preambleSegments: Array<Array<Segment>>,
4836 ) {
4837 // The preamble is ready.
4838 - const willFlushAllSegments =
4839 - request.allPendingTasks === 0 && request.trackedPostpones === null;
4840 - writePreambleStart(
4841 - destination,
4842 - request.resumableState,
4843 - request.renderState,
4844 - willFlushAllSegments,
4845 - );
4838 + writePreambleStart(destination, request.resumableState, request.renderState);
4839 for (let i = 0; i < preambleSegments.length; i++) {
4840 const segments = preambleSegments[i];
4841 for (let j = 0; j < segments.length; j++) {