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;