[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.
This commit is contained in:
Sebastian Markbåge
2025-05-01 18:14:42 -04:00
committed by GitHub
parent 680e38d074
commit b75d1bf717
5 changed files with 26 additions and 24 deletions
@@ -5089,15 +5089,10 @@ export function writePreambleStart(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
willFlushAllSegments: boolean,
skipExpect?: boolean, // Used as an override by ReactFizzConfigMarkup
): void {
// This function must be called exactly once on every request
if (
enableFizzExternalRuntime &&
!willFlushAllSegments &&
renderState.externalRuntimeScript
) {
if (enableFizzExternalRuntime && renderState.externalRuntimeScript) {
// If the root segment is incomplete due to suspended tasks
// (e.g. willFlushAllSegments = false) and we are using data
// streaming format, ensure the external runtime is sent.
+21 -8
View File
@@ -3587,6 +3587,9 @@ describe('ReactDOMFizzServer', () => {
'<script type="importmap">' +
JSON.stringify(importMap) +
'</script><script async="" src="foo"></script>' +
(gate(flags => flags.shouldUseFizzExternalRuntime)
? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
: '') +
'<link rel="expect" href="#«R»" blocking="render">',
);
});
@@ -4501,7 +4504,8 @@ describe('ReactDOMFizzServer', () => {
expect(document.getElementsByTagName('script').length).toEqual(1);
});
it('does not send the external runtime for static pages', async () => {
// @gate shouldUseFizzExternalRuntime
it('does (unfortunately) send the external runtime for static pages', async () => {
await act(() => {
const {pipe} = renderToPipeableStream(
<html>
@@ -4515,11 +4519,11 @@ describe('ReactDOMFizzServer', () => {
});
// no scripts should be sent
expect(document.getElementsByTagName('script').length).toEqual(0);
expect(document.getElementsByTagName('script').length).toEqual(1);
// the html should be as-is
expect(document.documentElement.innerHTML).toEqual(
'<head><link rel="expect" href="#«R»" blocking="render"></head><body><p>hello world!</p><template id="«R»"></template></body>',
'<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>',
);
});
@@ -5317,7 +5321,9 @@ describe('ReactDOMFizzServer', () => {
});
expect(container.innerHTML).toEqual(
'<div>hello<b>world, <!-- -->Foo</b>!</div>',
(gate(flags => flags.shouldUseFizzExternalRuntime)
? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
: '') + '<div>hello<b>world, <!-- -->Foo</b>!</div>',
);
const errors = [];
ReactDOMClient.hydrateRoot(container, <App name="Foo" />, {
@@ -5518,7 +5524,7 @@ describe('ReactDOMFizzServer', () => {
pipe(writable);
});
expect(container.firstElementChild.outerHTML).toEqual(
expect(container.lastElementChild.outerHTML).toEqual(
'<div>hello<b>world<!-- --></b></div>',
);
@@ -5556,7 +5562,7 @@ describe('ReactDOMFizzServer', () => {
pipe(writable);
});
expect(container.firstElementChild.outerHTML).toEqual(
expect(container.lastElementChild.outerHTML).toEqual(
'<div>hello<b>world</b></div>',
);
@@ -5696,7 +5702,10 @@ describe('ReactDOMFizzServer', () => {
});
expect(container.innerHTML).toEqual(
'<div><!--$-->hello<!-- -->world<!-- --><!--/$--><!--$-->world<!-- --><!--/$--><!--$-->hello<!-- -->world<!-- --><br><!--/$--><!--$-->world<!-- --><br><!--/$--></div>',
(gate(flags => flags.shouldUseFizzExternalRuntime)
? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
: '') +
'<div><!--$-->hello<!-- -->world<!-- --><!--/$--><!--$-->world<!-- --><!--/$--><!--$-->hello<!-- -->world<!-- --><br><!--/$--><!--$-->world<!-- --><br><!--/$--></div>',
);
const errors = [];
@@ -6499,7 +6508,11 @@ describe('ReactDOMFizzServer', () => {
});
expect(document.documentElement.outerHTML).toEqual(
'<html><head><link rel="expect" href="#«R»" blocking="render"></head><body><script>try { foo() } catch (e) {} ;</script><template id="«R»"></template></body></html>',
'<html><head>' +
(gate(flags => flags.shouldUseFizzExternalRuntime)
? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
: '') +
'<link rel="expect" href="#«R»" blocking="render"></head><body><script>try { foo() } catch (e) {} ;</script><template id="«R»"></template></body></html>',
);
});
@@ -701,6 +701,9 @@ describe('ReactDOMFloat', () => {
});
expect(chunks).toEqual([
'<!DOCTYPE html><html><head><script async="" src="foo"></script>' +
(gate(flags => flags.shouldUseFizzExternalRuntime)
? '<script src="react-dom/unstable_server-external-runtime" async=""></script>'
: '') +
'<link rel="expect" href="#«R»" blocking="render"/><title>foo</title></head>' +
'<body>bar<template id="«R»"></template>',
'</body></html>',
-2
View File
@@ -208,14 +208,12 @@ export function writePreambleStart(
destination: Destination,
resumableState: ResumableState,
renderState: RenderState,
willFlushAllSegments: boolean,
skipExpect?: boolean, // Used as an override by ReactFizzConfigMarkup
): void {
return writePreambleStartImpl(
destination,
resumableState,
renderState,
willFlushAllSegments,
true, // skipExpect
);
}
+1 -8
View File
@@ -4835,14 +4835,7 @@ function flushPreamble(
preambleSegments: Array<Array<Segment>>,
) {
// The preamble is ready.
const willFlushAllSegments =
request.allPendingTasks === 0 && request.trackedPostpones === null;
writePreambleStart(
destination,
request.resumableState,
request.renderState,
willFlushAllSegments,
);
writePreambleStart(destination, request.resumableState, request.renderState);
for (let i = 0; i < preambleSegments.length; i++) {
const segments = preambleSegments[i];
for (let j = 0; j < segments.length; j++) {