@samitouri / QOS-React-2 / commits / 208ceeb46c

Cleanup enableFloat flag (#28613)

Cleanup enableFloat flag

Jan Kassens committed Mar 22, 2024 at 12:22 UTC 208ceeb46ca2838c9bf24cd341435f87b2d50569
31 files changed +599 -1047
packages/react-dom-bindings/src/client/ReactDOMComponentTree.js
+3 -3
@@ -34,7 +34,7 @@ import {
34
35 import {getParentSuspenseInstance} from './ReactFiberConfigDOM';
36
37 -import {enableScopeAPI, enableFloat} from 'shared/ReactFeatureFlags';
37 +import {enableScopeAPI} from 'shared/ReactFeatureFlags';
38
39 const randomKey = Math.random().toString(36).slice(2);
40 const internalInstanceKey = '__reactFiber$' + randomKey;
@@ -175,7 +175,7 @@ export function getInstanceFromNode(node: Node): Fiber | null {
175 tag === HostComponent ||
176 tag === HostText ||
177 tag === SuspenseComponent ||
178 - (enableFloat ? tag === HostHoistable : false) ||
178 + tag === HostHoistable ||
179 tag === HostSingleton ||
180 tag === HostRoot
181 ) {
@@ -195,7 +195,7 @@ export function getNodeFromInstance(inst: Fiber): Instance | TextInstance {
195 const tag = inst.tag;
196 if (
197 tag === HostComponent ||
198 - (enableFloat ? tag === HostHoistable : false) ||
198 + tag === HostHoistable ||
199 tag === HostSingleton ||
200 tag === HostText
201 ) {
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
-22
@@ -91,7 +91,6 @@ import {
91 enableBigIntSupport,
92 enableCreateEventHandleAPI,
93 enableScopeAPI,
94 - enableFloat,
94 enableTrustedTypesIntegration,
95 enableFormActions,
96 enableAsyncActions,
@@ -2161,25 +2160,16 @@ function preconnectAs(
2160 }
2161
2162 function prefetchDNS(href: string) {
2164 - if (!enableFloat) {
2165 - return;
2166 - }
2163 previousDispatcher.prefetchDNS(href);
2164 preconnectAs('dns-prefetch', href, null);
2165 }
2166
2167 function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
2172 - if (!enableFloat) {
2173 - return;
2174 - }
2168 previousDispatcher.preconnect(href, crossOrigin);
2169 preconnectAs('preconnect', href, crossOrigin);
2170 }
2171
2172 function preload(href: string, as: string, options?: ?PreloadImplOptions) {
2180 - if (!enableFloat) {
2181 - return;
2182 - }
2173 previousDispatcher.preload(href, as, options);
2174 const ownerDocument = getGlobalDocument();
2175 if (ownerDocument && href && as) {
@@ -2258,9 +2248,6 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) {
2248 }
2249
2250 function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
2261 - if (!enableFloat) {
2262 - return;
2263 - }
2251 previousDispatcher.preloadModule(href, options);
2252 const ownerDocument = getGlobalDocument();
2253 if (ownerDocument && href) {
@@ -2322,9 +2309,6 @@ function preinitStyle(
2309 precedence: ?string,
2310 options?: ?PreinitStyleOptions,
2311 ) {
2325 - if (!enableFloat) {
2326 - return;
2327 - }
2312 previousDispatcher.preinitStyle(href, precedence, options);
2313
2314 const ownerDocument = getGlobalDocument();
@@ -2399,9 +2383,6 @@ function preinitStyle(
2383 }
2384
2385 function preinitScript(src: string, options?: ?PreinitScriptOptions) {
2402 - if (!enableFloat) {
2403 - return;
2404 - }
2386 previousDispatcher.preinitScript(src, options);
2387
2388 const ownerDocument = getGlobalDocument();
@@ -2458,9 +2439,6 @@ function preinitModuleScript(
2439 src: string,
2440 options?: ?PreinitModuleScriptOptions,
2441 ) {
2461 - if (!enableFloat) {
2462 - return;
2463 - }
2442 previousDispatcher.preinitModuleScript(src, options);
2443
2444 const ownerDocument = getGlobalDocument();
packages/react-dom-bindings/src/events/DOMPluginEventSystem.js
+4 -5
@@ -52,7 +52,6 @@ import {
52 enableLegacyFBSupport,
53 enableCreateEventHandleAPI,
54 enableScopeAPI,
55 - enableFloat,
55 enableFormActions,
56 } from 'shared/ReactFeatureFlags';
57 import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener';
@@ -647,7 +646,7 @@ export function dispatchEventForPluginEventSystem(
646 if (
647 parentTag === HostComponent ||
648 parentTag === HostText ||
650 - (enableFloat ? parentTag === HostHoistable : false) ||
649 + parentTag === HostHoistable ||
650 parentTag === HostSingleton
651 ) {
652 node = ancestorInst = parentNode;
@@ -705,7 +704,7 @@ export function accumulateSinglePhaseListeners(
704 // Handle listeners that are on HostComponents (i.e. <div>)
705 if (
706 (tag === HostComponent ||
708 - (enableFloat ? tag === HostHoistable : false) ||
707 + tag === HostHoistable ||
708 tag === HostSingleton) &&
709 stateNode !== null
710 ) {
@@ -819,7 +818,7 @@ export function accumulateTwoPhaseListeners(
818 // Handle listeners that are on HostComponents (i.e. <div>)
819 if (
820 (tag === HostComponent ||
822 - (enableFloat ? tag === HostHoistable : false) ||
821 + tag === HostHoistable ||
822 tag === HostSingleton) &&
823 stateNode !== null
824 ) {
@@ -922,7 +921,7 @@ function accumulateEnterLeaveListenersForEvent(
921 }
922 if (
923 (tag === HostComponent ||
925 - (enableFloat ? tag === HostHoistable : false) ||
924 + tag === HostHoistable ||
925 tag === HostSingleton) &&
926 stateNode !== null
927 ) {
packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js
+122 -138
@@ -16,8 +16,6 @@ import type {
16 PreinitModuleScriptOptions,
17 } from 'react-dom/src/shared/ReactDOMTypes';
18
19 -import {enableFloat} from 'shared/ReactFeatureFlags';
20 -
19 import {
20 emitHint,
21 getHints,
@@ -40,107 +38,99 @@ ReactDOMCurrentDispatcher.current = {
38 };
39
40 function prefetchDNS(href: string) {
43 - if (enableFloat) {
44 - if (typeof href === 'string' && href) {
45 - const request = resolveRequest();
46 - if (request) {
47 - const hints = getHints(request);
48 - const key = 'D|' + href;
49 - if (hints.has(key)) {
50 - // duplicate hint
51 - return;
52 - }
53 - hints.add(key);
54 - emitHint(request, 'D', href);
55 - } else {
56 - previousDispatcher.prefetchDNS(href);
41 + if (typeof href === 'string' && href) {
42 + const request = resolveRequest();
43 + if (request) {
44 + const hints = getHints(request);
45 + const key = 'D|' + href;
46 + if (hints.has(key)) {
47 + // duplicate hint
48 + return;
49 }
50 + hints.add(key);
51 + emitHint(request, 'D', href);
52 + } else {
53 + previousDispatcher.prefetchDNS(href);
54 }
55 }
56 }
57
58 function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
63 - if (enableFloat) {
64 - if (typeof href === 'string') {
65 - const request = resolveRequest();
66 - if (request) {
67 - const hints = getHints(request);
59 + if (typeof href === 'string') {
60 + const request = resolveRequest();
61 + if (request) {
62 + const hints = getHints(request);
63
69 - const key = `C|${crossOrigin == null ? 'null' : crossOrigin}|${href}`;
70 - if (hints.has(key)) {
71 - // duplicate hint
72 - return;
73 - }
74 - hints.add(key);
75 - if (typeof crossOrigin === 'string') {
76 - emitHint(request, 'C', [href, crossOrigin]);
77 - } else {
78 - emitHint(request, 'C', href);
79 - }
64 + const key = `C|${crossOrigin == null ? 'null' : crossOrigin}|${href}`;
65 + if (hints.has(key)) {
66 + // duplicate hint
67 + return;
68 + }
69 + hints.add(key);
70 + if (typeof crossOrigin === 'string') {
71 + emitHint(request, 'C', [href, crossOrigin]);
72 } else {
81 - previousDispatcher.preconnect(href, crossOrigin);
73 + emitHint(request, 'C', href);
74 }
75 + } else {
76 + previousDispatcher.preconnect(href, crossOrigin);
77 }
78 }
79 }
80
81 function preload(href: string, as: string, options?: ?PreloadImplOptions) {
88 - if (enableFloat) {
89 - if (typeof href === 'string') {
90 - const request = resolveRequest();
91 - if (request) {
92 - const hints = getHints(request);
93 - let key = 'L';
94 - if (as === 'image' && options) {
95 - key += getImagePreloadKey(
96 - href,
97 - options.imageSrcSet,
98 - options.imageSizes,
99 - );
100 - } else {
101 - key += `[${as}]${href}`;
102 - }
103 - if (hints.has(key)) {
104 - // duplicate hint
105 - return;
106 - }
107 - hints.add(key);
82 + if (typeof href === 'string') {
83 + const request = resolveRequest();
84 + if (request) {
85 + const hints = getHints(request);
86 + let key = 'L';
87 + if (as === 'image' && options) {
88 + key += getImagePreloadKey(
89 + href,
90 + options.imageSrcSet,
91 + options.imageSizes,
92 + );
93 + } else {
94 + key += `[${as}]${href}`;
95 + }
96 + if (hints.has(key)) {
97 + // duplicate hint
98 + return;
99 + }
100 + hints.add(key);
101
109 - const trimmed = trimOptions(options);
110 - if (trimmed) {
111 - emitHint(request, 'L', [href, as, trimmed]);
112 - } else {
113 - emitHint(request, 'L', [href, as]);
114 - }
102 + const trimmed = trimOptions(options);
103 + if (trimmed) {
104 + emitHint(request, 'L', [href, as, trimmed]);
105 } else {
116 - previousDispatcher.preload(href, as, options);
106 + emitHint(request, 'L', [href, as]);
107 }
108 + } else {
109 + previousDispatcher.preload(href, as, options);
110 }
111 }
112 }
113
114 function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
123 - if (enableFloat) {
124 - if (typeof href === 'string') {
125 - const request = resolveRequest();
126 - if (request) {
127 - const hints = getHints(request);
128 - const key = 'm|' + href;
129 - if (hints.has(key)) {
130 - // duplicate hint
131 - return;
132 - }
133 - hints.add(key);
115 + if (typeof href === 'string') {
116 + const request = resolveRequest();
117 + if (request) {
118 + const hints = getHints(request);
119 + const key = 'm|' + href;
120 + if (hints.has(key)) {
121 + // duplicate hint
122 + return;
123 + }
124 + hints.add(key);
125
135 - const trimmed = trimOptions(options);
136 - if (trimmed) {
137 - return emitHint(request, 'm', [href, trimmed]);
138 - } else {
139 - return emitHint(request, 'm', href);
140 - }
126 + const trimmed = trimOptions(options);
127 + if (trimmed) {
128 + return emitHint(request, 'm', [href, trimmed]);
129 } else {
142 - previousDispatcher.preloadModule(href, options);
130 + return emitHint(request, 'm', href);
131 }
132 + } else {
133 + previousDispatcher.preloadModule(href, options);
134 }
135 }
136 }
@@ -150,59 +140,55 @@ function preinitStyle(
140 precedence: ?string,
141 options?: ?PreinitStyleOptions,
142 ) {
153 - if (enableFloat) {
154 - if (typeof href === 'string') {
155 - const request = resolveRequest();
156 - if (request) {
157 - const hints = getHints(request);
158 - const key = 'S|' + href;
159 - if (hints.has(key)) {
160 - // duplicate hint
161 - return;
162 - }
163 - hints.add(key);
143 + if (typeof href === 'string') {
144 + const request = resolveRequest();
145 + if (request) {
146 + const hints = getHints(request);
147 + const key = 'S|' + href;
148 + if (hints.has(key)) {
149 + // duplicate hint
150 + return;
151 + }
152 + hints.add(key);
153
165 - const trimmed = trimOptions(options);
166 - if (trimmed) {
167 - return emitHint(request, 'S', [
168 - href,
169 - typeof precedence === 'string' ? precedence : 0,
170 - trimmed,
171 - ]);
172 - } else if (typeof precedence === 'string') {
173 - return emitHint(request, 'S', [href, precedence]);
174 - } else {
175 - return emitHint(request, 'S', href);
176 - }
154 + const trimmed = trimOptions(options);
155 + if (trimmed) {
156 + return emitHint(request, 'S', [
157 + href,
158 + typeof precedence === 'string' ? precedence : 0,
159 + trimmed,
160 + ]);
161 + } else if (typeof precedence === 'string') {
162 + return emitHint(request, 'S', [href, precedence]);
163 } else {
178 - previousDispatcher.preinitStyle(href, precedence, options);
164 + return emitHint(request, 'S', href);
165 }
166 + } else {
167 + previousDispatcher.preinitStyle(href, precedence, options);
168 }
169 }
170 }
171
172 function preinitScript(src: string, options?: ?PreinitScriptOptions) {
185 - if (enableFloat) {
186 - if (typeof src === 'string') {
187 - const request = resolveRequest();
188 - if (request) {
189 - const hints = getHints(request);
190 - const key = 'X|' + src;
191 - if (hints.has(key)) {
192 - // duplicate hint
193 - return;
194 - }
195 - hints.add(key);
173 + if (typeof src === 'string') {
174 + const request = resolveRequest();
175 + if (request) {
176 + const hints = getHints(request);
177 + const key = 'X|' + src;
178 + if (hints.has(key)) {
179 + // duplicate hint
180 + return;
181 + }
182 + hints.add(key);
183
197 - const trimmed = trimOptions(options);
198 - if (trimmed) {
199 - return emitHint(request, 'X', [src, trimmed]);
200 - } else {
201 - return emitHint(request, 'X', src);
202 - }
184 + const trimmed = trimOptions(options);
185 + if (trimmed) {
186 + return emitHint(request, 'X', [src, trimmed]);
187 } else {
204 - previousDispatcher.preinitScript(src, options);
188 + return emitHint(request, 'X', src);
189 }
190 + } else {
191 + previousDispatcher.preinitScript(src, options);
192 }
193 }
194 }
@@ -211,27 +197,25 @@ function preinitModuleScript(
197 src: string,
198 options?: ?PreinitModuleScriptOptions,
199 ) {
214 - if (enableFloat) {
215 - if (typeof src === 'string') {
216 - const request = resolveRequest();
217 - if (request) {
218 - const hints = getHints(request);
219 - const key = 'M|' + src;
220 - if (hints.has(key)) {
221 - // duplicate hint
222 - return;
223 - }
224 - hints.add(key);
200 + if (typeof src === 'string') {
201 + const request = resolveRequest();
202 + if (request) {
203 + const hints = getHints(request);
204 + const key = 'M|' + src;
205 + if (hints.has(key)) {
206 + // duplicate hint
207 + return;
208 + }
209 + hints.add(key);
210
226 - const trimmed = trimOptions(options);
227 - if (trimmed) {
228 - return emitHint(request, 'M', [src, trimmed]);
229 - } else {
230 - return emitHint(request, 'M', src);
231 - }
211 + const trimmed = trimOptions(options);
212 + if (trimmed) {
213 + return emitHint(request, 'M', [src, trimmed]);
214 } else {
233 - previousDispatcher.preinitModuleScript(src, options);
215 + return emitHint(request, 'M', src);
216 }
217 + } else {
218 + previousDispatcher.preinitModuleScript(src, options);
219 }
220 }
221 }
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+351 -506
@@ -31,7 +31,6 @@ import {
31 enableBigIntSupport,
32 enableFilterEmptyStringAttributesDOM,
33 enableCustomElementPropertySupport,
34 - enableFloat,
34 enableFormActions,
35 enableFizzExternalRuntime,
36 enableNewBooleanProps,
@@ -383,11 +382,6 @@ export function createRenderState(
382 );
383 }
384 if (enableFizzExternalRuntime) {
386 - if (!enableFloat) {
387 - throw new Error(
388 - 'enableFizzExternalRuntime without enableFloat is not supported. This should never appear in production, since it means you are using a misconfigured React bundle.',
389 - );
390 - }
385 if (externalRuntimeConfig !== undefined) {
386 if (typeof externalRuntimeConfig === 'string') {
387 externalRuntimeScript = {
@@ -2333,44 +2327,40 @@ function pushMeta(
2327 noscriptTagInScope: boolean,
2328 isFallback: boolean,
2329 ): null {
2336 - if (enableFloat) {
2337 - if (
2338 - insertionMode === SVG_MODE ||
2339 - noscriptTagInScope ||
2340 - props.itemProp != null
2341 - ) {
2342 - return pushSelfClosing(target, props, 'meta');
2343 - } else {
2344 - if (textEmbedded) {
2345 - // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2346 - // to be safe and assume text will follow by inserting a textSeparator
2347 - target.push(textSeparator);
2348 - }
2330 + if (
2331 + insertionMode === SVG_MODE ||
2332 + noscriptTagInScope ||
2333 + props.itemProp != null
2334 + ) {
2335 + return pushSelfClosing(target, props, 'meta');
2336 + } else {
2337 + if (textEmbedded) {
2338 + // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2339 + // to be safe and assume text will follow by inserting a textSeparator
2340 + target.push(textSeparator);
2341 + }
2342
2350 - if (isFallback) {
2351 - // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2352 - // because they are likely superceded by primary content and we want to avoid needing to clean
2353 - // them up when the primary content is ready. They are never hydrated on the client anyway because
2354 - // boundaries in fallback are awaited or client render, in either case there is never hydration
2355 - return null;
2356 - } else if (typeof props.charSet === 'string') {
2357 - // "charset" Should really be config and not picked up from tags however since this is
2358 - // the only way to embed the tag today we flush it on a special queue on the Request so it
2359 - // can go before everything else. Like viewport this means that the tag will escape it's
2360 - // parent container.
2361 - return pushSelfClosing(renderState.charsetChunks, props, 'meta');
2362 - } else if (props.name === 'viewport') {
2363 - // "viewport" is flushed on the Request so it can go earlier that Float resources that
2364 - // might be affected by it. This means it can escape the boundary it is rendered within.
2365 - // This is a pragmatic solution to viewport being incredibly sensitive to document order
2366 - // without requiring all hoistables to be flushed too early.
2367 - return pushSelfClosing(renderState.viewportChunks, props, 'meta');
2368 - } else {
2369 - return pushSelfClosing(renderState.hoistableChunks, props, 'meta');
2370 - }
2343 + if (isFallback) {
2344 + // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2345 + // because they are likely superceded by primary content and we want to avoid needing to clean
2346 + // them up when the primary content is ready. They are never hydrated on the client anyway because
2347 + // boundaries in fallback are awaited or client render, in either case there is never hydration
2348 + return null;
2349 + } else if (typeof props.charSet === 'string') {
2350 + // "charset" Should really be config and not picked up from tags however since this is
2351 + // the only way to embed the tag today we flush it on a special queue on the Request so it
2352 + // can go before everything else. Like viewport this means that the tag will escape it's
2353 + // parent container.
2354 + return pushSelfClosing(renderState.charsetChunks, props, 'meta');
2355 + } else if (props.name === 'viewport') {
2356 + // "viewport" is flushed on the Request so it can go earlier that Float resources that
2357 + // might be affected by it. This means it can escape the boundary it is rendered within.
2358 + // This is a pragmatic solution to viewport being incredibly sensitive to document order
2359 + // without requiring all hoistables to be flushed too early.
2360 + return pushSelfClosing(renderState.viewportChunks, props, 'meta');
2361 + } else {
2362 + return pushSelfClosing(renderState.hoistableChunks, props, 'meta');
2363 }
2372 - } else {
2373 - return pushSelfClosing(target, props, 'meta');
2364 }
2365 }
2366
@@ -2385,172 +2375,168 @@ function pushLink(
2375 noscriptTagInScope: boolean,
2376 isFallback: boolean,
2377 ): null {
2388 - if (enableFloat) {
2389 - const rel = props.rel;
2390 - const href = props.href;
2391 - const precedence = props.precedence;
2378 + const rel = props.rel;
2379 + const href = props.href;
2380 + const precedence = props.precedence;
2381 + if (
2382 + insertionMode === SVG_MODE ||
2383 + noscriptTagInScope ||
2384 + props.itemProp != null ||
2385 + typeof rel !== 'string' ||
2386 + typeof href !== 'string' ||
2387 + href === ''
2388 + ) {
2389 + if (__DEV__) {
2390 + if (rel === 'stylesheet' && typeof props.precedence === 'string') {
2391 + if (typeof href !== 'string' || !href) {
2392 + console.error(
2393 + 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and expected the `href` prop to be a non-empty string but ecountered %s instead. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop ensure there is a non-empty string `href` prop as well, otherwise remove the `precedence` prop.',
2394 + getValueDescriptorExpectingObjectForWarning(href),
2395 + );
2396 + }
2397 + }
2398 + }
2399 + pushLinkImpl(target, props);
2400 + return null;
2401 + }
2402 +
2403 + if (props.rel === 'stylesheet') {
2404 + // This <link> may hoistable as a Stylesheet Resource, otherwise it will emit in place
2405 + const key = getResourceKey(href);
2406 if (
2393 - insertionMode === SVG_MODE ||
2394 - noscriptTagInScope ||
2395 - props.itemProp != null ||
2396 - typeof rel !== 'string' ||
2397 - typeof href !== 'string' ||
2398 - href === ''
2407 + typeof precedence !== 'string' ||
2408 + props.disabled != null ||
2409 + props.onLoad ||
2410 + props.onError
2411 ) {
2412 + // This stylesheet is either not opted into Resource semantics or has conflicting properties which
2413 + // disqualify it for such. We can still create a preload resource to help it load faster on the
2414 + // client
2415 if (__DEV__) {
2401 - if (rel === 'stylesheet' && typeof props.precedence === 'string') {
2402 - if (typeof href !== 'string' || !href) {
2416 + if (typeof precedence === 'string') {
2417 + if (props.disabled != null) {
2418 + console.error(
2419 + 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and a `disabled` prop. The presence of the `disabled` prop indicates an intent to manage the stylesheet active state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the `disabled` prop, otherwise remove the `precedence` prop.',
2420 + );
2421 + } else if (props.onLoad || props.onError) {
2422 + const propDescription =
2423 + props.onLoad && props.onError
2424 + ? '`onLoad` and `onError` props'
2425 + : props.onLoad
2426 + ? '`onLoad` prop'
2427 + : '`onError` prop';
2428 console.error(
2404 - 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and expected the `href` prop to be a non-empty string but ecountered %s instead. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop ensure there is a non-empty string `href` prop as well, otherwise remove the `precedence` prop.',
2405 - getValueDescriptorExpectingObjectForWarning(href),
2429 + 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and %s. The presence of loading and error handlers indicates an intent to manage the stylesheet loading state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the %s, otherwise remove the `precedence` prop.',
2430 + propDescription,
2431 + propDescription,
2432 );
2433 }
2434 }
2435 }
2410 - pushLinkImpl(target, props);
2411 - return null;
2412 - }
2413 -
2414 - if (props.rel === 'stylesheet') {
2415 - // This <link> may hoistable as a Stylesheet Resource, otherwise it will emit in place
2416 - const key = getResourceKey(href);
2417 - if (
2418 - typeof precedence !== 'string' ||
2419 - props.disabled != null ||
2420 - props.onLoad ||
2421 - props.onError
2422 - ) {
2423 - // This stylesheet is either not opted into Resource semantics or has conflicting properties which
2424 - // disqualify it for such. We can still create a preload resource to help it load faster on the
2425 - // client
2426 - if (__DEV__) {
2427 - if (typeof precedence === 'string') {
2428 - if (props.disabled != null) {
2429 - console.error(
2430 - 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and a `disabled` prop. The presence of the `disabled` prop indicates an intent to manage the stylesheet active state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the `disabled` prop, otherwise remove the `precedence` prop.',
2431 - );
2432 - } else if (props.onLoad || props.onError) {
2433 - const propDescription =
2434 - props.onLoad && props.onError
2435 - ? '`onLoad` and `onError` props'
2436 - : props.onLoad
2437 - ? '`onLoad` prop'
2438 - : '`onError` prop';
2439 - console.error(
2440 - 'React encountered a `<link rel="stylesheet" .../>` with a `precedence` prop and %s. The presence of loading and error handlers indicates an intent to manage the stylesheet loading state from your from your Component code and React will not hoist or deduplicate this stylesheet. If your intent was to have React hoist and deduplciate this stylesheet using the `precedence` prop remove the %s, otherwise remove the `precedence` prop.',
2441 - propDescription,
2442 - propDescription,
2443 - );
2444 - }
2445 - }
2446 - }
2447 - return pushLinkImpl(target, props);
2448 - } else {
2449 - // This stylesheet refers to a Resource and we create a new one if necessary
2450 - let styleQueue = renderState.styles.get(precedence);
2451 - const hasKey = resumableState.styleResources.hasOwnProperty(key);
2452 - const resourceState = hasKey
2453 - ? resumableState.styleResources[key]
2454 - : undefined;
2455 - if (resourceState !== EXISTS) {
2456 - // We are going to create this resource now so it is marked as Exists
2457 - resumableState.styleResources[key] = EXISTS;
2458 -
2459 - // If this is the first time we've encountered this precedence we need
2460 - // to create a StyleQueue
2461 - if (!styleQueue) {
2462 - styleQueue = {
2463 - precedence: stringToChunk(escapeTextForBrowser(precedence)),
2464 - rules: ([]: Array<Chunk | PrecomputedChunk>),
2465 - hrefs: ([]: Array<Chunk | PrecomputedChunk>),
2466 - sheets: (new Map(): Map<string, StylesheetResource>),
2467 - };
2468 - renderState.styles.set(precedence, styleQueue);
2469 - }
2470 -
2471 - const resource: StylesheetResource = {
2472 - state: PENDING,
2473 - props: stylesheetPropsFromRawProps(props),
2436 + return pushLinkImpl(target, props);
2437 + } else {
2438 + // This stylesheet refers to a Resource and we create a new one if necessary
2439 + let styleQueue = renderState.styles.get(precedence);
2440 + const hasKey = resumableState.styleResources.hasOwnProperty(key);
2441 + const resourceState = hasKey
2442 + ? resumableState.styleResources[key]
2443 + : undefined;
2444 + if (resourceState !== EXISTS) {
2445 + // We are going to create this resource now so it is marked as Exists
2446 + resumableState.styleResources[key] = EXISTS;
2447 +
2448 + // If this is the first time we've encountered this precedence we need
2449 + // to create a StyleQueue
2450 + if (!styleQueue) {
2451 + styleQueue = {
2452 + precedence: stringToChunk(escapeTextForBrowser(precedence)),
2453 + rules: ([]: Array<Chunk | PrecomputedChunk>),
2454 + hrefs: ([]: Array<Chunk | PrecomputedChunk>),
2455 + sheets: (new Map(): Map<string, StylesheetResource>),
2456 };
2457 + renderState.styles.set(precedence, styleQueue);
2458 + }
2459
2476 - if (resourceState) {
2477 - // When resourceState is truty it is a Preload state. We cast it for clarity
2478 - const preloadState: Preloaded | PreloadedWithCredentials =
2479 - resourceState;
2480 - if (preloadState.length === 2) {
2481 - adoptPreloadCredentials(resource.props, preloadState);
2482 - }
2460 + const resource: StylesheetResource = {
2461 + state: PENDING,
2462 + props: stylesheetPropsFromRawProps(props),
2463 + };
2464
2484 - const preloadResource = renderState.preloads.stylesheets.get(key);
2485 - if (preloadResource && preloadResource.length > 0) {
2486 - // The Preload for this resource was created in this render pass and has not flushed yet so
2487 - // we need to clear it to avoid it flushing.
2488 - preloadResource.length = 0;
2489 - } else {
2490 - // Either the preload resource from this render already flushed in this render pass
2491 - // or the preload flushed in a prior pass (prerender). In either case we need to mark
2492 - // this resource as already having been preloaded.
2493 - resource.state = PRELOADED;
2494 - }
2495 - } else {
2496 - // We don't need to check whether a preloadResource exists in the renderState
2497 - // because if it did exist then the resourceState would also exist and we would
2498 - // have hit the primary if condition above.
2465 + if (resourceState) {
2466 + // When resourceState is truty it is a Preload state. We cast it for clarity
2467 + const preloadState: Preloaded | PreloadedWithCredentials =
2468 + resourceState;
2469 + if (preloadState.length === 2) {
2470 + adoptPreloadCredentials(resource.props, preloadState);
2471 }
2472
2501 - // We add the newly created resource to our StyleQueue and if necessary
2502 - // track the resource with the currently rendering boundary
2503 - styleQueue.sheets.set(key, resource);
2504 - if (hoistableState) {
2505 - hoistableState.stylesheets.add(resource);
2473 + const preloadResource = renderState.preloads.stylesheets.get(key);
2474 + if (preloadResource && preloadResource.length > 0) {
2475 + // The Preload for this resource was created in this render pass and has not flushed yet so
2476 + // we need to clear it to avoid it flushing.
2477 + preloadResource.length = 0;
2478 + } else {
2479 + // Either the preload resource from this render already flushed in this render pass
2480 + // or the preload flushed in a prior pass (prerender). In either case we need to mark
2481 + // this resource as already having been preloaded.
2482 + resource.state = PRELOADED;
2483 }
2484 } else {
2508 - // We need to track whether this boundary should wait on this resource or not.
2509 - // Typically this resource should always exist since we either had it or just created
2510 - // it. However, it's possible when you resume that the style has already been emitted
2511 - // and then it wouldn't be recreated in the RenderState and there's no need to track
2512 - // it again since we should've hoisted it to the shell already.
2513 - if (styleQueue) {
2514 - const resource = styleQueue.sheets.get(key);
2515 - if (resource) {
2516 - if (hoistableState) {
2517 - hoistableState.stylesheets.add(resource);
2518 - }
2485 + // We don't need to check whether a preloadResource exists in the renderState
2486 + // because if it did exist then the resourceState would also exist and we would
2487 + // have hit the primary if condition above.
2488 + }
2489 +
2490 + // We add the newly created resource to our StyleQueue and if necessary
2491 + // track the resource with the currently rendering boundary
2492 + styleQueue.sheets.set(key, resource);
2493 + if (hoistableState) {
2494 + hoistableState.stylesheets.add(resource);
2495 + }
2496 + } else {
2497 + // We need to track whether this boundary should wait on this resource or not.
2498 + // Typically this resource should always exist since we either had it or just created
2499 + // it. However, it's possible when you resume that the style has already been emitted
2500 + // and then it wouldn't be recreated in the RenderState and there's no need to track
2501 + // it again since we should've hoisted it to the shell already.
2502 + if (styleQueue) {
2503 + const resource = styleQueue.sheets.get(key);
2504 + if (resource) {
2505 + if (hoistableState) {
2506 + hoistableState.stylesheets.add(resource);
2507 }
2508 }
2509 }
2522 - if (textEmbedded) {
2523 - // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2524 - // to be safe and assume text will follow by inserting a textSeparator
2525 - target.push(textSeparator);
2526 - }
2527 - return null;
2510 }
2529 - } else if (props.onLoad || props.onError) {
2530 - // When using load handlers we cannot hoist and need to emit links in place
2531 - return pushLinkImpl(target, props);
2532 - } else {
2533 - // We can hoist this link so we may need to emit a text separator.
2534 - // @TODO refactor text separators so we don't have to defensively add
2535 - // them when we don't end up emitting a tag as a result of pushStartInstance
2511 if (textEmbedded) {
2512 // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2513 // to be safe and assume text will follow by inserting a textSeparator
2514 target.push(textSeparator);
2515 }
2541 -
2542 - if (isFallback) {
2543 - // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2544 - // because they are likely superceded by primary content and we want to avoid needing to clean
2545 - // them up when the primary content is ready. They are never hydrated on the client anyway because
2546 - // boundaries in fallback are awaited or client render, in either case there is never hydration
2547 - return null;
2548 - } else {
2549 - return pushLinkImpl(renderState.hoistableChunks, props);
2550 - }
2516 + return null;
2517 }
2552 - } else {
2518 + } else if (props.onLoad || props.onError) {
2519 + // When using load handlers we cannot hoist and need to emit links in place
2520 return pushLinkImpl(target, props);
2521 + } else {
2522 + // We can hoist this link so we may need to emit a text separator.
2523 + // @TODO refactor text separators so we don't have to defensively add
2524 + // them when we don't end up emitting a tag as a result of pushStartInstance
2525 + if (textEmbedded) {
2526 + // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2527 + // to be safe and assume text will follow by inserting a textSeparator
2528 + target.push(textSeparator);
2529 + }
2530 +
2531 + if (isFallback) {
2532 + // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2533 + // because they are likely superceded by primary content and we want to avoid needing to clean
2534 + // them up when the primary content is ready. They are never hydrated on the client anyway because
2535 + // boundaries in fallback are awaited or client render, in either case there is never hydration
2536 + return null;
2537 + } else {
2538 + return pushLinkImpl(renderState.hoistableChunks, props);
2539 + }
2540 }
2541 }
2542
@@ -2623,84 +2609,78 @@ function pushStyle(
2609 }
2610 }
2611 }
2626 - if (enableFloat) {
2627 - const precedence = props.precedence;
2628 - const href = props.href;
2612 + const precedence = props.precedence;
2613 + const href = props.href;
2614
2630 - if (
2631 - insertionMode === SVG_MODE ||
2632 - noscriptTagInScope ||
2633 - props.itemProp != null ||
2634 - typeof precedence !== 'string' ||
2635 - typeof href !== 'string' ||
2636 - href === ''
2637 - ) {
2638 - // This style tag is not able to be turned into a Style Resource
2639 - return pushStyleImpl(target, props);
2615 + if (
2616 + insertionMode === SVG_MODE ||
2617 + noscriptTagInScope ||
2618 + props.itemProp != null ||
2619 + typeof precedence !== 'string' ||
2620 + typeof href !== 'string' ||
2621 + href === ''
2622 + ) {
2623 + // This style tag is not able to be turned into a Style Resource
2624 + return pushStyleImpl(target, props);
2625 + }
2626 +
2627 + if (__DEV__) {
2628 + if (href.includes(' ')) {
2629 + console.error(
2630 + 'React expected the `href` prop for a <style> tag opting into hoisting semantics using the `precedence` prop to not have any spaces but ecountered spaces instead. using spaces in this prop will cause hydration of this style to fail on the client. The href for the <style> where this ocurred is "%s".',
2631 + href,
2632 + );
2633 }
2634 + }
2635 +
2636 + const key = getResourceKey(href);
2637 + let styleQueue = renderState.styles.get(precedence);
2638 + const hasKey = resumableState.styleResources.hasOwnProperty(key);
2639 + const resourceState = hasKey ? resumableState.styleResources[key] : undefined;
2640 + if (resourceState !== EXISTS) {
2641 + // We are going to create this resource now so it is marked as Exists
2642 + resumableState.styleResources[key] = EXISTS;
2643
2644 if (__DEV__) {
2643 - if (href.includes(' ')) {
2645 + if (resourceState) {
2646 console.error(
2645 - 'React expected the `href` prop for a <style> tag opting into hoisting semantics using the `precedence` prop to not have any spaces but ecountered spaces instead. using spaces in this prop will cause hydration of this style to fail on the client. The href for the <style> where this ocurred is "%s".',
2647 + 'React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.',
2648 href,
2649 );
2650 }
2651 }
2652
2651 - const key = getResourceKey(href);
2652 - let styleQueue = renderState.styles.get(precedence);
2653 - const hasKey = resumableState.styleResources.hasOwnProperty(key);
2654 - const resourceState = hasKey
2655 - ? resumableState.styleResources[key]
2656 - : undefined;
2657 - if (resourceState !== EXISTS) {
2658 - // We are going to create this resource now so it is marked as Exists
2659 - resumableState.styleResources[key] = EXISTS;
2660 -
2661 - if (__DEV__) {
2662 - if (resourceState) {
2663 - console.error(
2664 - 'React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.',
2665 - href,
2666 - );
2667 - }
2668 - }
2669 -
2670 - if (!styleQueue) {
2671 - // This is the first time we've encountered this precedence we need
2672 - // to create a StyleQueue.
2673 - styleQueue = {
2674 - precedence: stringToChunk(escapeTextForBrowser(precedence)),
2675 - rules: ([]: Array<Chunk | PrecomputedChunk>),
2676 - hrefs: [stringToChunk(escapeTextForBrowser(href))],
2677 - sheets: (new Map(): Map<string, StylesheetResource>),
2678 - };
2679 - renderState.styles.set(precedence, styleQueue);
2680 - } else {
2681 - // We have seen this precedence before and need to track this href
2682 - styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
2683 - }
2684 - pushStyleContents(styleQueue.rules, props);
2653 + if (!styleQueue) {
2654 + // This is the first time we've encountered this precedence we need
2655 + // to create a StyleQueue.
2656 + styleQueue = {
2657 + precedence: stringToChunk(escapeTextForBrowser(precedence)),
2658 + rules: ([]: Array<Chunk | PrecomputedChunk>),
2659 + hrefs: [stringToChunk(escapeTextForBrowser(href))],
2660 + sheets: (new Map(): Map<string, StylesheetResource>),
2661 + };
2662 + renderState.styles.set(precedence, styleQueue);
2663 + } else {
2664 + // We have seen this precedence before and need to track this href
2665 + styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
2666 }
2686 - if (styleQueue) {
2687 - // We need to track whether this boundary should wait on this resource or not.
2688 - // Typically this resource should always exist since we either had it or just created
2689 - // it. However, it's possible when you resume that the style has already been emitted
2690 - // and then it wouldn't be recreated in the RenderState and there's no need to track
2691 - // it again since we should've hoisted it to the shell already.
2692 - if (hoistableState) {
2693 - hoistableState.styles.add(styleQueue);
2694 - }
2667 + pushStyleContents(styleQueue.rules, props);
2668 + }
2669 + if (styleQueue) {
2670 + // We need to track whether this boundary should wait on this resource or not.
2671 + // Typically this resource should always exist since we either had it or just created
2672 + // it. However, it's possible when you resume that the style has already been emitted
2673 + // and then it wouldn't be recreated in the RenderState and there's no need to track
2674 + // it again since we should've hoisted it to the shell already.
2675 + if (hoistableState) {
2676 + hoistableState.styles.add(styleQueue);
2677 }
2678 + }
2679
2697 - if (textEmbedded) {
2698 - // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2699 - // to be safe and assume text will follow by inserting a textSeparator
2700 - target.push(textSeparator);
2701 - }
2702 - } else {
2703 - return pushStartGenericElement(target, props, 'style');
2680 + if (textEmbedded) {
2681 + // This link follows text but we aren't writing a tag. while not as efficient as possible we need
2682 + // to be safe and assume text will follow by inserting a textSeparator
2683 + target.push(textSeparator);
2684 }
2685 }
2686
@@ -3057,23 +3037,19 @@ function pushTitle(
3037 }
3038 }
3039
3060 - if (enableFloat) {
3061 - if (
3062 - insertionMode !== SVG_MODE &&
3063 - !noscriptTagInScope &&
3064 - props.itemProp == null
3065 - ) {
3066 - if (isFallback) {
3067 - // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
3068 - // because they are likely superceded by primary content and we want to avoid needing to clean
3069 - // them up when the primary content is ready. They are never hydrated on the client anyway because
3070 - // boundaries in fallback are awaited or client render, in either case there is never hydration
3071 - return null;
3072 - } else {
3073 - pushTitleImpl(renderState.hoistableChunks, props);
3074 - }
3040 + if (
3041 + insertionMode !== SVG_MODE &&
3042 + !noscriptTagInScope &&
3043 + props.itemProp == null
3044 + ) {
3045 + if (isFallback) {
3046 + // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
3047 + // because they are likely superceded by primary content and we want to avoid needing to clean
3048 + // them up when the primary content is ready. They are never hydrated on the client anyway because
3049 + // boundaries in fallback are awaited or client render, in either case there is never hydration
3050 + return null;
3051 } else {
3076 - return pushTitleImpl(target, props);
3052 + pushTitleImpl(renderState.hoistableChunks, props);
3053 }
3054 } else {
3055 return pushTitleImpl(target, props);
@@ -3128,97 +3104,19 @@ function pushTitleImpl(
3104 return null;
3105 }
3106
3131 -function pushStartTitle(
3132 - target: Array<Chunk | PrecomputedChunk>,
3133 - props: Object,
3134 -): ReactNodeList {
3135 - target.push(startChunkForTag('title'));
3136 -
3137 - let children = null;
3138 - for (const propKey in props) {
3139 - if (hasOwnProperty.call(props, propKey)) {
3140 - const propValue = props[propKey];
3141 - if (propValue == null) {
3142 - continue;
3143 - }
3144 - switch (propKey) {
3145 - case 'children':
3146 - children = propValue;
3147 - break;
3148 - case 'dangerouslySetInnerHTML':
3149 - throw new Error(
3150 - '`dangerouslySetInnerHTML` does not make sense on <title>.',
3151 - );
3152 - default:
3153 - pushAttribute(target, propKey, propValue);
3154 - break;
3155 - }
3156 - }
3157 - }
3158 - target.push(endOfStartTag);
3159 -
3160 - if (__DEV__) {
3161 - const childForValidation =
3162 - Array.isArray(children) && children.length < 2
3163 - ? children[0] || null
3164 - : children;
3165 - if (Array.isArray(children) && children.length > 1) {
3166 - console.error(
3167 - 'A title element received an array with more than 1 element as children. ' +
3168 - 'In browsers title Elements can only have Text Nodes as children. If ' +
3169 - 'the children being rendered output more than a single text node in aggregate the browser ' +
3170 - 'will display markup and comments as text in the title and hydration will likely fail and ' +
3171 - 'fall back to client rendering',
3172 - );
3173 - } else if (
3174 - childForValidation != null &&
3175 - childForValidation.$$typeof != null
3176 - ) {
3177 - console.error(
3178 - 'A title element received a React element for children. ' +
3179 - 'In the browser title Elements can only have Text Nodes as children. If ' +
3180 - 'the children being rendered output more than a single text node in aggregate the browser ' +
3181 - 'will display markup and comments as text in the title and hydration will likely fail and ' +
3182 - 'fall back to client rendering',
3183 - );
3184 - } else if (
3185 - childForValidation != null &&
3186 - typeof childForValidation !== 'string' &&
3187 - typeof childForValidation !== 'number' &&
3188 - ((enableBigIntSupport && typeof childForValidation !== 'bigint') ||
3189 - !enableBigIntSupport)
3190 - ) {
3191 - console.error(
3192 - 'A title element received a value that was not a string or number%s for children. ' +
3193 - 'In the browser title Elements can only have Text Nodes as children. If ' +
3194 - 'the children being rendered output more than a single text node in aggregate the browser ' +
3195 - 'will display markup and comments as text in the title and hydration will likely fail and ' +
3196 - 'fall back to client rendering',
3197 - enableBigIntSupport ? ' or bigint' : '',
3198 - );
3199 - }
3200 - }
3201 -
3202 - return children;
3203 -}
3204 -
3107 function pushStartHead(
3108 target: Array<Chunk | PrecomputedChunk>,
3109 props: Object,
3110 renderState: RenderState,
3111 insertionMode: InsertionMode,
3112 ): ReactNodeList {
3211 - if (enableFloat) {
3212 - if (insertionMode < HTML_MODE && renderState.headChunks === null) {
3213 - // This <head> is the Document.head and should be part of the preamble
3214 - renderState.headChunks = [];
3215 - return pushStartGenericElement(renderState.headChunks, props, 'head');
3216 - } else {
3217 - // This <head> is deep and is likely just an error. we emit it inline though.
3218 - // Validation should warn that this tag is the the wrong spot.
3219 - return pushStartGenericElement(target, props, 'head');
3220 - }
3113 + if (insertionMode < HTML_MODE && renderState.headChunks === null) {
3114 + // This <head> is the Document.head and should be part of the preamble
3115 + renderState.headChunks = [];
3116 + return pushStartGenericElement(renderState.headChunks, props, 'head');
3117 } else {
3118 + // This <head> is deep and is likely just an error. we emit it inline though.
3119 + // Validation should warn that this tag is the the wrong spot.
3120 return pushStartGenericElement(target, props, 'head');
3121 }
3122 }
@@ -3229,23 +3127,13 @@ function pushStartHtml(
3127 renderState: RenderState,
3128 insertionMode: InsertionMode,
3129 ): ReactNodeList {
3232 - if (enableFloat) {
3233 - if (insertionMode === ROOT_HTML_MODE && renderState.htmlChunks === null) {
3234 - // This <html> is the Document.documentElement and should be part of the preamble
3235 - renderState.htmlChunks = [DOCTYPE];
3236 - return pushStartGenericElement(renderState.htmlChunks, props, 'html');
3237 - } else {
3238 - // This <html> is deep and is likely just an error. we emit it inline though.
3239 - // Validation should warn that this tag is the the wrong spot.
3240 - return pushStartGenericElement(target, props, 'html');
3241 - }
3130 + if (insertionMode === ROOT_HTML_MODE && renderState.htmlChunks === null) {
3131 + // This <html> is the Document.documentElement and should be part of the preamble
3132 + renderState.htmlChunks = [DOCTYPE];
3133 + return pushStartGenericElement(renderState.htmlChunks, props, 'html');
3134 } else {
3243 - if (insertionMode === ROOT_HTML_MODE) {
3244 - // If we're rendering the html tag and we're at the root (i.e. not in foreignObject)
3245 - // then we also emit the DOCTYPE as part of the root content as a convenience for
3246 - // rendering the whole document.
3247 - target.push(DOCTYPE);
3248 - }
3135 + // This <html> is deep and is likely just an error. we emit it inline though.
3136 + // Validation should warn that this tag is the the wrong spot.
3137 return pushStartGenericElement(target, props, 'html');
3138 }
3139 }
@@ -3259,80 +3147,75 @@ function pushScript(
3147 insertionMode: InsertionMode,
3148 noscriptTagInScope: boolean,
3149 ): null {
3262 - if (enableFloat) {
3263 - const asyncProp = props.async;
3264 - if (
3265 - typeof props.src !== 'string' ||
3266 - !props.src ||
3267 - !(
3268 - asyncProp &&
3269 - typeof asyncProp !== 'function' &&
3270 - typeof asyncProp !== 'symbol'
3271 - ) ||
3272 - props.onLoad ||
3273 - props.onError ||
3274 - insertionMode === SVG_MODE ||
3275 - noscriptTagInScope ||
3276 - props.itemProp != null
3277 - ) {
3278 - // This script will not be a resource, we bailout early and emit it in place.
3279 - return pushScriptImpl(target, props);
3280 - }
3150 + const asyncProp = props.async;
3151 + if (
3152 + typeof props.src !== 'string' ||
3153 + !props.src ||
3154 + !(
3155 + asyncProp &&
3156 + typeof asyncProp !== 'function' &&
3157 + typeof asyncProp !== 'symbol'
3158 + ) ||
3159 + props.onLoad ||
3160 + props.onError ||
3161 + insertionMode === SVG_MODE ||
3162 + noscriptTagInScope ||
3163 + props.itemProp != null
3164 + ) {
3165 + // This script will not be a resource, we bailout early and emit it in place.
3166 + return pushScriptImpl(target, props);
3167 + }
3168
3282 - const src = props.src;
3283 - const key = getResourceKey(src);
3284 - // We can make this <script> into a ScriptResource
3169 + const src = props.src;
3170 + const key = getResourceKey(src);
3171 + // We can make this <script> into a ScriptResource
3172
3286 - let resources, preloads;
3287 - if (props.type === 'module') {
3288 - resources = resumableState.moduleScriptResources;
3289 - preloads = renderState.preloads.moduleScripts;
3290 - } else {
3291 - resources = resumableState.scriptResources;
3292 - preloads = renderState.preloads.scripts;
3293 - }
3173 + let resources, preloads;
3174 + if (props.type === 'module') {
3175 + resources = resumableState.moduleScriptResources;
3176 + preloads = renderState.preloads.moduleScripts;
3177 + } else {
3178 + resources = resumableState.scriptResources;
3179 + preloads = renderState.preloads.scripts;
3180 + }
3181
3295 - const hasKey = resources.hasOwnProperty(key);
3296 - const resourceState = hasKey ? resources[key] : undefined;
3297 - if (resourceState !== EXISTS) {
3298 - // We are going to create this resource now so it is marked as Exists
3299 - resources[key] = EXISTS;
3182 + const hasKey = resources.hasOwnProperty(key);
3183 + const resourceState = hasKey ? resources[key] : undefined;
3184 + if (resourceState !== EXISTS) {
3185 + // We are going to create this resource now so it is marked as Exists
3186 + resources[key] = EXISTS;
3187
3301 - let scriptProps = props;
3302 - if (resourceState) {
3303 - // When resourceState is truty it is a Preload state. We cast it for clarity
3304 - const preloadState: Preloaded | PreloadedWithCredentials =
3305 - resourceState;
3306 - if (preloadState.length === 2) {
3307 - scriptProps = {...props};
3308 - adoptPreloadCredentials(scriptProps, preloadState);
3309 - }
3310 -
3311 - const preloadResource = preloads.get(key);
3312 - if (preloadResource) {
3313 - // the preload resource exists was created in this render. Now that we have
3314 - // a script resource which will emit earlier than a preload would if it
3315 - // hasn't already flushed we prevent it from flushing by zeroing the length
3316 - preloadResource.length = 0;
3317 - }
3188 + let scriptProps = props;
3189 + if (resourceState) {
3190 + // When resourceState is truty it is a Preload state. We cast it for clarity
3191 + const preloadState: Preloaded | PreloadedWithCredentials = resourceState;
3192 + if (preloadState.length === 2) {
3193 + scriptProps = {...props};
3194 + adoptPreloadCredentials(scriptProps, preloadState);
3195 }
3196
3320 - const resource: Resource = [];
3321 - // Add to the script flushing queue
3322 - renderState.scripts.add(resource);
3323 - // encode the tag as Chunks
3324 - pushScriptImpl(resource, scriptProps);
3197 + const preloadResource = preloads.get(key);
3198 + if (preloadResource) {
3199 + // the preload resource exists was created in this render. Now that we have
3200 + // a script resource which will emit earlier than a preload would if it
3201 + // hasn't already flushed we prevent it from flushing by zeroing the length
3202 + preloadResource.length = 0;
3203 + }
3204 }
3205
3327 - if (textEmbedded) {
3328 - // This script follows text but we aren't writing a tag. while not as efficient as possible we need
3329 - // to be safe and assume text will follow by inserting a textSeparator
3330 - target.push(textSeparator);
3331 - }
3332 - return null;
3333 - } else {
3334 - return pushScriptImpl(target, props);
3206 + const resource: Resource = [];
3207 + // Add to the script flushing queue
3208 + renderState.scripts.add(resource);
3209 + // encode the tag as Chunks
3210 + pushScriptImpl(resource, scriptProps);
3211 + }
3212 +
3213 + if (textEmbedded) {
3214 + // This script follows text but we aren't writing a tag. while not as efficient as possible we need
3215 + // to be safe and assume text will follow by inserting a textSeparator
3216 + target.push(textSeparator);
3217 }
3218 + return null;
3219 }
3220
3221 function pushScriptImpl(
@@ -3678,16 +3561,14 @@ export function pushStartInstance(
3561 case 'menuitem':
3562 return pushStartMenuItem(target, props);
3563 case 'title':
3681 - return enableFloat
3682 - ? pushTitle(
3683 - target,
3684 - props,
3685 - renderState,
3686 - formatContext.insertionMode,
3687 - !!(formatContext.tagScope & NOSCRIPT_SCOPE),
3688 - isFallback,
3689 - )
3690 - : pushStartTitle(target, props);
3564 + return pushTitle(
3565 + target,
3566 + props,
3567 + renderState,
3568 + formatContext.insertionMode,
3569 + !!(formatContext.tagScope & NOSCRIPT_SCOPE),
3570 + isFallback,
3571 + );
3572 case 'link':
3573 return pushLink(
3574 target,
@@ -3701,17 +3582,15 @@ export function pushStartInstance(
3582 isFallback,
3583 );
3584 case 'script':
3704 - return enableFloat
3705 - ? pushScript(
3706 - target,
3707 - props,
3708 - resumableState,
3709 - renderState,
3710 - textEmbedded,
3711 - formatContext.insertionMode,
3712 - !!(formatContext.tagScope & NOSCRIPT_SCOPE),
3713 - )
3714 - : pushStartGenericElement(target, props, type);
3585 + return pushScript(
3586 + target,
3587 + props,
3588 + resumableState,
3589 + renderState,
3590 + textEmbedded,
3591 + formatContext.insertionMode,
3592 + !!(formatContext.tagScope & NOSCRIPT_SCOPE),
3593 + );
3594 case 'style':
3595 return pushStyle(
3596 target,
@@ -3739,15 +3618,13 @@ export function pushStartInstance(
3618 return pushStartPreformattedElement(target, props, type);
3619 }
3620 case 'img': {
3742 - return enableFloat
3743 - ? pushImg(
3744 - target,
3745 - props,
3746 - resumableState,
3747 - renderState,
3748 - !!(formatContext.tagScope & PICTURE_SCOPE),
3749 - )
3750 - : pushSelfClosing(target, props, type);
3621 + return pushImg(
3622 + target,
3623 + props,
3624 + resumableState,
3625 + renderState,
3626 + !!(formatContext.tagScope & PICTURE_SCOPE),
3627 + );
3628 }
3629 // Omitted close tags
3630 case 'base':
@@ -3820,21 +3697,16 @@ export function pushEndInstance(
3697 formatContext: FormatContext,
3698 ): void {
3699 switch (type) {
3823 - // When float is on we expect title and script tags to always be pushed in
3824 - // a unit and never return children. when we end up pushing the end tag we
3825 - // want to ensure there is no extra closing tag pushed
3700 + // We expect title and script tags to always be pushed in a unit and never
3701 + // return children. when we end up pushing the end tag we want to ensure
3702 + // there is no extra closing tag pushed
3703 case 'title':
3704 case 'style':
3828 - case 'script': {
3829 - if (!enableFloat) {
3830 - break;
3831 - }
3832 - // Fall through
3833 - }
3834 -
3705 + case 'script':
3706 // Omitted close tags
3707 // TODO: Instead of repeating this switch we could try to pass a flag from above.
3708 // That would require returning a tuple. Which might be ok if it gets inlined.
3709 + // fallthrough
3710 case 'area':
3711 case 'base':
3712 case 'br':
@@ -3859,14 +3731,14 @@ export function pushEndInstance(
3731 // This is so we can withhold them until the postamble when we know
3732 // we won't emit any more tags
3733 case 'body': {
3862 - if (enableFloat && formatContext.insertionMode <= HTML_HTML_MODE) {
3734 + if (formatContext.insertionMode <= HTML_HTML_MODE) {
3735 resumableState.hasBody = true;
3736 return;
3737 }
3738 break;
3739 }
3740 case 'html':
3869 - if (enableFloat && formatContext.insertionMode === ROOT_HTML_MODE) {
3741 + if (formatContext.insertionMode === ROOT_HTML_MODE) {
3742 resumableState.hasHtml = true;
3743 return;
3744 }
@@ -4270,21 +4142,18 @@ export function writeCompletedBoundaryInstruction(
4142 id: number,
4143 hoistableState: HoistableState,
4144 ): boolean {
4273 - let requiresStyleInsertion;
4274 - if (enableFloat) {
4275 - requiresStyleInsertion = renderState.stylesToHoist;
4276 - // If necessary stylesheets will be flushed with this instruction.
4277 - // Any style tags not yet hoisted in the Document will also be hoisted.
4278 - // We reset this state since after this instruction executes all styles
4279 - // up to this point will have been hoisted
4280 - renderState.stylesToHoist = false;
4281 - }
4145 + const requiresStyleInsertion = renderState.stylesToHoist;
4146 + // If necessary stylesheets will be flushed with this instruction.
4147 + // Any style tags not yet hoisted in the Document will also be hoisted.
4148 + // We reset this state since after this instruction executes all styles
4149 + // up to this point will have been hoisted
4150 + renderState.stylesToHoist = false;
4151 const scriptFormat =
4152 !enableFizzExternalRuntime ||
4153 resumableState.streamingFormat === ScriptStreamingFormat;
4154 if (scriptFormat) {
4155 writeChunk(destination, renderState.startInlineScript);
4287 - if (enableFloat && requiresStyleInsertion) {
4156 + if (requiresStyleInsertion) {
4157 if (
4158 (resumableState.instructions & SentCompleteBoundaryFunction) ===
4159 NothingSent
@@ -4314,7 +4183,7 @@ export function writeCompletedBoundaryInstruction(
4183 }
4184 }
4185 } else {
4317 - if (enableFloat && requiresStyleInsertion) {
4186 + if (requiresStyleInsertion) {
4187 writeChunk(destination, completeBoundaryWithStylesData1);
4188 } else {
4189 writeChunk(destination, completeBoundaryData1);
@@ -4334,7 +4203,7 @@ export function writeCompletedBoundaryInstruction(
4203 }
4204 writeChunk(destination, renderState.segmentPrefix);
4205 writeChunk(destination, idChunk);
4337 - if (enableFloat && requiresStyleInsertion) {
4206 + if (requiresStyleInsertion) {
4207 // Script and data writers must format this differently:
4208 // - script writer emits an array literal, whose string elements are
4209 // escaped for javascript e.g. ["A", "B"]
@@ -5384,9 +5253,6 @@ function getImageResourceKey(
5253 }
5254
5255 function prefetchDNS(href: string) {
5387 - if (!enableFloat) {
5388 - return;
5389 - }
5256 const request = resolveRequest();
5257 if (!request) {
5258 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -5440,9 +5306,6 @@ function prefetchDNS(href: string) {
5306 }
5307
5308 function preconnect(href: string, crossOrigin: ?CrossOriginEnum) {
5443 - if (!enableFloat) {
5444 - return;
5445 - }
5309 const request = resolveRequest();
5310 if (!request) {
5311 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -5504,9 +5367,6 @@ function preconnect(href: string, crossOrigin: ?CrossOriginEnum) {
5367 }
5368
5369 function preload(href: string, as: string, options?: ?PreloadImplOptions) {
5507 - if (!enableFloat) {
5508 - return;
5509 - }
5370 const request = resolveRequest();
5371 if (!request) {
5372 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -5708,9 +5568,6 @@ function preloadModule(
5568 href: string,
5569 options?: ?PreloadModuleImplOptions,
5570 ): void {
5711 - if (!enableFloat) {
5712 - return;
5713 - }
5571 const request = resolveRequest();
5572 if (!request) {
5573 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -5785,9 +5642,6 @@ function preinitStyle(
5642 precedence: ?string,
5643 options?: ?PreinitStyleOptions,
5644 ): void {
5788 - if (!enableFloat) {
5789 - return;
5790 - }
5645 const request = resolveRequest();
5646 if (!request) {
5647 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -5873,9 +5727,6 @@ function preinitStyle(
5727 }
5728
5729 function preinitScript(src: string, options?: ?PreinitScriptOptions): void {
5876 - if (!enableFloat) {
5877 - return;
5878 - }
5730 const request = resolveRequest();
5731 if (!request) {
5732 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -5939,9 +5790,6 @@ function preinitModuleScript(
5790 src: string,
5791 options?: ?PreinitModuleScriptOptions,
5792 ): void {
5942 - if (!enableFloat) {
5943 - return;
5944 - }
5793 const request = resolveRequest();
5794 if (!request) {
5795 // In async contexts we can sometimes resolve resources from AsyncLocalStorage. If we can't we can also
@@ -6011,9 +5859,6 @@ function preloadBootstrapScriptOrModule(
5859 href: string,
5860 props: PreloadProps,
5861 ): void {
6014 - if (!enableFloat) {
6015 - return;
6016 - }
5862 const key = getResourceKey(href);
5863
5864 if (__DEV__) {
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+40 -107
@@ -5080,7 +5080,6 @@ describe('ReactDOMFizzServer', () => {
5080 );
5081 });
5082
5083 - // @gate enableFloat
5083 it('can emit the preamble even if the head renders asynchronously', async () => {
5084 function AsyncNoOutput() {
5085 readText('nooutput');
@@ -5133,7 +5132,6 @@ describe('ReactDOMFizzServer', () => {
5132 );
5133 });
5134
5136 - // @gate enableFloat
5135 it('holds back body and html closing tags (the postamble) until all pending tasks are completed', async () => {
5136 const chunks = [];
5137 writable.on('data', chunk => {
@@ -5734,14 +5732,7 @@ describe('ReactDOMFizzServer', () => {
5732 ', or object with a novel `toString` method but found an Array with length 2 instead. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert `children` of <title> tags to a single string value which is why Arrays of length greater than 1 are not supported. When using JSX it can be commong to combine text nodes and value nodes. For example: <title>hello {nameOfUser}</title>. While not immediately apparent, `children` in this case is an Array with length 2. If your `children` prop is using this form try rewriting it using a template string: <title>{`hello ${nameOfUser}`}</title>.',
5733 ]);
5734
5737 - if (gate(flags => flags.enableFloat)) {
5738 - expect(getVisibleChildren(document.head)).toEqual(<title />);
5739 - } else {
5740 - expect(getVisibleChildren(document.head)).toEqual(
5741 - <title>{'hello1<!-- -->hello2'}</title>,
5742 - );
5743 - }
5744 -
5735 + expect(getVisibleChildren(document.head)).toEqual(<title />);
5736 const errors = [];
5737 ReactDOMClient.hydrateRoot(document.head, <App />, {
5738 onRecoverableError(error) {
@@ -5749,24 +5740,9 @@ describe('ReactDOMFizzServer', () => {
5740 },
5741 });
5742 await waitForAll([]);
5752 - if (gate(flags => flags.enableFloat)) {
5753 - expect(errors).toEqual([]);
5754 - // with float, the title doesn't render on the client or on the server
5755 - expect(getVisibleChildren(document.head)).toEqual(<title />);
5756 - } else {
5757 - expect(errors).toEqual(
5758 - [
5759 - gate(flags => flags.enableClientRenderFallbackOnTextMismatch)
5760 - ? 'Text content does not match server-rendered HTML.'
5761 - : null,
5762 - 'Hydration failed because the initial UI does not match what was rendered on the server.',
5763 - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
5764 - ].filter(Boolean),
5765 - );
5766 - expect(getVisibleChildren(document.head)).toEqual(
5767 - <title>{['hello1', 'hello2']}</title>,
5768 - );
5769 - }
5743 + expect(errors).toEqual([]);
5744 + // with float, the title doesn't render on the client or on the server
5745 + expect(getVisibleChildren(document.head)).toEqual(<title />);
5746 });
5747
5748 it('should warn in dev if you pass a React Component as a child to <title>', async () => {
@@ -5784,37 +5760,20 @@ describe('ReactDOMFizzServer', () => {
5760 );
5761 }
5762
5787 - if (gate(flags => flags.enableFloat)) {
5788 - await expect(async () => {
5789 - await act(() => {
5790 - const {pipe} = renderToPipeableStream(<App />);
5791 - pipe(writable);
5792 - });
5793 - }).toErrorDev([
5794 - 'React expects the `children` prop of <title> tags to be a string, number' +
5795 - gate(flags => (flags.enableBigIntSupport ? ', bigint' : '')) +
5796 - ', or object with a novel `toString` method but found an object that appears to be a React element which never implements a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title> tags to a single string value which is why rendering React elements is not supported. If the `children` of <title> is a React Component try moving the <title> tag into that component. If the `children` of <title> is some HTML markup change it to be Text only to be valid HTML.',
5797 - ]);
5798 - } else {
5799 - await expect(async () => {
5800 - await act(() => {
5801 - const {pipe} = renderToPipeableStream(<App />);
5802 - pipe(writable);
5803 - });
5804 - }).toErrorDev([
5805 - 'A title element received a React element for children. In the browser title Elements can only have Text Nodes as children. If the children being rendered output more than a single text node in aggregate the browser will display markup and comments as text in the title and hydration will likely fail and fall back to client rendering',
5806 - ]);
5807 - }
5808 -
5809 - if (gate(flags => flags.enableFloat)) {
5810 - // object titles are toStringed when float is on
5811 - expect(getVisibleChildren(document.head)).toEqual(
5812 - <title>{'[object Object]'}</title>,
5813 - );
5814 - } else {
5815 - expect(getVisibleChildren(document.head)).toEqual(<title>hello</title>);
5816 - }
5817 -
5763 + await expect(async () => {
5764 + await act(() => {
5765 + const {pipe} = renderToPipeableStream(<App />);
5766 + pipe(writable);
5767 + });
5768 + }).toErrorDev([
5769 + 'React expects the `children` prop of <title> tags to be a string, number' +
5770 + gate(flags => (flags.enableBigIntSupport ? ', bigint' : '')) +
5771 + ', or object with a novel `toString` method but found an object that appears to be a React element which never implements a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title> tags to a single string value which is why rendering React elements is not supported. If the `children` of <title> is a React Component try moving the <title> tag into that component. If the `children` of <title> is some HTML markup change it to be Text only to be valid HTML.',
5772 + ]);
5773 + // object titles are toStringed when float is on
5774 + expect(getVisibleChildren(document.head)).toEqual(
5775 + <title>{'[object Object]'}</title>,
5776 + );
5777 const errors = [];
5778 ReactDOMClient.hydrateRoot(document.head, <App />, {
5779 onRecoverableError(error) {
@@ -5823,14 +5782,10 @@ describe('ReactDOMFizzServer', () => {
5782 });
5783 await waitForAll([]);
5784 expect(errors).toEqual([]);
5826 - if (gate(flags => flags.enableFloat)) {
5827 - // object titles are toStringed when float is on
5828 - expect(getVisibleChildren(document.head)).toEqual(
5829 - <title>{'[object Object]'}</title>,
5830 - );
5831 - } else {
5832 - expect(getVisibleChildren(document.head)).toEqual(<title>hello</title>);
5833 - }
5785 + // object titles are toStringed when float is on
5786 + expect(getVisibleChildren(document.head)).toEqual(
5787 + <title>{'[object Object]'}</title>,
5788 + );
5789 });
5790
5791 it('should warn in dev if you pass an object that does not implement toString as a child to <title>', async () => {
@@ -5842,37 +5797,20 @@ describe('ReactDOMFizzServer', () => {
5797 );
5798 }
5799
5845 - if (gate(flags => flags.enableFloat)) {
5846 - await expect(async () => {
5847 - await act(() => {
5848 - const {pipe} = renderToPipeableStream(<App />);
5849 - pipe(writable);
5850 - });
5851 - }).toErrorDev([
5852 - 'React expects the `children` prop of <title> tags to be a string, number' +
5853 - gate(flags => (flags.enableBigIntSupport ? ', bigint' : '')) +
5854 - ', or object with a novel `toString` method but found an object that does not implement a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title> tags to a single string value. Using the default `toString` method available on every object is almost certainly an error. Consider whether the `children` of this <title> is an object in error and change it to a string or number value if so. Otherwise implement a `toString` method that React can use to produce a valid <title>.',
5855 - ]);
5856 - } else {
5857 - await expect(async () => {
5858 - await act(() => {
5859 - const {pipe} = renderToPipeableStream(<App />);
5860 - pipe(writable);
5861 - });
5862 - }).toErrorDev([
5863 - 'A title element received a React element for children. In the browser title Elements can only have Text Nodes as children. If the children being rendered output more than a single text node in aggregate the browser will display markup and comments as text in the title and hydration will likely fail and fall back to client rendering',
5864 - ]);
5865 - }
5866 -
5867 - if (gate(flags => flags.enableFloat)) {
5868 - // object titles are toStringed when float is on
5869 - expect(getVisibleChildren(document.head)).toEqual(
5870 - <title>{'[object Object]'}</title>,
5871 - );
5872 - } else {
5873 - expect(getVisibleChildren(document.head)).toEqual(<title>hello</title>);
5874 - }
5875 -
5800 + await expect(async () => {
5801 + await act(() => {
5802 + const {pipe} = renderToPipeableStream(<App />);
5803 + pipe(writable);
5804 + });
5805 + }).toErrorDev([
5806 + 'React expects the `children` prop of <title> tags to be a string, number' +
5807 + gate(flags => (flags.enableBigIntSupport ? ', bigint' : '')) +
5808 + ', or object with a novel `toString` method but found an object that does not implement a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title> tags to a single string value. Using the default `toString` method available on every object is almost certainly an error. Consider whether the `children` of this <title> is an object in error and change it to a string or number value if so. Otherwise implement a `toString` method that React can use to produce a valid <title>.',
5809 + ]);
5810 + // object titles are toStringed when float is on
5811 + expect(getVisibleChildren(document.head)).toEqual(
5812 + <title>{'[object Object]'}</title>,
5813 + );
5814 const errors = [];
5815 ReactDOMClient.hydrateRoot(document.head, <App />, {
5816 onRecoverableError(error) {
@@ -5881,14 +5819,10 @@ describe('ReactDOMFizzServer', () => {
5819 });
5820 await waitForAll([]);
5821 expect(errors).toEqual([]);
5884 - if (gate(flags => flags.enableFloat)) {
5885 - // object titles are toStringed when float is on
5886 - expect(getVisibleChildren(document.head)).toEqual(
5887 - <title>{'[object Object]'}</title>,
5888 - );
5889 - } else {
5890 - expect(getVisibleChildren(document.head)).toEqual(<title>hello</title>);
5891 - }
5822 + // object titles are toStringed when float is on
5823 + expect(getVisibleChildren(document.head)).toEqual(
5824 + <title>{'[object Object]'}</title>,
5825 + );
5826 });
5827 });
5828
@@ -6436,7 +6370,6 @@ describe('ReactDOMFizzServer', () => {
6370 );
6371 });
6372
6439 - // @gate enableFloat
6373 it('warns if script has complex children', async () => {
6374 function MyScript() {
6375 return 'bar();';
packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
+3 -9
@@ -62,15 +62,9 @@ describe('ReactDOMFizzServerBrowser', () => {
62 </html>,
63 );
64 const result = await readResult(stream);
65 - if (gate(flags => flags.enableFloat)) {
66 - expect(result).toMatchInlineSnapshot(
67 - `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
68 - );
69 - } else {
70 - expect(result).toMatchInlineSnapshot(
71 - `"<!DOCTYPE html><html><body>hello world</body></html>"`,
72 - );
73 - }
65 + expect(result).toMatchInlineSnapshot(
66 + `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
67 + );
68 });
69
70 it('should emit bootstrap script src at the end', async () => {
packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
+4 -10
@@ -73,16 +73,10 @@ describe('ReactDOMFizzServerNode', () => {
73 );
74 pipe(writable);
75 jest.runAllTimers();
76 - if (gate(flags => flags.enableFloat)) {
77 - // with Float, we emit empty heads if they are elided when rendering <html>
78 - expect(output.result).toMatchInlineSnapshot(
79 - `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
80 - );
81 - } else {
82 - expect(output.result).toMatchInlineSnapshot(
83 - `"<!DOCTYPE html><html><body>hello world</body></html>"`,
84 - );
85 - }
76 + // with Float, we emit empty heads if they are elided when rendering <html>
77 + expect(output.result).toMatchInlineSnapshot(
78 + `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
79 + );
80 });
81
82 it('should emit bootstrap script src at the end', () => {
packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js
+3 -9
@@ -126,15 +126,9 @@ describe('ReactDOMFizzStaticBrowser', () => {
126 </html>,
127 );
128 const prelude = await readContent(result.prelude);
129 - if (gate(flags => flags.enableFloat)) {
130 - expect(prelude).toMatchInlineSnapshot(
131 - `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
132 - );
133 - } else {
134 - expect(prelude).toMatchInlineSnapshot(
135 - `"<!DOCTYPE html><html><body>hello world</body></html>"`,
136 - );
137 - }
129 + expect(prelude).toMatchInlineSnapshot(
130 + `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
131 + );
132 });
133
134 // @gate experimental
packages/react-dom/src/__tests__/ReactDOMFizzStaticNode-test.js
+3 -9
@@ -63,15 +63,9 @@ describe('ReactDOMFizzStaticNode', () => {
63 </html>,
64 );
65 const prelude = await readContent(result.prelude);
66 - if (gate(flags => flags.enableFloat)) {
67 - expect(prelude).toMatchInlineSnapshot(
68 - `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
69 - );
70 - } else {
71 - expect(prelude).toMatchInlineSnapshot(
72 - `"<!DOCTYPE html><html><body>hello world</body></html>"`,
73 - );
74 - }
66 + expect(prelude).toMatchInlineSnapshot(
67 + `"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
68 + );
69 });
70
71 // @gate experimental
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+2 -64
@@ -400,7 +400,6 @@ describe('ReactDOMFloat', () => {
400 }
401 }
402
403 - // @gate enableFloat
403 it('can render resources before singletons', async () => {
404 const root = ReactDOMClient.createRoot(document);
405 root.render(
@@ -433,7 +432,6 @@ describe('ReactDOMFloat', () => {
432 );
433 });
434
436 - // @gate enableFloat
435 it('can hydrate non Resources in head when Resources are also inserted there', async () => {
436 await act(() => {
437 const {pipe} = renderToPipeableStream(
@@ -499,7 +497,6 @@ describe('ReactDOMFloat', () => {
497 );
498 });
499
502 - // @gate enableFloat || !__DEV__
500 it('warns if you render resource-like elements above <head> or <body>', async () => {
501 const root = ReactDOMClient.createRoot(document);
502
@@ -631,7 +628,6 @@ describe('ReactDOMFloat', () => {
628 );
629 });
630
634 - // @gate enableFloat
631 it('can acquire a resource after releasing it in the same commit', async () => {
632 const root = ReactDOMClient.createRoot(container);
633 root.render(
@@ -671,7 +667,6 @@ describe('ReactDOMFloat', () => {
667 );
668 });
669
674 - // @gate enableFloat
670 it('emits an implicit <head> element to hold resources when none is rendered but an <html> is rendered', async () => {
671 const chunks = [];
672
@@ -697,7 +692,6 @@ describe('ReactDOMFloat', () => {
692 ]);
693 });
694
700 - // @gate enableFloat
695 it('dedupes if the external runtime is explicitly loaded using preinit', async () => {
696 const unstable_externalRuntimeSrc = 'src-of-external-runtime';
697 function App() {
@@ -731,7 +725,6 @@ describe('ReactDOMFloat', () => {
725 ).toEqual(['<script src="src-of-external-runtime" async=""></script>']);
726 });
727
734 - // @gate enableFloat
728 it('can send style insertion implementation independent of boundary commpletion instruction implementation', async () => {
729 await act(() => {
730 renderToPipeableStream(
@@ -790,7 +783,6 @@ describe('ReactDOMFloat', () => {
783 );
784 });
785
793 - // @gate enableFloat
786 it('can avoid inserting a late stylesheet if it already rendered on the client', async () => {
787 await act(() => {
788 renderToPipeableStream(
@@ -911,7 +903,6 @@ describe('ReactDOMFloat', () => {
903 );
904 });
905
914 - // @gate enableFloat
906 it('can hoist <link rel="stylesheet" .../> and <style /> tags together, respecting order of discovery', async () => {
907 const css = `
908 body {
@@ -1196,7 +1187,6 @@ body {
1187 );
1188 });
1189
1199 - // @gate enableFloat
1190 it('client renders a boundary if a style Resource dependency fails to load', async () => {
1191 function App() {
1192 return (
@@ -1298,7 +1288,6 @@ body {
1288 ]);
1289 });
1290
1301 - // @gate enableFloat
1291 it('treats stylesheet links with a precedence as a resource', async () => {
1292 await act(() => {
1293 const {pipe} = renderToPipeableStream(
@@ -1339,7 +1328,6 @@ body {
1328 );
1329 });
1330
1342 - // @gate enableFloat
1331 it('inserts text separators following text when followed by an element that is converted to a resource and thus removed from the html inline', async () => {
1332 // If you render many of these as siblings the values get emitted as a single text with no separator sometimes
1333 // because the link gets elided as a resource
@@ -1386,7 +1374,6 @@ body {
1374 );
1375 });
1376
1389 - // @gate enableFloat
1377 it('hoists late stylesheets the correct precedence', async () => {
1378 function PresetPrecedence() {
1379 ReactDOM.preinit('preset', {as: 'style', precedence: 'preset'});
@@ -1671,7 +1658,6 @@ body {
1658 );
1659 });
1660
1674 - // @gate enableFloat
1661 it('normalizes stylesheet resource precedence for all boundaries inlined as part of the shell flush', async () => {
1662 await act(() => {
1663 const {pipe} = renderToPipeableStream(
@@ -1755,7 +1741,6 @@ body {
1741 );
1742 });
1743
1758 - // @gate enableFloat
1744 it('stylesheet resources are inserted according to precedence order on the client', async () => {
1745 await act(() => {
1746 const {pipe} = renderToPipeableStream(
@@ -1836,7 +1821,6 @@ body {
1821 );
1822 });
1823
1839 - // @gate enableFloat
1824 it('inserts preloads in render phase eagerly', async () => {
1825 function Throw() {
1826 throw new Error('Uh oh!');
@@ -1878,7 +1862,6 @@ body {
1862 );
1863 });
1864
1881 - // @gate enableFloat
1865 it('will include child boundary stylesheet resources in the boundary reveal instruction', async () => {
1866 await act(() => {
1867 const {pipe} = renderToPipeableStream(
@@ -1997,7 +1980,6 @@ body {
1980 );
1981 });
1982
2000 - // @gate enableFloat
1983 it('will hoist resources of child boundaries emitted as part of a partial boundary to the parent boundary', async () => {
1984 await act(() => {
1985 const {pipe} = renderToPipeableStream(
@@ -2161,7 +2143,6 @@ body {
2143 );
2144 });
2145
2164 - // @gate enableFloat
2146 it('encodes attributes consistently whether resources are flushed in shell or in late boundaries', async () => {
2147 function App() {
2148 return (
@@ -2305,7 +2286,6 @@ body {
2286 );
2287 });
2288
2308 - // @gate enableFloat
2289 it('boundary stylesheet resource dependencies hoist to a parent boundary when flushed inline', async () => {
2290 await act(() => {
2291 const {pipe} = renderToPipeableStream(
@@ -2428,7 +2408,6 @@ body {
2408 );
2409 });
2410
2431 - // @gate enableFloat
2411 it('always enforces crossOrigin "anonymous" for font preloads', async () => {
2412 function App() {
2413 ReactDOM.preload('foo', {as: 'font', type: 'font/woff2'});
@@ -2619,7 +2598,6 @@ body {
2598 ]);
2599 });
2600
2622 - // @gate enableFloat
2601 it('can hydrate resources and components in the head and body even if a browser or 3rd party script injects extra html nodes', async () => {
2602 // This is a stress test case for hydrating a complex combination of hoistable elements, hoistable resources and host components
2603 // in an environment that has been manipulated by 3rd party scripts/extensions to modify the <head> and <body>
@@ -5195,7 +5173,6 @@ body {
5173 });
5174
5175 describe('ReactDOM.preload(href, { as: ... })', () => {
5198 - // @gate enableFloat
5176 it('creates a preload resource when called', async () => {
5177 function App() {
5178 ReactDOM.preload('foo', {as: 'style'});
@@ -5284,7 +5261,6 @@ body {
5261 );
5262 });
5263
5287 - // @gate enableFloat
5264 it('can seed connection props for stylesheet and script resources', async () => {
5265 function App() {
5266 ReactDOM.preload('foo', {
@@ -5326,7 +5302,6 @@ body {
5302 );
5303 });
5304
5329 - // @gate enableFloat
5305 it('warns if you do not pass in a valid href argument or options argument', async () => {
5306 function App() {
5307 ReactDOM.preload();
@@ -5629,7 +5604,6 @@ body {
5604 });
5605
5606 describe('ReactDOM.preinit(href, { as: ... })', () => {
5632 - // @gate enableFloat
5607 it('creates a stylesheet resource when ReactDOM.preinit(..., {as: "style" }) is called', async () => {
5608 function App() {
5609 ReactDOM.preinit('foo', {as: 'style'});
@@ -5713,7 +5687,6 @@ body {
5687 );
5688 });
5689
5716 - // @gate enableFloat
5690 it('creates a stylesheet resource in the ownerDocument when ReactDOM.preinit(..., {as: "style" }) is called outside of render on the client', async () => {
5691 function App() {
5692 React.useEffect(() => {
@@ -5739,7 +5712,6 @@ body {
5712 );
5713 });
5714
5742 - // @gate enableFloat
5715 it('creates a stylesheet resource in the ownerDocument when ReactDOM.preinit(..., {as: "style" }) is called outside of render on the client', async () => {
5716 // This is testing behavior, but it shows that it is not a good idea to preinit inside a shadowRoot. The point is we are asserting a behavior
5717 // you would want to avoid in a real app.
@@ -5783,7 +5755,6 @@ body {
5755 expect(getMeaningfulChildren(shadow)).toEqual(<div>shadow</div>);
5756 });
5757
5786 - // @gate enableFloat
5758 it('creates a script resource when ReactDOM.preinit(..., {as: "script" }) is called', async () => {
5759 function App() {
5760 ReactDOM.preinit('foo', {as: 'script'});
@@ -5861,7 +5832,6 @@ body {
5832 );
5833 });
5834
5864 - // @gate enableFloat
5835 it('creates a script resource when ReactDOM.preinit(..., {as: "script" }) is called outside of render on the client', async () => {
5836 function App() {
5837 React.useEffect(() => {
@@ -5887,7 +5857,6 @@ body {
5857 );
5858 });
5859
5890 - // @gate enableFloat
5860 it('warns if you do not pass in a valid href argument or options argument', async () => {
5861 function App() {
5862 ReactDOM.preinit();
@@ -6296,7 +6265,6 @@ body {
6265 });
6266
6267 describe('Stylesheet Resources', () => {
6299 - // @gate enableFloat
6268 it('treats link rel stylesheet elements as a stylesheet resource when it includes a precedence when server rendering', async () => {
6269 await act(() => {
6270 const {pipe} = renderToPipeableStream(
@@ -6323,7 +6291,6 @@ body {
6291 );
6292 });
6293
6326 - // @gate enableFloat
6294 it('treats link rel stylesheet elements as a stylesheet resource when it includes a precedence when client rendering', async () => {
6295 const root = ReactDOMClient.createRoot(document);
6296 root.render(
@@ -6349,7 +6316,6 @@ body {
6316 );
6317 });
6318
6352 - // @gate enableFloat
6319 it('treats link rel stylesheet elements as a stylesheet resource when it includes a precedence when hydrating', async () => {
6320 await act(() => {
6321 const {pipe} = renderToPipeableStream(
@@ -6387,7 +6353,6 @@ body {
6353 );
6354 });
6355
6390 - // @gate enableFloat
6356 it('hoists stylesheet resources to the correct precedence', async () => {
6357 await act(() => {
6358 const {pipe} = renderToPipeableStream(
@@ -6449,7 +6414,6 @@ body {
6414 );
6415 });
6416
6452 - // @gate enableFloat
6417 it('retains styles even after the last referring Resource unmounts', async () => {
6418 // This test is true until a future update where there is some form of garbage collection.
6419 const root = ReactDOMClient.createRoot(document);
@@ -6482,7 +6446,7 @@ body {
6446 );
6447 });
6448
6485 - // @gate enableFloat && enableClientRenderFallbackOnTextMismatch
6449 + // @gate enableClientRenderFallbackOnTextMismatch
6450 it('retains styles even when a new html, head, and/body mount', async () => {
6451 await act(() => {
6452 const {pipe} = renderToPipeableStream(
@@ -6534,7 +6498,6 @@ body {
6498 );
6499 });
6500
6537 - // @gate enableFloat
6501 it('retains styles in head through head remounts', async () => {
6502 const root = ReactDOMClient.createRoot(document);
6503 root.render(
@@ -6585,7 +6548,6 @@ body {
6548 </html>,
6549 );
6550 });
6588 - // @gate enableFloat
6551 it('can support styles inside portals to a shadowRoot', async () => {
6552 const shadow = document.body.attachShadow({mode: 'open'});
6553 const root = ReactDOMClient.createRoot(container);
@@ -6629,7 +6591,6 @@ body {
6591 <div>shadow</div>,
6592 ]);
6593 });
6632 - // @gate enableFloat
6594 it('can support styles inside portals to an element in shadowRoots', async () => {
6595 const template = document.createElement('template');
6596 template.innerHTML =
@@ -6699,7 +6660,6 @@ body {
6660 ]);
6661 });
6662
6702 - // @gate enableFloat
6663 it('escapes hrefs when selecting matching elements in the document when rendering Resources', async () => {
6664 function App() {
6665 ReactDOM.preload('preload', {as: 'style'});
@@ -6767,7 +6727,6 @@ body {
6727 );
6728 });
6729
6770 - // @gate enableFloat
6730 it('escapes hrefs when selecting matching elements in the document when using preload and preinit', async () => {
6731 await act(() => {
6732 const {pipe} = renderToPipeableStream(
@@ -6830,7 +6789,6 @@ body {
6789 );
6790 });
6791
6833 - // @gate enableFloat
6792 it('does not create stylesheet resources when inside an <svg> context', async () => {
6793 await act(() => {
6794 const {pipe} = renderToPipeableStream(
@@ -6891,7 +6849,6 @@ body {
6849 );
6850 });
6851
6894 - // @gate enableFloat
6852 it('does not create stylesheet resources when inside a <noscript> context', async () => {
6853 await act(() => {
6854 const {pipe} = renderToPipeableStream(
@@ -6933,7 +6890,6 @@ body {
6890 );
6891 });
6892
6936 - // @gate enableFloat
6893 it('warns if you provide a `precedence` prop with other props that invalidate the creation of a stylesheet resource', async () => {
6894 await expect(async () => {
6895 await act(() => {
@@ -7006,7 +6962,6 @@ body {
6962 ]);
6963 });
6964
7009 - // @gate enableFloat
6965 it('will not block displaying a Suspense boundary on a stylesheet with media that does not match', async () => {
6966 await act(() => {
6967 renderToPipeableStream(
@@ -7123,7 +7078,6 @@ body {
7078 });
7079
7080 describe('Style Resource', () => {
7126 - // @gate enableFloat
7081 it('treats <style href="..." precedence="..."> elements as a style resource when server rendering', async () => {
7082 const css = `
7083 body {
@@ -7153,7 +7107,6 @@ body {
7107 );
7108 });
7109
7156 - // @gate enableFloat
7110 it('can insert style resources as part of a boundary reveal', async () => {
7111 const cssRed = `
7112 body {
@@ -7269,7 +7222,6 @@ background-color: green;
7222 );
7223 });
7224
7272 - // @gate enableFloat
7225 it('can emit styles early when a partial boundary flushes', async () => {
7226 const css = 'body { background-color: red; }';
7227 await act(() => {
@@ -7601,7 +7553,6 @@ background-color: green;
7553 });
7554
7555 describe('Script Resources', () => {
7604 - // @gate enableFloat
7556 it('treats async scripts without onLoad or onError as Resources', async () => {
7557 await act(() => {
7558 const {pipe} = renderToPipeableStream(
@@ -7681,7 +7632,6 @@ background-color: green;
7632 );
7633 });
7634
7684 - // @gate enableFloat
7635 it('does not create script resources when inside an <svg> context', async () => {
7636 await act(() => {
7637 const {pipe} = renderToPipeableStream(
@@ -7742,7 +7692,6 @@ background-color: green;
7692 );
7693 });
7694
7745 - // @gate enableFloat
7695 it('does not create script resources when inside a <noscript> context', async () => {
7696 await act(() => {
7697 const {pipe} = renderToPipeableStream(
@@ -7786,7 +7735,6 @@ background-color: green;
7735 });
7736
7737 describe('Hoistables', () => {
7789 - // @gate enableFloat
7738 it('can hoist meta tags on the server and hydrate them on the client', async () => {
7739 await act(() => {
7740 const {pipe} = renderToPipeableStream(
@@ -7840,7 +7788,6 @@ background-color: green;
7788 );
7789 });
7790
7843 - // @gate enableFloat
7791 it('can hoist meta tags on the client', async () => {
7792 const root = ReactDOMClient.createRoot(container);
7793 await act(() => {
@@ -7862,7 +7809,6 @@ background-color: green;
7809 expect(getMeaningfulChildren(document.head)).toEqual(undefined);
7810 });
7811
7865 - // @gate enableFloat
7812 it('can hoist link (non-stylesheet) tags on the server and hydrate them on the client', async () => {
7813 await act(() => {
7814 const {pipe} = renderToPipeableStream(
@@ -7916,7 +7862,6 @@ background-color: green;
7862 );
7863 });
7864
7919 - // @gate enableFloat
7865 it('can hoist link (non-stylesheet) tags on the client', async () => {
7866 const root = ReactDOMClient.createRoot(container);
7867 await act(() => {
@@ -7938,7 +7883,6 @@ background-color: green;
7883 expect(getMeaningfulChildren(document.head)).toEqual(undefined);
7884 });
7885
7941 - // @gate enableFloat
7886 it('can hoist title tags on the server and hydrate them on the client', async () => {
7887 await act(() => {
7888 const {pipe} = renderToPipeableStream(
@@ -7992,7 +7936,6 @@ background-color: green;
7936 );
7937 });
7938
7995 - // @gate enableFloat
7939 it('can hoist title tags on the client', async () => {
7940 const root = ReactDOMClient.createRoot(container);
7941 await act(() => {
@@ -8014,7 +7957,6 @@ background-color: green;
7957 expect(getMeaningfulChildren(document.head)).toEqual(undefined);
7958 });
7959
8017 - // @gate enableFloat
7960 it('prioritizes ordering for certain hoistables over others when rendering on the server', async () => {
7961 await act(() => {
7962 const {pipe} = renderToPipeableStream(
@@ -8054,7 +7996,6 @@ background-color: green;
7996 );
7997 });
7998
8057 - // @gate enableFloat
7999 it('supports rendering hoistables outside of <html> scope', async () => {
8000 await act(() => {
8001 const {pipe} = renderToPipeableStream(
@@ -8232,7 +8173,6 @@ background-color: green;
8173 );
8174 });
8175
8235 - // @gate enableFloat
8176 it('does not hoist inside an <svg> context', async () => {
8177 await act(() => {
8178 const {pipe} = renderToPipeableStream(
@@ -8266,7 +8206,6 @@ background-color: green;
8206 ]);
8207 });
8208
8269 - // @gate enableFloat
8209 it('does not hoist inside noscript context', async () => {
8210 await act(() => {
8211 const {pipe} = renderToPipeableStream(
@@ -8293,7 +8232,7 @@ background-color: green;
8232 ]);
8233 });
8234
8296 - // @gate enableFloat && (enableClientRenderFallbackOnTextMismatch || !__DEV__)
8235 + // @gate enableClientRenderFallbackOnTextMismatch || !__DEV__
8236 it('can render a title before a singleton even if that singleton clears its contents', async () => {
8237 await act(() => {
8238 const {pipe} = renderToPipeableStream(
@@ -8350,7 +8289,6 @@ background-color: green;
8289 );
8290 });
8291
8353 - // @gate enableFloat
8292 it('can update title tags', async () => {
8293 const root = ReactDOMClient.createRoot(container);
8294 await act(() => {
packages/react-dom/src/__tests__/ReactDOMLegacyFloat-test.js
-1
@@ -22,7 +22,6 @@ describe('ReactDOMFloat', () => {
22 });
23
24 // fixes #27177
25 - // @gate enableFloat
25 it('does not hoist above the <html> tag', async () => {
26 const result = ReactDOMFizzServer.renderToString(
27 <html>
packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+5 -30
@@ -222,21 +222,8 @@ describe('ReactDOMRoot', () => {
222 });
223
224 it('warns if creating a root on the document.body', async () => {
225 - if (gate(flags => flags.enableFloat)) {
226 - // we no longer expect an error for this if float is enabled
227 - ReactDOMClient.createRoot(document.body);
228 - } else {
229 - expect(() => {
230 - ReactDOMClient.createRoot(document.body);
231 - }).toErrorDev(
232 - 'createRoot(): Creating roots directly with document.body is ' +
233 - 'discouraged, since its children are often manipulated by third-party ' +
234 - 'scripts and browser extensions. This may lead to subtle ' +
235 - 'reconciliation issues. Try using a container element created ' +
236 - 'for your app.',
237 - {withoutStack: true},
238 - );
239 - }
225 + // we no longer expect an error for this if float is enabled
226 + ReactDOMClient.createRoot(document.body);
227 });
228
229 it('warns if updating a root that has had its contents removed', async () => {
@@ -245,21 +232,9 @@ describe('ReactDOMRoot', () => {
232 await waitForAll([]);
233 container.innerHTML = '';
234
248 - if (gate(flags => flags.enableFloat)) {
249 - // When either of these flags are on this validation is turned off so we
250 - // expect there to be no warnings
251 - root.render(<div>Hi</div>);
252 - } else {
253 - expect(() => {
254 - root.render(<div>Hi</div>);
255 - }).toErrorDev(
256 - 'It looks like the React-rendered content of the ' +
257 - 'root container was removed without using React. This is not ' +
258 - 'supported and will cause errors. Instead, call ' +
259 - "root.unmount() to empty a root's container.",
260 - {withoutStack: true},
261 - );
262 - }
235 + // When either of these flags are on this validation is turned off so we
236 + // expect there to be no warnings
237 + root.render(<div>Hi</div>);
238 });
239
240 it('should render different components in same root', async () => {
packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
-3
@@ -123,7 +123,6 @@ describe('ReactDOM HostSingleton', () => {
123 : children;
124 }
125
126 - // @gate enableFloat
126 it('warns if you render the same singleton twice at the same time', async () => {
127 const root = ReactDOMClient.createRoot(document);
128 root.render(
@@ -208,7 +207,6 @@ describe('ReactDOM HostSingleton', () => {
207 );
208 });
209
211 - // @gate enableFloat
210 it('renders into html, head, and body persistently so the node identities never change and extraneous styles are retained', async () => {
211 // Server render some html that will get replaced with a client render
212 await actIntoEmptyDocument(() => {
@@ -806,7 +804,6 @@ describe('ReactDOM HostSingleton', () => {
804 );
805 });
806
809 - // @gate enableFloat
807 it('clears persistent body when it is the container', async () => {
808 await actIntoEmptyDocument(() => {
809 const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
packages/react-dom/src/__tests__/ReactRenderDocument-test.js
+23 -50
@@ -319,57 +319,30 @@ describe('rendering React components at document', () => {
319 }
320 }
321
322 - if (gate(flags => flags.enableFloat)) {
323 - // with float the title no longer is a hydration mismatch so we get an error on the body mismatch
324 - expect(() => {
325 - ReactDOM.flushSync(() => {
326 - ReactDOMClient.hydrateRoot(
327 - testDocument,
328 - <Component text="Hello world" />,
329 - {
330 - onRecoverableError: error => {
331 - Scheduler.log('Log recoverable error: ' + error.message);
332 - },
333 - },
334 - );
335 - });
336 - }).toErrorDev(
337 - [
338 - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
339 - 'Expected server HTML to contain a matching text node for "Hello world" in <body>',
340 - ],
341 - {withoutStack: 1},
342 - );
343 - assertLog([
344 - 'Log recoverable error: Hydration failed because the initial UI does not match what was rendered on the server.',
345 - 'Log recoverable error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
346 - ]);
347 - } else {
348 - // getTestDocument() has an extra <meta> that we didn't render.
349 - expect(() => {
350 - ReactDOM.flushSync(() => {
351 - ReactDOMClient.hydrateRoot(
352 - testDocument,
353 - <Component text="Hello world" />,
354 - {
355 - onRecoverableError: error => {
356 - Scheduler.log('Log recoverable error: ' + error.message);
357 - },
322 + // with float the title no longer is a hydration mismatch so we get an error on the body mismatch
323 + expect(() => {
324 + ReactDOM.flushSync(() => {
325 + ReactDOMClient.hydrateRoot(
326 + testDocument,
327 + <Component text="Hello world" />,
328 + {
329 + onRecoverableError: error => {
330 + Scheduler.log('Log recoverable error: ' + error.message);
331 },
359 - );
360 - });
361 - }).toErrorDev(
362 - [
363 - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
364 - 'Warning: Text content did not match. Server: "test doc" Client: "Hello World"',
365 - ],
366 - {withoutStack: 1},
367 - );
368 - assertLog([
369 - 'Log recoverable error: Text content does not match server-rendered HTML.',
370 - 'Log recoverable error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
371 - ]);
372 - }
332 + },
333 + );
334 + });
335 + }).toErrorDev(
336 + [
337 + 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
338 + 'Expected server HTML to contain a matching text node for "Hello world" in <body>',
339 + ],
340 + {withoutStack: 1},
341 + );
342 + assertLog([
343 + 'Log recoverable error: Hydration failed because the initial UI does not match what was rendered on the server.',
344 + 'Log recoverable error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.',
345 + ]);
346 expect(testDocument.body.innerHTML).toBe('Hello world');
347 });
348
packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js
-1
@@ -38,7 +38,6 @@ describe('react-dom-server-rendering-stub', () => {
38 expect(ReactDOM.unstable_runWithPriority).toBe(undefined);
39 });
40
41 - // @gate enableFloat
41 it('provides preload, preloadModule, preinit, and preinitModule exports', async () => {
42 function App() {
43 ReactDOM.preload('foo', {as: 'style'});
packages/react-dom/src/test-utils/ReactTestUtils.js
+1 -2
@@ -21,7 +21,6 @@ import {
21 } from 'react-reconciler/src/ReactWorkTags';
22 import {SyntheticEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
23 import {ELEMENT_NODE} from 'react-dom-bindings/src/client/HTMLNodeType';
24 -import {enableFloat} from 'shared/ReactFeatureFlags';
24 import assign from 'shared/assign';
25 import isArray from 'shared/isArray';
26
@@ -63,7 +62,7 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
62 node.tag === HostText ||
63 node.tag === ClassComponent ||
64 node.tag === FunctionComponent ||
66 - (enableFloat ? node.tag === HostHoistable : false) ||
65 + node.tag === HostHoistable ||
66 node.tag === HostSingleton
67 ) {
68 const publicInst = node.stateNode;
packages/react-reconciler/src/ReactFiber.js
+2 -3
@@ -36,7 +36,6 @@ import {
36 allowConcurrentByDefault,
37 enableTransitionTracing,
38 enableDebugTracing,
39 - enableFloat,
39 enableDO_NOT_USE_disableStrictPassiveEffect,
40 enableRenderableContext,
41 } from 'shared/ReactFeatureFlags';
@@ -508,14 +507,14 @@ export function createFiberFromTypeAndProps(
507 }
508 }
509 } else if (typeof type === 'string') {
511 - if (enableFloat && supportsResources && supportsSingletons) {
510 + if (supportsResources && supportsSingletons) {
511 const hostContext = getHostContext();
512 fiberTag = isHostHoistableType(type, pendingProps, hostContext)
513 ? HostHoistable
514 : isHostSingletonType(type)
515 ? HostSingleton
516 : HostComponent;
518 - } else if (enableFloat && supportsResources) {
517 + } else if (supportsResources) {
518 const hostContext = getHostContext();
519 fiberTag = isHostHoistableType(type, pendingProps, hostContext)
520 ? HostHoistable
packages/react-reconciler/src/ReactFiberBeginWork.js
+1 -2
@@ -105,7 +105,6 @@ import {
105 enableTransitionTracing,
106 enableLegacyHidden,
107 enableCPUSuspense,
108 - enableFloat,
108 enableFormActions,
109 enableAsyncActions,
110 enablePostpone,
@@ -4089,7 +4088,7 @@ function beginWork(
4088 case HostRoot:
4089 return updateHostRoot(current, workInProgress, renderLanes);
4090 case HostHoistable:
4092 - if (enableFloat && supportsResources) {
4091 + if (supportsResources) {
4092 return updateHostHoistable(current, workInProgress, renderLanes);
4093 }
4094 // Fall through
packages/react-reconciler/src/ReactFiberCommitWork.js
+8 -11
@@ -52,7 +52,6 @@ import {
52 enableCache,
53 enableTransitionTracing,
54 enableUseEffectEventHook,
55 - enableFloat,
55 enableLegacyHidden,
56 disableStringRefs,
57 } from 'shared/ReactFeatureFlags';
@@ -1105,7 +1104,7 @@ function commitLayoutEffectOnFiber(
1104 break;
1105 }
1106 case HostHoistable: {
1108 - if (enableFloat && supportsResources) {
1107 + if (supportsResources) {
1108 recursivelyTraverseLayoutEffects(
1109 finishedRoot,
1110 finishedWork,
@@ -1509,9 +1508,7 @@ function hideOrUnhideAllChildren(finishedWork: Fiber, isHidden: boolean) {
1508 while (true) {
1509 if (
1510 node.tag === HostComponent ||
1512 - (enableFloat && supportsResources
1513 - ? node.tag === HostHoistable
1514 - : false) ||
1511 + (supportsResources ? node.tag === HostHoistable : false) ||
1512 (supportsSingletons ? node.tag === HostSingleton : false)
1513 ) {
1514 if (hostSubtreeRoot === null) {
@@ -1733,7 +1730,7 @@ function isHostParent(fiber: Fiber): boolean {
1730 return (
1731 fiber.tag === HostComponent ||
1732 fiber.tag === HostRoot ||
1736 - (enableFloat && supportsResources ? fiber.tag === HostHoistable : false) ||
1733 + (supportsResources ? fiber.tag === HostHoistable : false) ||
1734 (supportsSingletons ? fiber.tag === HostSingleton : false) ||
1735 fiber.tag === HostPortal
1736 );
@@ -2011,7 +2008,7 @@ function commitDeletionEffectsOnFiber(
2008 // that don't modify the stack.
2009 switch (deletedFiber.tag) {
2010 case HostHoistable: {
2014 - if (enableFloat && supportsResources) {
2011 + if (supportsResources) {
2012 if (!offscreenSubtreeWasHidden) {
2013 safelyDetachRef(deletedFiber, nearestMountedAncestor);
2014 }
@@ -2604,7 +2601,7 @@ function commitMutationEffectsOnFiber(
2601 return;
2602 }
2603 case HostHoistable: {
2607 - if (enableFloat && supportsResources) {
2604 + if (supportsResources) {
2605 // We cast because we always set the root at the React root and so it cannot be
2606 // null while we are processing mutation effects
2607 const hoistableRoot: HoistableRoot = (currentHoistableRoot: any);
@@ -2800,7 +2797,7 @@ function commitMutationEffectsOnFiber(
2797 return;
2798 }
2799 case HostRoot: {
2803 - if (enableFloat && supportsResources) {
2800 + if (supportsResources) {
2801 prepareToCommitHoistables();
2802
2803 const previousHoistableRoot = currentHoistableRoot;
@@ -2845,7 +2842,7 @@ function commitMutationEffectsOnFiber(
2842 return;
2843 }
2844 case HostPortal: {
2848 - if (enableFloat && supportsResources) {
2845 + if (supportsResources) {
2846 const previousHoistableRoot = currentHoistableRoot;
2847 currentHoistableRoot = getHoistableRoot(
2848 finishedWork.stateNode.containerInfo,
@@ -4114,7 +4111,7 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) {
4111 }
4112 case HostRoot:
4113 case HostPortal: {
4117 - if (enableFloat && supportsResources) {
4114 + if (supportsResources) {
4115 const previousHoistableRoot = currentHoistableRoot;
4116 const container: Container = fiber.stateNode.containerInfo;
4117 currentHoistableRoot = getHoistableRoot(container);
packages/react-reconciler/src/ReactFiberCompleteWork.js
+1 -2
@@ -38,7 +38,6 @@ import {
38 enableProfilerTimer,
39 enableCache,
40 enableTransitionTracing,
41 - enableFloat,
41 enableRenderableContext,
42 passChildrenWhenCloningPersistedNodes,
43 } from 'shared/ReactFeatureFlags';
@@ -1051,7 +1050,7 @@ function completeWork(
1050 return null;
1051 }
1052 case HostHoistable: {
1054 - if (enableFloat && supportsResources) {
1053 + if (supportsResources) {
1054 // The branching here is more complicated than you might expect because
1055 // a HostHoistable sometimes corresponds to a Resource and sometimes
1056 // corresponds to an Instance. It can also switch during an update.
packages/react-reconciler/src/ReactFiberHotReloading.js
+1 -2
@@ -14,7 +14,6 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes';
14 import type {Instance} from './ReactFiberConfig';
15 import type {ReactNodeList} from 'shared/ReactTypes';
16
17 -import {enableFloat} from 'shared/ReactFeatureFlags';
17 import {
18 flushSync,
19 scheduleUpdateOnFiber,
@@ -468,7 +467,7 @@ function findChildHostInstancesForFiberShallowly(
467 while (true) {
468 if (
469 node.tag === HostComponent ||
471 - (enableFloat ? node.tag === HostHoistable : false) ||
470 + node.tag === HostHoistable ||
471 (supportsSingletons ? node.tag === HostSingleton : false)
472 ) {
473 // We got a match.
packages/react-reconciler/src/ReactFiberTreeReflection.js
+2 -3
@@ -25,7 +25,6 @@ import {
25 SuspenseComponent,
26 } from './ReactWorkTags';
27 import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
28 -import {enableFloat} from 'shared/ReactFeatureFlags';
28
29 const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
30
@@ -280,7 +279,7 @@ function findCurrentHostFiberImpl(node: Fiber): Fiber | null {
279 const tag = node.tag;
280 if (
281 tag === HostComponent ||
283 - (enableFloat ? tag === HostHoistable : false) ||
282 + tag === HostHoistable ||
283 tag === HostSingleton ||
284 tag === HostText
285 ) {
@@ -311,7 +310,7 @@ function findCurrentHostFiberWithNoPortalsImpl(node: Fiber): Fiber | null {
310 const tag = node.tag;
311 if (
312 tag === HostComponent ||
314 - (enableFloat ? tag === HostHoistable : false) ||
313 + tag === HostHoistable ||
314 tag === HostSingleton ||
315 tag === HostText
316 ) {
packages/react-server/src/ReactFizzServer.js
+20 -42
@@ -141,7 +141,6 @@ import {
141 enableBigIntSupport,
142 enableScopeAPI,
143 enableSuspenseAvoidThisFallbackFizz,
144 - enableFloat,
144 enableCache,
145 enablePostpone,
146 enableRenderableContext,
@@ -3820,12 +3819,8 @@ function flushSegment(
3819 const id = boundary.rootSegmentID;
3820 writeStartPendingSuspenseBoundary(destination, request.renderState, id);
3821
3823 - // We are going to flush the fallback so we need to hoist the fallback
3824 - // state to the parent boundary
3825 - if (enableFloat) {
3826 - if (hoistableState) {
3827 - hoistHoistables(hoistableState, boundary.fallbackState);
3828 - }
3822 + if (hoistableState) {
3823 + hoistHoistables(hoistableState, boundary.fallbackState);
3824 }
3825 // Flush the fallback.
3826 flushSubtree(request, destination, segment, hoistableState);
@@ -3859,10 +3854,8 @@ function flushSegment(
3854
3855 return writeEndPendingSuspenseBoundary(destination, request.renderState);
3856 } else {
3862 - if (enableFloat) {
3863 - if (hoistableState) {
3864 - hoistHoistables(hoistableState, boundary.contentState);
3865 - }
3857 + if (hoistableState) {
3858 + hoistHoistables(hoistableState, boundary.contentState);
3859 }
3860 // We can inline this boundary's content as a complete boundary.
3861 writeStartCompletedSuspenseBoundary(destination, request.renderState);
@@ -3927,14 +3920,11 @@ function flushCompletedBoundary(
3920 }
3921 completedSegments.length = 0;
3922
3930 - if (enableFloat) {
3931 - writeHoistablesForBoundary(
3932 - destination,
3933 - boundary.contentState,
3934 - request.renderState,
3935 - );
3936 - }
3937 -
3923 + writeHoistablesForBoundary(
3924 + destination,
3925 + boundary.contentState,
3926 + request.renderState,
3927 + );
3928 return writeCompletedBoundaryInstruction(
3929 destination,
3930 request.resumableState,
@@ -3965,15 +3955,11 @@ function flushPartialBoundary(
3955 }
3956 completedSegments.splice(0, i);
3957
3968 - if (enableFloat) {
3969 - return writeHoistablesForBoundary(
3970 - destination,
3971 - boundary.contentState,
3972 - request.renderState,
3973 - );
3974 - } else {
3975 - return true;
3976 - }
3958 + return writeHoistablesForBoundary(
3959 + destination,
3960 + boundary.contentState,
3961 + request.renderState,
3962 + );
3963 }
3964
3965 function flushPartiallyCompletedSegment(
@@ -4035,10 +4021,7 @@ function flushCompletedQueues(
4021 // We postponed the root, so we write nothing.
4022 return;
4023 } else if (request.pendingRootTasks === 0) {
4038 - if (enableFloat) {
4039 - flushPreamble(request, destination, completedRootSegment);
4040 - }
4041 -
4024 + flushPreamble(request, destination, completedRootSegment);
4025 flushSegment(request, destination, completedRootSegment, null);
4026 request.completedRootSegment = null;
4027 writeCompletedRoot(destination, request.renderState);
@@ -4047,10 +4030,7 @@ function flushCompletedQueues(
4030 return;
4031 }
4032 }
4050 - if (enableFloat) {
4051 - writeHoistables(destination, request.resumableState, request.renderState);
4052 - }
4053 -
4033 + writeHoistables(destination, request.resumableState, request.renderState);
4034 // We emit client rendering instructions for already emitted boundaries first.
4035 // This is so that we can signal to the client to start client rendering them as
4036 // soon as possible.
@@ -4126,12 +4106,10 @@ function flushCompletedQueues(
4106 // either they have pending task or they're complete.
4107 ) {
4108 request.flushScheduled = false;
4129 - if (enableFloat) {
4130 - // We write the trailing tags but only if don't have any data to resume.
4131 - // If we need to resume we'll write the postamble in the resume instead.
4132 - if (!enablePostpone || request.trackedPostpones === null) {
4133 - writePostamble(destination, request.resumableState);
4134 - }
4109 + // We write the trailing tags but only if don't have any data to resume.
4110 + // If we need to resume we'll write the postamble in the resume instead.
4111 + if (!enablePostpone || request.trackedPostpones === null) {
4112 + writePostamble(destination, request.resumableState);
4113 }
4114 completeWriting(destination);
4115 flushBuffered(destination);
packages/shared/ReactFeatureFlags.js
-2
@@ -100,8 +100,6 @@ export const enableSuspenseAvoidThisFallbackFizz = false;
100
101 export const enableCPUSuspense = __EXPERIMENTAL__;
102
103 -export const enableFloat = true;
104 -
103 // Enables unstable_useMemoCache hook, intended as a compilation target for
104 // auto-memoization.
105 export const enableUseMemoCacheHook = __EXPERIMENTAL__;
packages/shared/forks/ReactFeatureFlags.native-fb.js
-2
@@ -85,8 +85,6 @@ export const enableNewBooleanProps = true;
85
86 export const enableTransitionTracing = false;
87
88 -export const enableFloat = true;
89 -
88 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
89 export const enableFizzExternalRuntime = true;
90
packages/shared/forks/ReactFeatureFlags.native-oss.js
-2
@@ -76,8 +76,6 @@ export const consoleManagedByDevToolsDuringStrictMode = false;
76
77 export const enableTransitionTracing = false;
78
79 -export const enableFloat = true;
80 -
79 export const useModernStrictMode = false;
80 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
81 export const enableFizzExternalRuntime = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-2
@@ -66,8 +66,6 @@ export const consoleManagedByDevToolsDuringStrictMode = false;
66
67 export const enableTransitionTracing = false;
68
69 -export const enableFloat = true;
70 -
69 export const useModernStrictMode = false;
70 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
71 export const enableFizzExternalRuntime = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-2
@@ -69,8 +69,6 @@ export const consoleManagedByDevToolsDuringStrictMode = false;
69
70 export const enableTransitionTracing = false;
71
72 -export const enableFloat = true;
73 -
72 export const useModernStrictMode = false;
73 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
74 export const enableDeferRootSchedulingToMicrotask = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-2
@@ -69,8 +69,6 @@ export const consoleManagedByDevToolsDuringStrictMode = false;
69
70 export const enableTransitionTracing = false;
71
72 -export const enableFloat = true;
73 -
72 export const useModernStrictMode = false;
73 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
74 export const enableFizzExternalRuntime = false;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -56,7 +56,6 @@ export const enableSuspenseAvoidThisFallbackFizz = false;
56
57 export const enableCustomElementPropertySupport = true;
58 export const enableCPUSuspense = true;
59 -export const enableFloat = true;
59 export const enableUseMemoCacheHook = true;
60 export const enableUseEffectEventHook = true;
61 export const enableFilterEmptyStringAttributesDOM = true;