Skip to content

Commit f51a814

Browse files
committed
[Float][Fizz][Legacy] hoisted elements no longer emit before <html> in legacy apis such as renderToString() (#27269)
renderToString is a legacy server API which used a trick to avoid having the DOCTYPE included when rendering full documents by setting the root formatcontext to HTML_MODE rather than ROOT_HTML_MODE. Previously this was of little consequence but with Float the Root mode started to be used for things like determining if we could flush hoistable elements yet. In issue #27177 we see that hoisted elements can appear before the <html> tag when using a legacy API `renderToString`. This change exports a DOCTYPE from FizzConfigDOM and FizzConfigDOMLegacy respectively, using an empty chunk in the legacy case. The only runtime perf cost here is that for legacy APIs there is an extra empty chunk to write when rendering a top level <html> tag which is trivial enough Fixes #27177 DiffTrain build for [86198b9](86198b9)
1 parent c1e19bb commit f51a814

File tree

6 files changed

+37
-33
lines changed

6 files changed

+37
-33
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
dd480ef923930c8906a02664b01bcdea50707b5d
1+
86198b923199224b60533952b636348bb0484a6d

compiled/facebook-www/ReactDOMServer-dev.classic.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-classic-5c145eb3";
22+
var ReactVersion = "18.3.0-www-classic-2d41a773";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -2130,6 +2130,16 @@ function createFormatContext(insertionMode, selectedValue, noscriptTagInScope) {
21302130
noscriptTagInScope: noscriptTagInScope
21312131
};
21322132
}
2133+
2134+
function createRootFormatContext(namespaceURI) {
2135+
var insertionMode =
2136+
namespaceURI === "http://www.w3.org/2000/svg"
2137+
? SVG_MODE
2138+
: namespaceURI === "http://www.w3.org/1998/Math/MathML"
2139+
? MATHML_MODE
2140+
: ROOT_HTML_MODE;
2141+
return createFormatContext(insertionMode, null, false);
2142+
}
21332143
function getChildFormatContext(parentContext, type, props) {
21342144
switch (type) {
21352145
case "noscript":
@@ -4255,7 +4265,7 @@ function pushStartHtml(target, props, responseState, insertionMode) {
42554265
{
42564266
if (insertionMode === ROOT_HTML_MODE && responseState.htmlChunks === null) {
42574267
// This <html> is the Document.documentElement and should be part of the preamble
4258-
responseState.htmlChunks = [DOCTYPE];
4268+
responseState.htmlChunks = [doctypeChunk];
42594269
return pushStartGenericElement(responseState.htmlChunks, props, "html");
42604270
} else {
42614271
// This <html> is deep and is likely just an error. we emit it inline though.
@@ -4614,8 +4624,6 @@ function startChunkForTag(tag) {
46144624

46154625
return tagStartChunk;
46164626
}
4617-
4618-
var DOCTYPE = stringToPrecomputedChunk("<!DOCTYPE html>");
46194627
function pushStartInstance(
46204628
target,
46214629
type,
@@ -7093,14 +7101,8 @@ function createResponseState(
70937101
generateStaticMarkup: generateStaticMarkup
70947102
};
70957103
}
7096-
function createRootFormatContext() {
7097-
return {
7098-
insertionMode: HTML_MODE,
7099-
// We skip the root mode because we don't want to emit the DOCTYPE in legacy mode.
7100-
selectedValue: null,
7101-
noscriptTagInScope: false
7102-
};
7103-
}
7104+
7105+
var doctypeChunk = stringToPrecomputedChunk("");
71047106
function pushTextInstance(target, text, responseState, textEmbedded) {
71057107
if (responseState.generateStaticMarkup) {
71067108
target.push(stringToChunk(escapeTextForBrowser(text)));

compiled/facebook-www/ReactDOMServer-dev.modern.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-modern-0fdce021";
22+
var ReactVersion = "18.3.0-www-modern-283d577d";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -2130,6 +2130,16 @@ function createFormatContext(insertionMode, selectedValue, noscriptTagInScope) {
21302130
noscriptTagInScope: noscriptTagInScope
21312131
};
21322132
}
2133+
2134+
function createRootFormatContext(namespaceURI) {
2135+
var insertionMode =
2136+
namespaceURI === "http://www.w3.org/2000/svg"
2137+
? SVG_MODE
2138+
: namespaceURI === "http://www.w3.org/1998/Math/MathML"
2139+
? MATHML_MODE
2140+
: ROOT_HTML_MODE;
2141+
return createFormatContext(insertionMode, null, false);
2142+
}
21332143
function getChildFormatContext(parentContext, type, props) {
21342144
switch (type) {
21352145
case "noscript":
@@ -4255,7 +4265,7 @@ function pushStartHtml(target, props, responseState, insertionMode) {
42554265
{
42564266
if (insertionMode === ROOT_HTML_MODE && responseState.htmlChunks === null) {
42574267
// This <html> is the Document.documentElement and should be part of the preamble
4258-
responseState.htmlChunks = [DOCTYPE];
4268+
responseState.htmlChunks = [doctypeChunk];
42594269
return pushStartGenericElement(responseState.htmlChunks, props, "html");
42604270
} else {
42614271
// This <html> is deep and is likely just an error. we emit it inline though.
@@ -4614,8 +4624,6 @@ function startChunkForTag(tag) {
46144624

46154625
return tagStartChunk;
46164626
}
4617-
4618-
var DOCTYPE = stringToPrecomputedChunk("<!DOCTYPE html>");
46194627
function pushStartInstance(
46204628
target,
46214629
type,
@@ -7093,14 +7101,8 @@ function createResponseState(
70937101
generateStaticMarkup: generateStaticMarkup
70947102
};
70957103
}
7096-
function createRootFormatContext() {
7097-
return {
7098-
insertionMode: HTML_MODE,
7099-
// We skip the root mode because we don't want to emit the DOCTYPE in legacy mode.
7100-
selectedValue: null,
7101-
noscriptTagInScope: false
7102-
};
7103-
}
7104+
7105+
var doctypeChunk = stringToPrecomputedChunk("");
71047106
function pushTextInstance(target, text, responseState, textEmbedded) {
71057107
if (responseState.generateStaticMarkup) {
71067108
target.push(stringToChunk(escapeTextForBrowser(text)));

compiled/facebook-www/ReactDOMServer-prod.classic.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,7 @@ function pushStartInstance(
13961396
0 === formatContext.insertionMode &&
13971397
null === responseState.htmlChunks
13981398
) {
1399-
responseState.htmlChunks = ["<!DOCTYPE html>"];
1399+
responseState.htmlChunks = [""];
14001400
var JSCompiler_inline_result$jscomp$6 = pushStartGenericElement(
14011401
responseState.htmlChunks,
14021402
props,
@@ -4164,7 +4164,7 @@ function renderToStringImpl(
41644164
options ? options.identifierPrefix : void 0,
41654165
unstable_externalRuntimeSrc
41664166
),
4167-
{ insertionMode: 2, selectedValue: null, noscriptTagInScope: !1 },
4167+
createFormatContext(0, null, !1),
41684168
Infinity,
41694169
onError,
41704170
void 0,
@@ -4215,4 +4215,4 @@ exports.renderToString = function (children, options) {
42154215
'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server'
42164216
);
42174217
};
4218-
exports.version = "18.3.0-www-classic-5a0762c7";
4218+
exports.version = "18.3.0-www-classic-1ad72241";

compiled/facebook-www/ReactDOMServer-prod.modern.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,7 @@ function pushStartInstance(
13961396
0 === formatContext.insertionMode &&
13971397
null === responseState.htmlChunks
13981398
) {
1399-
responseState.htmlChunks = ["<!DOCTYPE html>"];
1399+
responseState.htmlChunks = [""];
14001400
var JSCompiler_inline_result$jscomp$6 = pushStartGenericElement(
14011401
responseState.htmlChunks,
14021402
props,
@@ -4147,7 +4147,7 @@ function renderToStringImpl(
41474147
options ? options.identifierPrefix : void 0,
41484148
unstable_externalRuntimeSrc
41494149
),
4150-
{ insertionMode: 2, selectedValue: null, noscriptTagInScope: !1 },
4150+
createFormatContext(0, null, !1),
41514151
Infinity,
41524152
onError,
41534153
void 0,
@@ -4198,4 +4198,4 @@ exports.renderToString = function (children, options) {
41984198
'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server'
41994199
);
42004200
};
4201-
exports.version = "18.3.0-www-modern-bd3b7799";
4201+
exports.version = "18.3.0-www-modern-a4787be6";

compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4262,7 +4262,7 @@ function pushStartHtml(target, props, responseState, insertionMode) {
42624262
{
42634263
if (insertionMode === ROOT_HTML_MODE && responseState.htmlChunks === null) {
42644264
// This <html> is the Document.documentElement and should be part of the preamble
4265-
responseState.htmlChunks = [DOCTYPE];
4265+
responseState.htmlChunks = [doctypeChunk];
42664266
return pushStartGenericElement(responseState.htmlChunks, props, "html");
42674267
} else {
42684268
// This <html> is deep and is likely just an error. we emit it inline though.
@@ -4622,7 +4622,7 @@ function startChunkForTag(tag) {
46224622
return tagStartChunk;
46234623
}
46244624

4625-
var DOCTYPE = stringToPrecomputedChunk("<!DOCTYPE html>");
4625+
var doctypeChunk = stringToPrecomputedChunk("<!DOCTYPE html>");
46264626
function pushStartInstance(
46274627
target,
46284628
type,

0 commit comments

Comments
 (0)