Clean up enablePostpone Experiment (#35048)
We're not shipping this and it's a lot of code to maintain that is blocking my refactor of Fizz for SuspenseList.
Sebastian Markbåge committed
Nov 5, 2025 at 00:05 UTC
dd048c3b2d8b5760dec718fb0926ca0b68660922
47 files changed
+216
-4115
packages/react-client/src/ReactFlightClient.js
-105
@@ -39,12 +39,9 @@ import type {
39
EncodeFormActionCallback,
40
} from './ReactFlightReplyClient';
41
42
-import type {Postpone} from 'react/src/ReactPostpone';
43
-
42
import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences';
43
44
import {
47
- enablePostpone,
45
enableProfilerTimer,
46
enableComponentPerformanceTrack,
47
enableAsyncDebugInfo,
@@ -89,7 +86,6 @@ import {
86
import {
87
REACT_LAZY_TYPE,
88
REACT_ELEMENT_TYPE,
92
- REACT_POSTPONE_TYPE,
89
ASYNC_ITERATOR,
90
REACT_FRAGMENT_TYPE,
91
} from 'shared/ReactSymbols';
@@ -3460,88 +3456,6 @@ function resolveErrorDev(
3456
return error;
3457
}
3458
3463
-function resolvePostponeProd(
3464
- response: Response,
3465
- id: number,
3466
- streamState: StreamState,
3467
-): void {
3468
- if (__DEV__) {
3469
- // These errors should never make it into a build so we don't need to encode them in codes.json
3470
- // eslint-disable-next-line react-internal/prod-error-codes
3471
- throw new Error(
3472
- 'resolvePostponeProd should never be called in development mode. Use resolvePostponeDev instead. This is a bug in React.',
3473
- );
3474
- }
3475
- const error = new Error(
3476
- 'A Server Component was postponed. The reason is omitted in production' +
3477
- ' builds to avoid leaking sensitive details.',
3478
- );
3479
- const postponeInstance: Postpone = (error: any);
3480
- postponeInstance.$$typeof = REACT_POSTPONE_TYPE;
3481
- postponeInstance.stack = 'Error: ' + error.message;
3482
- const chunks = response._chunks;
3483
- const chunk = chunks.get(id);
3484
- if (!chunk) {
3485
- const newChunk: ErroredChunk<any> = createErrorChunk(
3486
- response,
3487
- postponeInstance,
3488
- );
3489
- chunks.set(id, newChunk);
3490
- } else {
3491
- triggerErrorOnChunk(response, chunk, postponeInstance);
3492
- }
3493
-}
3494
-
3495
-function resolvePostponeDev(
3496
- response: Response,
3497
- id: number,
3498
- reason: string,
3499
- stack: ReactStackTrace,
3500
- env: string,
3501
- streamState: StreamState,
3502
-): void {
3503
- if (!__DEV__) {
3504
- // These errors should never make it into a build so we don't need to encode them in codes.json
3505
- // eslint-disable-next-line react-internal/prod-error-codes
3506
- throw new Error(
3507
- 'resolvePostponeDev should never be called in production mode. Use resolvePostponeProd instead. This is a bug in React.',
3508
- );
3509
- }
3510
- let postponeInstance: Postpone;
3511
- const callStack = buildFakeCallStack(
3512
- response,
3513
- stack,
3514
- env,
3515
- false,
3516
- // $FlowFixMe[incompatible-use]
3517
- Error.bind(null, reason || ''),
3518
- );
3519
- const rootTask = response._debugRootTask;
3520
- if (rootTask != null) {
3521
- postponeInstance = rootTask.run(callStack);
3522
- } else {
3523
- postponeInstance = callStack();
3524
- }
3525
- postponeInstance.$$typeof = REACT_POSTPONE_TYPE;
3526
- const chunks = response._chunks;
3527
- const chunk = chunks.get(id);
3528
- if (!chunk) {
3529
- const newChunk: ErroredChunk<any> = createErrorChunk(
3530
- response,
3531
- postponeInstance,
3532
- );
3533
- if (__DEV__) {
3534
- resolveChunkDebugInfo(response, streamState, newChunk);
3535
- }
3536
- chunks.set(id, newChunk);
3537
- } else {
3538
- if (__DEV__) {
3539
- resolveChunkDebugInfo(response, streamState, chunk);
3540
- }
3541
- triggerErrorOnChunk(response, chunk, postponeInstance);
3542
- }
3543
-}
3544
-
3459
function resolveErrorModel(
3460
response: Response,
3461
id: number,
@@ -4893,25 +4807,6 @@ function processFullStringRow(
4807
return;
4808
}
4809
// Fallthrough
4896
- case 80 /* "P" */: {
4897
- if (enablePostpone) {
4898
- if (__DEV__) {
4899
- const postponeInfo = JSON.parse(row);
4900
- resolvePostponeDev(
4901
- response,
4902
- id,
4903
- postponeInfo.reason,
4904
- postponeInfo.stack,
4905
- postponeInfo.env,
4906
- streamState,
4907
- );
4908
- } else {
4909
- resolvePostponeProd(response, id, streamState);
4910
- }
4911
- return;
4912
- }
4913
- }
4914
- // Fallthrough
4810
default: /* """ "{" "[" "t" "f" "n" "0" - "9" */ {
4811
if (__DEV__ && row === '') {
4812
resolveDebugHalt(response, id);
packages/react-dom/src/__tests__/ReactDOMFizzDeferredValue-test.js
-37
@@ -90,43 +90,6 @@ describe('ReactDOMFizzForm', () => {
90
expect(container.textContent).toEqual('Final');
91
});
92
93
- // @gate enablePostpone
94
- it(
95
- 'if initial value postpones during hydration, it will switch to the ' +
96
- 'final value instead',
97
- async () => {
98
- function Content() {
99
- const isInitial = useDeferredValue(false, true);
100
- if (isInitial) {
101
- React.unstable_postpone();
102
- }
103
- return <Text text="Final" />;
104
- }
105
-
106
- function App() {
107
- return (
108
- <div>
109
- <Suspense fallback={<Text text="Loading..." />}>
110
- <Content />
111
- </Suspense>
112
- </div>
113
- );
114
- }
115
-
116
- const stream = await serverAct(() =>
117
- ReactDOMServer.renderToReadableStream(<App />),
118
- );
119
- await readIntoContainer(stream);
120
- expect(container.textContent).toEqual('Loading...');
121
-
122
- assertLog(['Loading...']);
123
- // After hydration, it's updated to the final value
124
- await act(() => ReactDOMClient.hydrateRoot(container, <App />));
125
- expect(container.textContent).toEqual('Final');
126
- assertLog(['Loading...', 'Final']);
127
- },
128
- );
129
-
93
it(
94
'useDeferredValue during hydration has higher priority than remaining ' +
95
'incremental hydration',
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+59
-1510
@@ -6665,150 +6665,6 @@ describe('ReactDOMFizzServer', () => {
6665
]);
6666
});
6667
6668
- // @gate enablePostpone
6669
- it('client renders postponed boundaries without erroring', async () => {
6670
- function Postponed({isClient}) {
6671
- if (!isClient) {
6672
- React.unstable_postpone('testing postpone');
6673
- }
6674
- return 'client only';
6675
- }
6676
-
6677
- function App({isClient}) {
6678
- return (
6679
- <div>
6680
- <Suspense fallback={'loading...'}>
6681
- <Postponed isClient={isClient} />
6682
- </Suspense>
6683
- </div>
6684
- );
6685
- }
6686
-
6687
- const errors = [];
6688
-
6689
- await act(() => {
6690
- const {pipe} = renderToPipeableStream(<App isClient={false} />, {
6691
- onError(error) {
6692
- errors.push(error.message);
6693
- },
6694
- });
6695
- pipe(writable);
6696
- });
6697
-
6698
- expect(getVisibleChildren(container)).toEqual(<div>loading...</div>);
6699
-
6700
- ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
6701
- onRecoverableError(error) {
6702
- errors.push(error.message);
6703
- },
6704
- });
6705
- await waitForAll([]);
6706
- // Postponing should not be logged as a recoverable error since it's intentional.
6707
- expect(errors).toEqual([]);
6708
- expect(getVisibleChildren(container)).toEqual(<div>client only</div>);
6709
- });
6710
-
6711
- // @gate enablePostpone
6712
- it('errors if trying to postpone outside a Suspense boundary', async () => {
6713
- function Postponed() {
6714
- React.unstable_postpone('testing postpone');
6715
- return 'client only';
6716
- }
6717
-
6718
- function App() {
6719
- return (
6720
- <div>
6721
- <Postponed />
6722
- </div>
6723
- );
6724
- }
6725
-
6726
- const errors = [];
6727
- const fatalErrors = [];
6728
- const postponed = [];
6729
- let written = false;
6730
-
6731
- const testWritable = new Stream.Writable();
6732
- testWritable._write = (chunk, encoding, next) => {
6733
- written = true;
6734
- };
6735
-
6736
- await act(() => {
6737
- const {pipe} = renderToPipeableStream(<App />, {
6738
- onPostpone(reason) {
6739
- postponed.push(reason);
6740
- },
6741
- onError(error) {
6742
- errors.push(error.message);
6743
- },
6744
- onShellError(error) {
6745
- fatalErrors.push(error.message);
6746
- },
6747
- });
6748
- pipe(testWritable);
6749
- });
6750
-
6751
- expect(written).toBe(false);
6752
- // Postponing is not logged as an error but as a postponed reason.
6753
- expect(errors).toEqual([]);
6754
- expect(postponed).toEqual(['testing postpone']);
6755
- // However, it does error the shell.
6756
- expect(fatalErrors).toEqual(['testing postpone']);
6757
- });
6758
-
6759
- // @gate enablePostpone
6760
- it('can postpone in a fallback', async () => {
6761
- function Postponed({isClient}) {
6762
- if (!isClient) {
6763
- React.unstable_postpone('testing postpone');
6764
- }
6765
- return 'loading...';
6766
- }
6767
-
6768
- const lazyText = React.lazy(async () => {
6769
- await 0; // causes the fallback to start work
6770
- return {default: 'Hello'};
6771
- });
6772
-
6773
- function App({isClient}) {
6774
- return (
6775
- <div>
6776
- <Suspense fallback="Outer">
6777
- <Suspense fallback={<Postponed isClient={isClient} />}>
6778
- {lazyText}
6779
- </Suspense>
6780
- </Suspense>
6781
- </div>
6782
- );
6783
- }
6784
-
6785
- const errors = [];
6786
-
6787
- await act(() => {
6788
- const {pipe} = renderToPipeableStream(<App isClient={false} />, {
6789
- onError(error) {
6790
- errors.push(error.message);
6791
- },
6792
- });
6793
- pipe(writable);
6794
- });
6795
-
6796
- // TODO: This should actually be fully resolved because the value could eventually
6797
- // resolve on the server even though the fallback couldn't so we should have been
6798
- // able to render it.
6799
- expect(getVisibleChildren(container)).toEqual(<div>Outer</div>);
6800
-
6801
- ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
6802
- onRecoverableError(error) {
6803
- errors.push(error.message);
6804
- },
6805
- });
6806
- await waitForAll([]);
6807
- // Postponing should not be logged as a recoverable error since it's intentional.
6808
- expect(errors).toEqual([]);
6809
- expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
6810
- });
6811
-
6668
it(
6669
'a transition that flows into a dehydrated boundary should not suspend ' +
6670
'if the boundary is showing a fallback',
@@ -6860,37 +6716,63 @@ describe('ReactDOMFizzServer', () => {
6716
},
6717
);
6718
6863
- // @gate enablePostpone
6864
- it('supports postponing in prerender and resuming later', async () => {
6719
+ // @gate enableHalt
6720
+ it('can resume a prerender that was aborted', async () => {
6721
+ const promise = new Promise(r => {});
6722
+
6723
let prerendering = true;
6866
- function Postpone() {
6724
+
6725
+ function Wait() {
6726
if (prerendering) {
6868
- React.unstable_postpone();
6727
+ return React.use(promise);
6728
+ } else {
6729
+ return 'Hello';
6730
}
6870
- return 'Hello';
6731
}
6732
6733
function App() {
6734
return (
6735
<div>
6736
<Suspense fallback="Loading...">
6877
- <Postpone />
6737
+ <p>
6738
+ <span>
6739
+ <Suspense fallback="Loading again...">
6740
+ <Wait />
6741
+ </Suspense>
6742
+ </span>
6743
+ </p>
6744
+ <p>
6745
+ <span>
6746
+ <Suspense fallback="Loading again too...">
6747
+ <Wait />
6748
+ </Suspense>
6749
+ </span>
6750
+ </p>
6751
</Suspense>
6752
</div>
6753
);
6754
}
6755
6883
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(<App />);
6884
- expect(prerendered.postponed).not.toBe(null);
6756
+ const controller = new AbortController();
6757
+ const signal = controller.signal;
6758
6886
- prerendering = false;
6759
+ const errors = [];
6760
+ function onError(error) {
6761
+ errors.push(error);
6762
+ }
6763
+ let pendingPrerender;
6764
+ await act(() => {
6765
+ pendingPrerender = ReactDOMFizzStatic.prerenderToNodeStream(<App />, {
6766
+ signal,
6767
+ onError,
6768
+ });
6769
+ });
6770
+ controller.abort('boom');
6771
6888
- const resumed = ReactDOMFizzServer.resumeToPipeableStream(
6889
- <App />,
6890
- JSON.parse(JSON.stringify(prerendered.postponed)),
6891
- );
6772
+ const prerendered = await pendingPrerender;
6773
+
6774
+ expect(errors).toEqual(['boom', 'boom']);
6775
6893
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
6776
const preludeWritable = new Stream.PassThrough();
6777
preludeWritable.setEncoding('utf8');
6778
preludeWritable.on('data', chunk => {
@@ -6901,1378 +6783,45 @@ describe('ReactDOMFizzServer', () => {
6783
prerendered.prelude.pipe(preludeWritable);
6784
});
6785
6904
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
6905
-
6906
- await act(() => {
6907
- resumed.pipe(writable);
6908
- });
6909
-
6910
- expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
6911
- });
6912
-
6913
- // @gate enablePostpone
6914
- it('client renders a component if it errors during resuming', async () => {
6915
- let prerendering = true;
6916
- let ssr = true;
6917
- function PostponeAndError() {
6918
- if (prerendering) {
6919
- React.unstable_postpone();
6920
- }
6921
- if (ssr) {
6922
- throw new Error('server error');
6923
- }
6924
- return 'Hello';
6925
- }
6926
-
6927
- function Postpone() {
6928
- if (prerendering) {
6929
- React.unstable_postpone();
6930
- }
6931
- return 'Hello';
6932
- }
6933
-
6934
- const lazyPostponeAndError = React.lazy(async () => {
6935
- return {default: <PostponeAndError />};
6936
- });
6937
-
6938
- function ReplayError() {
6939
- if (prerendering) {
6940
- return <Postpone />;
6941
- }
6942
- if (ssr) {
6943
- throw new Error('replay error');
6944
- }
6945
- return 'Hello';
6946
- }
6947
-
6948
- function App() {
6949
- return (
6950
- <div>
6951
- <Suspense fallback="Loading1">
6952
- <PostponeAndError />
6953
- </Suspense>
6954
- <Suspense fallback="Loading2">
6955
- <Postpone />
6956
- <Suspense fallback="Loading3">{lazyPostponeAndError}</Suspense>
6957
- </Suspense>
6958
- <Suspense fallback="Loading4">
6959
- <ReplayError />
6960
- </Suspense>
6961
- </div>
6962
- );
6963
- }
6964
-
6965
- const prerenderErrors = [];
6966
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
6967
- <App />,
6968
- {
6969
- onError(x) {
6970
- prerenderErrors.push(x.message);
6971
- },
6972
- },
6786
+ expect(getVisibleChildren(container)).toEqual(
6787
+ <div>
6788
+ <p>
6789
+ <span>Loading again...</span>
6790
+ </p>
6791
+ <p>
6792
+ <span>Loading again too...</span>
6793
+ </p>
6794
+ </div>,
6795
);
6974
- expect(prerendered.postponed).not.toBe(null);
6796
6797
prerendering = false;
6798
6978
- const ssrErrors = [];
6979
-
6980
- const resumed = ReactDOMFizzServer.resumeToPipeableStream(
6799
+ errors.length = 0;
6800
+ const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
6801
<App />,
6802
JSON.parse(JSON.stringify(prerendered.postponed)),
6803
{
6984
- onError(x) {
6985
- ssrErrors.push(x.message);
6986
- },
6804
+ onError,
6805
},
6806
);
6807
6990
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
6991
- const preludeWritable = new Stream.PassThrough();
6992
- preludeWritable.setEncoding('utf8');
6993
- preludeWritable.on('data', chunk => {
6994
- writable.write(chunk);
6995
- });
6996
-
6997
- await act(() => {
6998
- prerendered.prelude.pipe(preludeWritable);
6999
- });
7000
-
7001
- expect(getVisibleChildren(container)).toEqual(
7002
- <div>
7003
- {'Loading1'}
7004
- {'Loading2'}
7005
- {'Loading4'}
7006
- </div>,
7007
- );
7008
-
6808
await act(() => {
6809
resumed.pipe(writable);
6810
});
6811
7013
- expect(prerenderErrors).toEqual([]);
7014
-
7015
- expect(ssrErrors).toEqual(['server error', 'server error', 'replay error']);
7016
-
7017
- // Still loading...
7018
- expect(getVisibleChildren(container)).toEqual(
7019
- <div>
7020
- {'Loading1'}
7021
- {'Hello'}
7022
- {'Loading3'}
7023
- {'Loading4'}
7024
- </div>,
7025
- );
7026
-
7027
- const recoverableErrors = [];
7028
-
7029
- ssr = false;
7030
-
7031
- await clientAct(() => {
7032
- ReactDOMClient.hydrateRoot(container, <App />, {
7033
- onRecoverableError(x) {
7034
- recoverableErrors.push(x.message);
7035
- },
7036
- });
7037
- });
7038
-
7039
- expect(recoverableErrors).toEqual(
7040
- __DEV__
7041
- ? [
7042
- 'Switched to client rendering because the server rendering errored:\n\n' +
7043
- 'server error',
7044
- 'Switched to client rendering because the server rendering errored:\n\n' +
7045
- 'replay error',
7046
- 'Switched to client rendering because the server rendering errored:\n\n' +
7047
- 'server error',
7048
- ]
7049
- : [
7050
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7051
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7052
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7053
- ],
7054
- );
6812
+ expect(errors).toEqual([]);
6813
expect(getVisibleChildren(container)).toEqual(
6814
<div>
7057
- {'Hello'}
7058
- {'Hello'}
7059
- {'Hello'}
7060
- {'Hello'}
6815
+ <p>
6816
+ <span>Hello</span>
6817
+ </p>
6818
+ <p>
6819
+ <span>Hello</span>
6820
+ </p>
6821
</div>,
6822
);
6823
});
6824
7065
- // @gate enablePostpone
7066
- it('client renders a component if we abort before resuming', async () => {
7067
- let prerendering = true;
7068
- let ssr = true;
7069
- const promise = new Promise(() => {});
7070
- function PostponeAndSuspend() {
7071
- if (prerendering) {
7072
- React.unstable_postpone();
7073
- }
7074
- if (ssr) {
7075
- React.use(promise);
7076
- }
7077
- return 'Hello';
7078
- }
7079
-
7080
- function Postpone() {
7081
- if (prerendering) {
7082
- React.unstable_postpone();
7083
- }
7084
- return 'Hello';
7085
- }
7086
-
7087
- function DelayedBoundary() {
7088
- if (!prerendering && ssr) {
7089
- // We delay discovery of the boundary so we can abort before finding it.
7090
- React.use(promise);
7091
- }
7092
- return (
7093
- <Suspense fallback="Loading3">
7094
- <Postpone />
7095
- </Suspense>
7096
- );
7097
- }
7098
-
7099
- function App() {
7100
- return (
7101
- <div>
7102
- <Suspense fallback="Loading1">
7103
- <PostponeAndSuspend />
7104
- </Suspense>
7105
- <Suspense fallback="Loading2">
7106
- <Postpone />
7107
- </Suspense>
7108
- <Suspense fallback="Not used">
7109
- <DelayedBoundary />
7110
- </Suspense>
7111
- </div>
7112
- );
7113
- }
7114
-
7115
- const prerenderErrors = [];
7116
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
7117
- <App />,
7118
- {
7119
- onError(x) {
7120
- prerenderErrors.push(x.message);
7121
- },
7122
- },
7123
- );
7124
- expect(prerendered.postponed).not.toBe(null);
7125
-
7126
- prerendering = false;
7127
-
7128
- const ssrErrors = [];
7129
-
7130
- const resumed = ReactDOMFizzServer.resumeToPipeableStream(
7131
- <App />,
7132
- JSON.parse(JSON.stringify(prerendered.postponed)),
7133
- {
7134
- onError(x) {
7135
- ssrErrors.push(x.message);
7136
- },
7137
- },
7138
- );
7139
-
7140
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7141
- const preludeWritable = new Stream.PassThrough();
7142
- preludeWritable.setEncoding('utf8');
7143
- preludeWritable.on('data', chunk => {
7144
- writable.write(chunk);
7145
- });
7146
-
7147
- await act(() => {
7148
- prerendered.prelude.pipe(preludeWritable);
7149
- });
7150
-
7151
- expect(getVisibleChildren(container)).toEqual(
7152
- <div>
7153
- {'Loading1'}
7154
- {'Loading2'}
7155
- {'Loading3'}
7156
- </div>,
7157
- );
7158
-
7159
- await act(() => {
7160
- resumed.pipe(writable);
7161
- });
7162
-
7163
- const recoverableErrors = [];
7164
-
7165
- ssr = false;
7166
-
7167
- await clientAct(() => {
7168
- ReactDOMClient.hydrateRoot(container, <App />, {
7169
- onRecoverableError(x) {
7170
- recoverableErrors.push(x.message);
7171
- },
7172
- });
7173
- });
7174
-
7175
- expect(recoverableErrors).toEqual([]);
7176
- expect(prerenderErrors).toEqual([]);
7177
- expect(ssrErrors).toEqual([]);
7178
-
7179
- // Still loading...
7180
- expect(getVisibleChildren(container)).toEqual(
7181
- <div>
7182
- {'Loading1'}
7183
- {/*
7184
- This used to show "Hello" in this slot because the boundary was able to be flushed
7185
- early but we now prevent flushing while pendingRootTasks is not zero. This is how Edge
7186
- would work anyway because you don't get the stream until the root is unblocked on a resume
7187
- so Node now aligns with edge bevavior
7188
- {'Hello'}
7189
- */}
7190
- {'Loading2'}
7191
- {'Loading3'}
7192
- </div>,
7193
- );
7194
-
7195
- await clientAct(async () => {
7196
- await act(() => {
7197
- resumed.abort(new Error('aborted'));
7198
- });
7199
- });
7200
-
7201
- expect(getVisibleChildren(container)).toEqual(
7202
- <div>
7203
- {'Hello'}
7204
- {'Hello'}
7205
- {'Hello'}
7206
- </div>,
7207
- );
7208
-
7209
- expect(prerenderErrors).toEqual([]);
7210
- expect(ssrErrors).toEqual(['aborted', 'aborted']);
7211
- expect(recoverableErrors).toEqual(
7212
- __DEV__
7213
- ? [
7214
- 'Switched to client rendering because the server rendering aborted due to:\n\n' +
7215
- 'aborted',
7216
- 'Switched to client rendering because the server rendering aborted due to:\n\n' +
7217
- 'aborted',
7218
- ]
7219
- : [
7220
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7221
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7222
- ],
7223
- );
7224
- });
7225
-
7226
- // @gate enablePostpone
7227
- it('client renders remaining boundaries below the error in shell', async () => {
7228
- let prerendering = true;
7229
- let ssr = true;
7230
- function Postpone() {
7231
- if (prerendering) {
7232
- React.unstable_postpone();
7233
- }
7234
- return 'Hello';
7235
- }
7236
-
7237
- function ReplayError({children}) {
7238
- if (!prerendering && ssr) {
7239
- throw new Error('replay error');
7240
- }
7241
- return children;
7242
- }
7243
-
7244
- function App() {
7245
- return (
7246
- <div>
7247
- <div>
7248
- <Suspense fallback="Loading1">
7249
- <Postpone />
7250
- </Suspense>
7251
- <ReplayError>
7252
- <Suspense fallback="Loading2">
7253
- <Postpone />
7254
- </Suspense>
7255
- </ReplayError>
7256
- <Suspense fallback="Loading3">
7257
- <Postpone />
7258
- </Suspense>
7259
- </div>
7260
- <Suspense fallback="Not used">
7261
- <div>
7262
- <Suspense fallback="Loading4">
7263
- <Postpone />
7264
- </Suspense>
7265
- </div>
7266
- </Suspense>
7267
- <Suspense fallback="Loading5">
7268
- <Postpone />
7269
- <ReplayError>
7270
- <Suspense fallback="Loading6">
7271
- <Postpone />
7272
- </Suspense>
7273
- </ReplayError>
7274
- </Suspense>
7275
- </div>
7276
- );
7277
- }
7278
-
7279
- const prerenderErrors = [];
7280
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
7281
- <App />,
7282
- {
7283
- onError(x) {
7284
- prerenderErrors.push(x.message);
7285
- },
7286
- },
7287
- );
7288
- expect(prerendered.postponed).not.toBe(null);
7289
-
7290
- prerendering = false;
7291
-
7292
- const ssrErrors = [];
7293
-
7294
- const resumed = ReactDOMFizzServer.resumeToPipeableStream(
7295
- <App />,
7296
- JSON.parse(JSON.stringify(prerendered.postponed)),
7297
- {
7298
- onError(x) {
7299
- ssrErrors.push(x.message);
7300
- },
7301
- },
7302
- );
7303
-
7304
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7305
- const preludeWritable = new Stream.PassThrough();
7306
- preludeWritable.setEncoding('utf8');
7307
- preludeWritable.on('data', chunk => {
7308
- writable.write(chunk);
7309
- });
7310
-
7311
- await act(() => {
7312
- prerendered.prelude.pipe(preludeWritable);
7313
- });
7314
-
7315
- expect(getVisibleChildren(container)).toEqual(
7316
- <div>
7317
- <div>
7318
- {'Loading1'}
7319
- {'Loading2'}
7320
- {'Loading3'}
7321
- </div>
7322
- <div>{'Loading4'}</div>
7323
- {'Loading5'}
7324
- </div>,
7325
- );
7326
-
7327
- await act(() => {
7328
- resumed.pipe(writable);
7329
- });
7330
-
7331
- expect(getVisibleChildren(container)).toEqual(
7332
- <div>
7333
- <div>
7334
- {'Hello' /* This was matched and completed before the error */}
7335
- {
7336
- 'Loading2' /* This will be client rendered because its parent errored during replay */
7337
- }
7338
- {
7339
- 'Hello' /* This should be renderable since we matched which previous sibling errored */
7340
- }
7341
- </div>
7342
- <div>
7343
- {
7344
- 'Hello' /* This should be able to resume because it's in a different parent. */
7345
- }
7346
- </div>
7347
- {'Hello'}
7348
- {'Loading6' /* The parent could resolve even if the child didn't */}
7349
- </div>,
7350
- );
7351
-
7352
- const recoverableErrors = [];
7353
-
7354
- ssr = false;
7355
-
7356
- await clientAct(() => {
7357
- ReactDOMClient.hydrateRoot(container, <App />, {
7358
- onRecoverableError(x) {
7359
- recoverableErrors.push(x.message);
7360
- },
7361
- });
7362
- });
7363
-
7364
- expect(getVisibleChildren(container)).toEqual(
7365
- <div>
7366
- <div>
7367
- {'Hello'}
7368
- {'Hello'}
7369
- {'Hello'}
7370
- </div>
7371
- <div>{'Hello'}</div>
7372
- {'Hello'}
7373
- {'Hello'}
7374
- </div>,
7375
- );
7376
-
7377
- // We should've logged once for each boundary that this affected.
7378
- expect(prerenderErrors).toEqual([]);
7379
- expect(ssrErrors).toEqual([
7380
- // This error triggered in two replay components.
7381
- 'replay error',
7382
- 'replay error',
7383
- ]);
7384
- expect(recoverableErrors).toEqual(
7385
- // It surfaced in two different suspense boundaries.
7386
- __DEV__
7387
- ? [
7388
- 'Switched to client rendering because the server rendering errored:\n\n' +
7389
- 'replay error',
7390
- 'Switched to client rendering because the server rendering errored:\n\n' +
7391
- 'replay error',
7392
- ]
7393
- : [
7394
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7395
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7396
- ],
7397
- );
7398
- });
7399
-
7400
- // @gate enablePostpone
7401
- it('can client render a boundary after having already postponed', async () => {
7402
- let prerendering = true;
7403
- let ssr = true;
7404
-
7405
- function Postpone() {
7406
- if (prerendering) {
7407
- React.unstable_postpone();
7408
- }
7409
- return 'Hello';
7410
- }
7411
-
7412
- function ServerError() {
7413
- if (ssr) {
7414
- throw new Error('server error');
7415
- }
7416
- return 'World';
7417
- }
7418
-
7419
- function App() {
7420
- return (
7421
- <div>
7422
- <Suspense fallback="Loading1">
7423
- <Postpone />
7424
- <ServerError />
7425
- </Suspense>
7426
- <Suspense fallback="Loading2">
7427
- <Postpone />
7428
- </Suspense>
7429
- </div>
7430
- );
7431
- }
7432
-
7433
- const prerenderErrors = [];
7434
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
7435
- <App />,
7436
- {
7437
- onError(x) {
7438
- prerenderErrors.push(x.message);
7439
- },
7440
- },
7441
- );
7442
- expect(prerendered.postponed).not.toBe(null);
7443
-
7444
- prerendering = false;
7445
-
7446
- const ssrErrors = [];
7447
-
7448
- const resumed = ReactDOMFizzServer.resumeToPipeableStream(
7449
- <App />,
7450
- JSON.parse(JSON.stringify(prerendered.postponed)),
7451
- {
7452
- onError(x) {
7453
- ssrErrors.push(x.message);
7454
- },
7455
- },
7456
- );
7457
-
7458
- const windowErrors = [];
7459
- function globalError(e) {
7460
- windowErrors.push(e.message);
7461
- }
7462
- window.addEventListener('error', globalError);
7463
-
7464
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7465
- const preludeWritable = new Stream.PassThrough();
7466
- preludeWritable.setEncoding('utf8');
7467
- preludeWritable.on('data', chunk => {
7468
- writable.write(chunk);
7469
- });
7470
-
7471
- await act(() => {
7472
- prerendered.prelude.pipe(preludeWritable);
7473
- });
7474
-
7475
- expect(windowErrors).toEqual([]);
7476
-
7477
- expect(getVisibleChildren(container)).toEqual(
7478
- <div>
7479
- {'Loading1'}
7480
- {'Loading2'}
7481
- </div>,
7482
- );
7483
-
7484
- await act(() => {
7485
- resumed.pipe(writable);
7486
- });
7487
-
7488
- expect(prerenderErrors).toEqual(['server error']);
7489
-
7490
- // Since this errored, we shouldn't have to replay it.
7491
- expect(ssrErrors).toEqual([]);
7492
-
7493
- expect(windowErrors).toEqual([]);
7494
-
7495
- // Still loading...
7496
- expect(getVisibleChildren(container)).toEqual(
7497
- <div>
7498
- {'Loading1'}
7499
- {'Hello'}
7500
- </div>,
7501
- );
7502
-
7503
- const recoverableErrors = [];
7504
-
7505
- ssr = false;
7506
-
7507
- await clientAct(() => {
7508
- ReactDOMClient.hydrateRoot(container, <App />, {
7509
- onRecoverableError(x) {
7510
- recoverableErrors.push(x.message);
7511
- },
7512
- });
7513
- });
7514
-
7515
- expect(recoverableErrors).toEqual(
7516
- __DEV__
7517
- ? [
7518
- 'Switched to client rendering because the server rendering errored:\n\n' +
7519
- 'server error',
7520
- ]
7521
- : [
7522
- 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7523
- ],
7524
- );
7525
- expect(getVisibleChildren(container)).toEqual(
7526
- <div>
7527
- {'Hello'}
7528
- {'World'}
7529
- {'Hello'}
7530
- </div>,
7531
- );
7532
-
7533
- expect(windowErrors).toEqual([]);
7534
-
7535
- window.removeEventListener('error', globalError);
7536
- });
7537
-
7538
- // @gate enablePostpone
7539
- it('can postpone in fallback', async () => {
7540
- let prerendering = true;
7541
- function Postpone() {
7542
- if (prerendering) {
7543
- React.unstable_postpone();
7544
- }
7545
- return 'Hello';
7546
- }
7547
-
7548
- let resolve;
7549
- const promise = new Promise(r => (resolve = r));
7550
-
7551
- function PostponeAndDelay() {
7552
- if (prerendering) {
7553
- React.unstable_postpone();
7554
- }
7555
- return React.use(promise);
7556
- }
7557
-
7558
- const Lazy = React.lazy(async () => {
7559
- await 0;
7560
- return {default: Postpone};
7561
- });
7562
-
7563
- function App() {
7564
- return (
7565
- <div>
7566
- <Suspense fallback="Outer">
7567
- <Suspense fallback={<Postpone />}>
7568
- <PostponeAndDelay /> World
7569
- </Suspense>
7570
- <Suspense fallback={<Postpone />}>
7571
- <Lazy />
7572
- </Suspense>
7573
- </Suspense>
7574
- </div>
7575
- );
7576
- }
7577
-
7578
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(<App />);
7579
- expect(prerendered.postponed).not.toBe(null);
7580
-
7581
- prerendering = false;
7582
-
7583
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7584
- const preludeWritable = new Stream.PassThrough();
7585
- preludeWritable.setEncoding('utf8');
7586
- preludeWritable.on('data', chunk => {
7587
- writable.write(chunk);
7588
- });
7589
-
7590
- await act(() => {
7591
- prerendered.prelude.pipe(preludeWritable);
7592
- });
7593
-
7594
- const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
7595
- <App />,
7596
- JSON.parse(JSON.stringify(prerendered.postponed)),
7597
- );
7598
-
7599
- expect(getVisibleChildren(container)).toEqual(<div>Outer</div>);
7600
-
7601
- // Read what we've completed so far
7602
- await act(() => {
7603
- resumed.pipe(writable);
7604
- });
7605
-
7606
- // Should have now resolved the postponed loading state, but not the promise
7607
- expect(getVisibleChildren(container)).toEqual(
7608
- <div>
7609
- {'Hello'}
7610
- {'Hello'}
7611
- </div>,
7612
- );
7613
-
7614
- // Resolve the final promise
7615
- await act(() => {
7616
- resolve('Hi');
7617
- });
7618
-
7619
- expect(getVisibleChildren(container)).toEqual(
7620
- <div>
7621
- {'Hi'}
7622
- {' World'}
7623
- {'Hello'}
7624
- </div>,
7625
- );
7626
- });
7627
-
7628
- // @gate enablePostpone
7629
- it('can discover new suspense boundaries in the resume', async () => {
7630
- let prerendering = true;
7631
- let resolveA;
7632
- const promiseA = new Promise(r => (resolveA = r));
7633
- let resolveB;
7634
- const promiseB = new Promise(r => (resolveB = r));
7635
-
7636
- function WaitA() {
7637
- return React.use(promiseA);
7638
- }
7639
- function WaitB() {
7640
- return React.use(promiseB);
7641
- }
7642
- function Postpone() {
7643
- if (prerendering) {
7644
- React.unstable_postpone();
7645
- }
7646
- return (
7647
- <span>
7648
- <Suspense fallback="Loading again...">
7649
- <WaitA />
7650
- </Suspense>
7651
- <WaitB />
7652
- </span>
7653
- );
7654
- }
7655
-
7656
- function App() {
7657
- return (
7658
- <div>
7659
- <Suspense fallback="Loading...">
7660
- <p>
7661
- <Postpone />
7662
- </p>
7663
- </Suspense>
7664
- </div>
7665
- );
7666
- }
7667
-
7668
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(<App />);
7669
- expect(prerendered.postponed).not.toBe(null);
7670
-
7671
- prerendering = false;
7672
-
7673
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7674
- const preludeWritable = new Stream.PassThrough();
7675
- preludeWritable.setEncoding('utf8');
7676
- preludeWritable.on('data', chunk => {
7677
- writable.write(chunk);
7678
- });
7679
-
7680
- await act(() => {
7681
- prerendered.prelude.pipe(preludeWritable);
7682
- });
7683
-
7684
- const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
7685
- <App />,
7686
- JSON.parse(JSON.stringify(prerendered.postponed)),
7687
- );
7688
-
7689
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
7690
-
7691
- // Read what we've completed so far
7692
- await act(() => {
7693
- resumed.pipe(writable);
7694
- });
7695
-
7696
- // Still blocked
7697
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
7698
-
7699
- // Resolve the first promise, this unblocks the inner boundary
7700
- await act(() => {
7701
- resolveA('Hello');
7702
- });
7703
-
7704
- // Still blocked
7705
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
7706
-
7707
- // Resolve the second promise, this unblocks the outer boundary
7708
- await act(() => {
7709
- resolveB('World');
7710
- });
7711
-
7712
- expect(getVisibleChildren(container)).toEqual(
7713
- <div>
7714
- <p>
7715
- <span>
7716
- {'Hello'}
7717
- {'World'}
7718
- </span>
7719
- </p>
7720
- </div>,
7721
- );
7722
- });
7723
-
7724
- // @gate enablePostpone
7725
- it('does not call onError when you abort with a postpone instance during prerender', async () => {
7726
- const promise = new Promise(r => {});
7727
-
7728
- function Wait() {
7729
- return React.use(promise);
7730
- }
7731
-
7732
- function App() {
7733
- return (
7734
- <div>
7735
- <Suspense fallback="Loading...">
7736
- <p>
7737
- <span>
7738
- <Suspense fallback="Loading again...">
7739
- <Wait />
7740
- </Suspense>
7741
- </span>
7742
- </p>
7743
- <p>
7744
- <span>
7745
- <Suspense fallback="Loading again too...">
7746
- <Wait />
7747
- </Suspense>
7748
- </span>
7749
- </p>
7750
- </Suspense>
7751
- </div>
7752
- );
7753
- }
7754
-
7755
- let postponeInstance;
7756
- try {
7757
- React.unstable_postpone('manufactured');
7758
- } catch (p) {
7759
- postponeInstance = p;
7760
- }
7761
-
7762
- const controller = new AbortController();
7763
- const signal = controller.signal;
7764
-
7765
- const errors = [];
7766
- function onError(error) {
7767
- errors.push(error);
7768
- }
7769
- const postpones = [];
7770
- function onPostpone(reason) {
7771
- postpones.push(reason);
7772
- }
7773
- let pendingPrerender;
7774
- await act(() => {
7775
- pendingPrerender = ReactDOMFizzStatic.prerenderToNodeStream(<App />, {
7776
- signal,
7777
- onError,
7778
- onPostpone,
7779
- });
7780
- });
7781
- controller.abort(postponeInstance);
7782
-
7783
- const prerendered = await pendingPrerender;
7784
-
7785
- expect(errors).toEqual([]);
7786
- expect(postpones).toEqual(['manufactured', 'manufactured']);
7787
-
7788
- await act(() => {
7789
- prerendered.prelude.pipe(writable);
7790
- });
7791
-
7792
- expect(getVisibleChildren(container)).toEqual(
7793
- <div>
7794
- <p>
7795
- <span>Loading again...</span>
7796
- </p>
7797
- <p>
7798
- <span>Loading again too...</span>
7799
- </p>
7800
- </div>,
7801
- );
7802
- });
7803
-
7804
- // @gate enableHalt
7805
- it('can resume a prerender that was aborted', async () => {
7806
- const promise = new Promise(r => {});
7807
-
7808
- let prerendering = true;
7809
-
7810
- function Wait() {
7811
- if (prerendering) {
7812
- return React.use(promise);
7813
- } else {
7814
- return 'Hello';
7815
- }
7816
- }
7817
-
7818
- function App() {
7819
- return (
7820
- <div>
7821
- <Suspense fallback="Loading...">
7822
- <p>
7823
- <span>
7824
- <Suspense fallback="Loading again...">
7825
- <Wait />
7826
- </Suspense>
7827
- </span>
7828
- </p>
7829
- <p>
7830
- <span>
7831
- <Suspense fallback="Loading again too...">
7832
- <Wait />
7833
- </Suspense>
7834
- </span>
7835
- </p>
7836
- </Suspense>
7837
- </div>
7838
- );
7839
- }
7840
-
7841
- const controller = new AbortController();
7842
- const signal = controller.signal;
7843
-
7844
- const errors = [];
7845
- function onError(error) {
7846
- errors.push(error);
7847
- }
7848
- let pendingPrerender;
7849
- await act(() => {
7850
- pendingPrerender = ReactDOMFizzStatic.prerenderToNodeStream(<App />, {
7851
- signal,
7852
- onError,
7853
- });
7854
- });
7855
- controller.abort('boom');
7856
-
7857
- const prerendered = await pendingPrerender;
7858
-
7859
- expect(errors).toEqual(['boom', 'boom']);
7860
-
7861
- const preludeWritable = new Stream.PassThrough();
7862
- preludeWritable.setEncoding('utf8');
7863
- preludeWritable.on('data', chunk => {
7864
- writable.write(chunk);
7865
- });
7866
-
7867
- await act(() => {
7868
- prerendered.prelude.pipe(preludeWritable);
7869
- });
7870
-
7871
- expect(getVisibleChildren(container)).toEqual(
7872
- <div>
7873
- <p>
7874
- <span>Loading again...</span>
7875
- </p>
7876
- <p>
7877
- <span>Loading again too...</span>
7878
- </p>
7879
- </div>,
7880
- );
7881
-
7882
- prerendering = false;
7883
-
7884
- errors.length = 0;
7885
- const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
7886
- <App />,
7887
- JSON.parse(JSON.stringify(prerendered.postponed)),
7888
- {
7889
- onError,
7890
- },
7891
- );
7892
-
7893
- await act(() => {
7894
- resumed.pipe(writable);
7895
- });
7896
-
7897
- expect(errors).toEqual([]);
7898
- expect(getVisibleChildren(container)).toEqual(
7899
- <div>
7900
- <p>
7901
- <span>Hello</span>
7902
- </p>
7903
- <p>
7904
- <span>Hello</span>
7905
- </p>
7906
- </div>,
7907
- );
7908
- });
7909
-
7910
- // @gate enablePostpone
7911
- it('does not call onError when you abort with a postpone instance during resume', async () => {
7912
- let prerendering = true;
7913
- const promise = new Promise(r => {});
7914
-
7915
- function Wait() {
7916
- return React.use(promise);
7917
- }
7918
- function Postpone() {
7919
- if (prerendering) {
7920
- React.unstable_postpone();
7921
- }
7922
- return (
7923
- <span>
7924
- <Suspense fallback="Loading again...">
7925
- <Wait />
7926
- </Suspense>
7927
- </span>
7928
- );
7929
- }
7930
-
7931
- function App() {
7932
- return (
7933
- <div>
7934
- <Suspense fallback="Loading...">
7935
- <p>
7936
- <Postpone />
7937
- </p>
7938
- <p>
7939
- <Postpone />
7940
- </p>
7941
- </Suspense>
7942
- </div>
7943
- );
7944
- }
7945
-
7946
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(<App />);
7947
- expect(prerendered.postponed).not.toBe(null);
7948
-
7949
- prerendering = false;
7950
-
7951
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
7952
- const preludeWritable = new Stream.PassThrough();
7953
- preludeWritable.setEncoding('utf8');
7954
- preludeWritable.on('data', chunk => {
7955
- writable.write(chunk);
7956
- });
7957
-
7958
- await act(() => {
7959
- prerendered.prelude.pipe(preludeWritable);
7960
- });
7961
-
7962
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
7963
-
7964
- let postponeInstance;
7965
- try {
7966
- React.unstable_postpone('manufactured');
7967
- } catch (p) {
7968
- postponeInstance = p;
7969
- }
7970
-
7971
- const errors = [];
7972
- function onError(error) {
7973
- errors.push(error);
7974
- }
7975
- const postpones = [];
7976
- function onPostpone(reason) {
7977
- postpones.push(reason);
7978
- }
7979
-
7980
- prerendering = false;
7981
-
7982
- const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
7983
- <App />,
7984
- JSON.parse(JSON.stringify(prerendered.postponed)),
7985
- {
7986
- onError,
7987
- onPostpone,
7988
- },
7989
- );
7990
-
7991
- await act(() => {
7992
- resumed.pipe(writable);
7993
- });
7994
- await act(() => {
7995
- resumed.abort(postponeInstance);
7996
- });
7997
-
7998
- expect(getVisibleChildren(container)).toEqual(
7999
- <div>
8000
- <p>
8001
- <span>Loading again...</span>
8002
- </p>
8003
- <p>
8004
- <span>Loading again...</span>
8005
- </p>
8006
- </div>,
8007
- );
8008
-
8009
- expect(errors).toEqual([]);
8010
- expect(postpones).toEqual(['manufactured', 'manufactured']);
8011
- });
8012
-
8013
- // @gate enablePostpone
8014
- it('does not call onError when you abort with a postpone instance during a render', async () => {
8015
- const promise = new Promise(r => {});
8016
-
8017
- function Wait() {
8018
- return React.use(promise);
8019
- }
8020
-
8021
- function App() {
8022
- return (
8023
- <div>
8024
- <Suspense fallback="Loading...">
8025
- <p>
8026
- <span>
8027
- <Suspense fallback="Loading again...">
8028
- <Wait />
8029
- </Suspense>
8030
- </span>
8031
- </p>
8032
- <p>
8033
- <span>
8034
- <Suspense fallback="Loading again...">
8035
- <Wait />
8036
- </Suspense>
8037
- </span>
8038
- </p>
8039
- </Suspense>
8040
- </div>
8041
- );
8042
- }
8043
-
8044
- const errors = [];
8045
- function onError(error) {
8046
- errors.push(error);
8047
- }
8048
- const postpones = [];
8049
- function onPostpone(reason) {
8050
- postpones.push(reason);
8051
- }
8052
- const result = await renderToPipeableStream(<App />, {onError, onPostpone});
8053
- await act(() => {
8054
- result.pipe(writable);
8055
- });
8056
-
8057
- expect(getVisibleChildren(container)).toEqual(
8058
- <div>
8059
- <p>
8060
- <span>Loading again...</span>
8061
- </p>
8062
- <p>
8063
- <span>Loading again...</span>
8064
- </p>
8065
- </div>,
8066
- );
8067
-
8068
- let postponeInstance;
8069
- try {
8070
- React.unstable_postpone('manufactured');
8071
- } catch (p) {
8072
- postponeInstance = p;
8073
- }
8074
- await act(() => {
8075
- result.abort(postponeInstance);
8076
- });
8077
-
8078
- expect(getVisibleChildren(container)).toEqual(
8079
- <div>
8080
- <p>
8081
- <span>Loading again...</span>
8082
- </p>
8083
- <p>
8084
- <span>Loading again...</span>
8085
- </p>
8086
- </div>,
8087
- );
8088
-
8089
- expect(errors).toEqual([]);
8090
- expect(postpones).toEqual(['manufactured', 'manufactured']);
8091
- });
8092
-
8093
- // @gate enablePostpone
8094
- it('fatally errors if you abort with a postpone in the shell during resume', async () => {
8095
- let prerendering = true;
8096
- const promise = new Promise(r => {});
8097
-
8098
- function Wait() {
8099
- return React.use(promise);
8100
- }
8101
- function Postpone() {
8102
- if (prerendering) {
8103
- React.unstable_postpone();
8104
- }
8105
- return (
8106
- <span>
8107
- <Suspense fallback="Loading again...">
8108
- <Wait />
8109
- </Suspense>
8110
- </span>
8111
- );
8112
- }
8113
-
8114
- function PostponeInShell() {
8115
- if (prerendering) {
8116
- React.unstable_postpone();
8117
- }
8118
- return <span>in shell</span>;
8119
- }
8120
-
8121
- function App() {
8122
- return (
8123
- <div>
8124
- <PostponeInShell />
8125
- <Suspense fallback="Loading...">
8126
- <p>
8127
- <Postpone />
8128
- </p>
8129
- <p>
8130
- <Postpone />
8131
- </p>
8132
- </Suspense>
8133
- </div>
8134
- );
8135
- }
8136
-
8137
- const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(<App />);
8138
- expect(prerendered.postponed).not.toBe(null);
8139
-
8140
- prerendering = false;
8141
-
8142
- // Create a separate stream so it doesn't close the writable. I.e. simple concat.
8143
- const preludeWritable = new Stream.PassThrough();
8144
- preludeWritable.setEncoding('utf8');
8145
- preludeWritable.on('data', chunk => {
8146
- writable.write(chunk);
8147
- });
8148
-
8149
- await act(() => {
8150
- prerendered.prelude.pipe(preludeWritable);
8151
- });
8152
-
8153
- expect(getVisibleChildren(container)).toEqual(undefined);
8154
-
8155
- let postponeInstance;
8156
- try {
8157
- React.unstable_postpone('manufactured');
8158
- } catch (p) {
8159
- postponeInstance = p;
8160
- }
8161
-
8162
- const errors = [];
8163
- function onError(error) {
8164
- errors.push(error);
8165
- }
8166
- const shellErrors = [];
8167
- function onShellError(error) {
8168
- shellErrors.push(error);
8169
- }
8170
- const postpones = [];
8171
- function onPostpone(reason) {
8172
- postpones.push(reason);
8173
- }
8174
-
8175
- prerendering = false;
8176
-
8177
- const resumed = ReactDOMFizzServer.resumeToPipeableStream(
8178
- <App />,
8179
- JSON.parse(JSON.stringify(prerendered.postponed)),
8180
- {
8181
- onError,
8182
- onShellError,
8183
- onPostpone,
8184
- },
8185
- );
8186
- await act(() => {
8187
- resumed.abort(postponeInstance);
8188
- });
8189
- expect(errors).toEqual([
8190
- new Error(
8191
- 'The render was aborted with postpone when the shell is incomplete. Reason: manufactured',
8192
- ),
8193
- ]);
8194
- expect(shellErrors).toEqual([
8195
- new Error(
8196
- 'The render was aborted with postpone when the shell is incomplete. Reason: manufactured',
8197
- ),
8198
- ]);
8199
- expect(postpones).toEqual([]);
8200
- });
8201
-
8202
- // @gate enablePostpone
8203
- it('fatally errors if you abort with a postpone in the shell during render', async () => {
8204
- const promise = new Promise(r => {});
8205
-
8206
- function Wait() {
8207
- return React.use(promise);
8208
- }
8209
-
8210
- function App() {
8211
- return (
8212
- <div>
8213
- <Suspense fallback="Loading...">
8214
- <p>
8215
- <span>
8216
- <Suspense fallback="Loading again...">
8217
- <Wait />
8218
- </Suspense>
8219
- </span>
8220
- </p>
8221
- <p>
8222
- <span>
8223
- <Suspense fallback="Loading again...">
8224
- <Wait />
8225
- </Suspense>
8226
- </span>
8227
- </p>
8228
- </Suspense>
8229
- </div>
8230
- );
8231
- }
8232
-
8233
- const errors = [];
8234
- function onError(error) {
8235
- errors.push(error);
8236
- }
8237
- const shellErrors = [];
8238
- function onShellError(error) {
8239
- shellErrors.push(error);
8240
- }
8241
- const postpones = [];
8242
- function onPostpone(reason) {
8243
- postpones.push(reason);
8244
- }
8245
- const result = renderToPipeableStream(<App />, {
8246
- onError,
8247
- onShellError,
8248
- onPostpone,
8249
- });
8250
-
8251
- let postponeInstance;
8252
- try {
8253
- React.unstable_postpone('manufactured');
8254
- } catch (p) {
8255
- postponeInstance = p;
8256
- }
8257
- await act(() => {
8258
- result.abort(postponeInstance);
8259
- });
8260
-
8261
- expect(getVisibleChildren(container)).toEqual(undefined);
8262
-
8263
- expect(errors).toEqual([
8264
- new Error(
8265
- 'The render was aborted with postpone when the shell is incomplete. Reason: manufactured',
8266
- ),
8267
- ]);
8268
- expect(shellErrors).toEqual([
8269
- new Error(
8270
- 'The render was aborted with postpone when the shell is incomplete. Reason: manufactured',
8271
- ),
8272
- ]);
8273
- expect(postpones).toEqual([]);
8274
- });
8275
-
6825
it('should NOT warn for using generator functions as components', async () => {
6826
function* Foo() {
6827
yield <h1 key="1">Hello</h1>;
packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
-41
@@ -551,45 +551,4 @@ describe('ReactDOMFizzServerBrowser', () => {
551
`"<link rel="preload" as="script" fetchPriority="low" nonce="R4nd0m" href="init.js"/><link rel="modulepreload" fetchPriority="low" nonce="R4nd0m" href="init.mjs"/><div>hello world</div><script nonce="${nonce}" id="_R_">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
552
);
553
});
554
-
555
- // @gate enablePostpone
556
- it('errors if trying to postpone outside a Suspense boundary', async () => {
557
- function Postponed() {
558
- React.unstable_postpone('testing postpone');
559
- return 'client only';
560
- }
561
-
562
- function App() {
563
- return (
564
- <div>
565
- <Postponed />
566
- </div>
567
- );
568
- }
569
-
570
- const errors = [];
571
- const postponed = [];
572
-
573
- let caughtError = null;
574
- try {
575
- await serverAct(() =>
576
- ReactDOMFizzServer.renderToReadableStream(<App />, {
577
- onError(error) {
578
- errors.push(error.message);
579
- },
580
- onPostpone(reason) {
581
- postponed.push(reason);
582
- },
583
- }),
584
- );
585
- } catch (error) {
586
- caughtError = error;
587
- }
588
-
589
- // Postponing is not logged as an error but as a postponed reason.
590
- expect(errors).toEqual([]);
591
- expect(postponed).toEqual(['testing postpone']);
592
- // However, it does error the shell.
593
- expect(caughtError.message).toEqual('testing postpone');
594
- });
554
});
packages/react-dom/src/__tests__/ReactDOMFizzStatic-test.js
+1
-39
@@ -231,9 +231,7 @@ describe('ReactDOMFizzStatic', () => {
231
const result = await promise;
232
233
expect(result.postponed).toBe(
234
- gate(flags => flags.enableHalt || flags.enablePostpone)
235
- ? null
236
- : undefined,
234
+ gate(flags => flags.enableHalt) ? null : undefined,
235
);
236
237
await act(async () => {
@@ -302,42 +300,6 @@ describe('ReactDOMFizzStatic', () => {
300
expect(getVisibleChildren(container)).toEqual('hello');
301
});
302
305
- // @gate enablePostpone
306
- it('includes stylesheet preloads in onHeaders when postponing in the Shell', async () => {
307
- let headers;
308
- function onHeaders(x) {
309
- headers = x;
310
- }
311
-
312
- function App() {
313
- ReactDOM.preload('image', {as: 'image', fetchPriority: 'high'});
314
- ReactDOM.preinit('style', {as: 'style'});
315
- React.unstable_postpone();
316
- return (
317
- <html>
318
- <body>hello</body>
319
- </html>
320
- );
321
- }
322
-
323
- const result = await ReactDOMFizzStatic.prerenderToNodeStream(<App />, {
324
- onHeaders,
325
- });
326
- expect(headers).toEqual({
327
- Link: `
328
-<image>; rel=preload; as="image"; fetchpriority="high",
329
- <style>; rel=preload; as="style"
330
-`
331
- .replaceAll('\n', '')
332
- .trim(),
333
- });
334
-
335
- await act(async () => {
336
- result.prelude.pipe(writable);
337
- });
338
- expect(getVisibleChildren(container)).toEqual(undefined);
339
- });
340
-
303
it('will prerender Suspense fallbacks before children', async () => {
304
const values = [];
305
function Indirection({children}) {
packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js
+12
-1218
@@ -25,7 +25,6 @@ global.TextDecoder = require('util').TextDecoder;
25
26
let JSDOM;
27
let React;
28
-let ReactDOM;
28
let ReactDOMFizzServer;
29
let ReactDOMFizzStatic;
30
let Suspense;
@@ -46,7 +45,6 @@ describe('ReactDOMFizzStaticBrowser', () => {
45
serverAct = require('internal-test-utils').serverAct;
46
47
React = require('react');
49
- ReactDOM = require('react-dom');
48
ReactDOMFizzServer = require('react-dom/server.browser');
49
ReactDOMFizzStatic = require('react-dom/static.browser');
50
Suspense = React.Suspense;
@@ -71,36 +69,6 @@ describe('ReactDOMFizzStaticBrowser', () => {
69
throw theInfinitePromise;
70
}
71
74
- function concat(streamA, streamB) {
75
- const readerA = streamA.getReader();
76
- const readerB = streamB.getReader();
77
- return new ReadableStream({
78
- start(controller) {
79
- function readA() {
80
- readerA.read().then(({done, value}) => {
81
- if (done) {
82
- readB();
83
- return;
84
- }
85
- controller.enqueue(value);
86
- readA();
87
- });
88
- }
89
- function readB() {
90
- readerB.read().then(({done, value}) => {
91
- if (done) {
92
- controller.close();
93
- return;
94
- }
95
- controller.enqueue(value);
96
- readB();
97
- });
98
- }
99
- readA();
100
- },
101
- });
102
- }
103
-
72
async function readContent(stream) {
73
const reader = stream.getReader();
74
let content = '';
@@ -609,1202 +577,28 @@ describe('ReactDOMFizzStaticBrowser', () => {
577
expect(errors).toEqual(['uh oh', 'uh oh']);
578
});
579
612
- // @gate enablePostpone
613
- it('supports postponing in prerender and resuming later', async () => {
614
- let prerendering = true;
615
- function Postpone() {
616
- if (prerendering) {
617
- React.unstable_postpone();
618
- }
619
- return ['Hello', 'World'];
620
- }
621
-
622
- function App() {
623
- return (
624
- <div>
625
- <Suspense fallback="Loading...">
626
- <Postpone />
627
- </Suspense>
628
- </div>
629
- );
630
- }
631
-
632
- const prerendered = await serverAct(() =>
633
- ReactDOMFizzStatic.prerender(<App />),
634
- );
635
- expect(prerendered.postponed).not.toBe(null);
636
-
637
- prerendering = false;
638
-
639
- const resumed = await serverAct(() =>
640
- ReactDOMFizzServer.resume(
641
- <App />,
642
- JSON.parse(JSON.stringify(prerendered.postponed)),
643
- ),
644
- );
645
-
646
- await readIntoContainer(prerendered.prelude);
647
-
648
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
649
-
650
- await readIntoContainer(resumed);
651
-
652
- expect(getVisibleChildren(container)).toEqual(
653
- <div>{['Hello', 'World']}</div>,
654
- );
655
- });
656
-
657
- // @gate enablePostpone
658
- it('supports postponing in prerender and resuming with a prefix', async () => {
659
- let prerendering = true;
660
- function Postpone() {
661
- if (prerendering) {
662
- React.unstable_postpone();
663
- }
664
- return 'World';
665
- }
666
-
667
- function App() {
668
- return (
669
- <div>
670
- <Suspense fallback="Loading...">
671
- Hello
672
- <Postpone />
673
- </Suspense>
674
- </div>
675
- );
676
- }
677
-
678
- const prerendered = await serverAct(() =>
679
- ReactDOMFizzStatic.prerender(<App />),
680
- );
681
- expect(prerendered.postponed).not.toBe(null);
682
-
683
- prerendering = false;
684
-
685
- const resumed = await serverAct(() =>
686
- ReactDOMFizzServer.resume(
687
- <App />,
688
- JSON.parse(JSON.stringify(prerendered.postponed)),
689
- ),
690
- );
691
-
692
- await readIntoContainer(prerendered.prelude);
693
-
694
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
695
-
696
- await readIntoContainer(resumed);
697
-
698
- expect(getVisibleChildren(container)).toEqual(
699
- <div>{['Hello', 'World']}</div>,
700
- );
701
- });
702
-
703
- // @gate enablePostpone
704
- it('supports postponing in lazy in prerender and resuming later', async () => {
705
- let prerendering = true;
706
- const Hole = React.lazy(async () => {
707
- React.unstable_postpone();
708
- });
709
-
710
- function App() {
711
- return (
712
- <div>
713
- <Suspense fallback="Loading...">
714
- Hi
715
- {prerendering ? Hole : 'Hello'}
716
- </Suspense>
717
- </div>
718
- );
719
- }
720
-
721
- const prerendered = await serverAct(() =>
722
- ReactDOMFizzStatic.prerender(<App />),
723
- );
724
- expect(prerendered.postponed).not.toBe(null);
725
-
726
- prerendering = false;
727
-
728
- const resumed = await serverAct(() =>
729
- ReactDOMFizzServer.resume(
730
- <App />,
731
- JSON.parse(JSON.stringify(prerendered.postponed)),
732
- ),
733
- );
734
-
735
- await readIntoContainer(prerendered.prelude);
736
-
737
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
738
-
739
- await readIntoContainer(resumed);
740
-
741
- expect(getVisibleChildren(container)).toEqual(
742
- <div>
743
- {'Hi'}
744
- {'Hello'}
745
- </div>,
746
- );
747
- });
748
-
749
- // @gate enablePostpone
750
- it('supports postponing in a nested array', async () => {
751
- let prerendering = true;
752
- const Hole = React.lazy(async () => {
753
- React.unstable_postpone();
754
- });
755
- function Postpone() {
756
- if (prerendering) {
757
- React.unstable_postpone();
758
- }
759
- return 'Hello';
760
- }
761
-
762
- function App() {
763
- return (
764
- <div>
765
- <Suspense fallback="Loading...">
766
- Hi
767
- {[<Postpone key="key" />, prerendering ? Hole : 'World']}
768
- </Suspense>
769
- </div>
770
- );
771
- }
772
-
773
- const prerendered = await serverAct(() =>
774
- ReactDOMFizzStatic.prerender(<App />),
775
- );
776
- expect(prerendered.postponed).not.toBe(null);
777
-
778
- prerendering = false;
779
-
780
- const resumed = await serverAct(() =>
781
- ReactDOMFizzServer.resume(
782
- <App />,
783
- JSON.parse(JSON.stringify(prerendered.postponed)),
784
- ),
785
- );
786
-
787
- await readIntoContainer(prerendered.prelude);
788
-
789
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
790
-
791
- await readIntoContainer(resumed);
792
-
793
- expect(getVisibleChildren(container)).toEqual(
794
- <div>{['Hi', 'Hello', 'World']}</div>,
795
- );
796
- });
797
-
798
- // @gate enablePostpone
799
- it('supports postponing in lazy as a direct child', async () => {
800
- let prerendering = true;
801
- const Hole = React.lazy(async () => {
802
- React.unstable_postpone();
803
- });
804
- function Postpone() {
805
- return prerendering ? Hole : 'Hello';
806
- }
807
-
808
- function App() {
809
- return (
810
- <div>
811
- <Suspense fallback="Loading...">
812
- <Postpone key="key" />
813
- </Suspense>
814
- </div>
815
- );
816
- }
817
-
818
- const prerendered = await serverAct(() =>
819
- ReactDOMFizzStatic.prerender(<App />),
820
- );
821
- expect(prerendered.postponed).not.toBe(null);
822
-
823
- prerendering = false;
824
-
825
- const resumed = await serverAct(() =>
826
- ReactDOMFizzServer.resume(
827
- <App />,
828
- JSON.parse(JSON.stringify(prerendered.postponed)),
829
- ),
830
- );
831
-
832
- await readIntoContainer(prerendered.prelude);
833
-
834
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
835
-
836
- await readIntoContainer(resumed);
837
-
838
- expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
839
- });
840
-
841
- // @gate enablePostpone
842
- it('only emits end tags once when resuming', async () => {
843
- let prerendering = true;
844
- function Postpone() {
845
- if (prerendering) {
846
- React.unstable_postpone();
847
- }
848
- return 'Hello';
849
- }
850
-
851
- function App() {
852
- return (
853
- <html>
854
- <body>
855
- <Suspense fallback="Loading...">
856
- <Postpone />
857
- </Suspense>
858
- </body>
859
- </html>
860
- );
861
- }
862
-
863
- const prerendered = await serverAct(() =>
864
- ReactDOMFizzStatic.prerender(<App />),
865
- );
866
- expect(prerendered.postponed).not.toBe(null);
867
-
868
- prerendering = false;
869
-
870
- const content = await serverAct(() =>
871
- ReactDOMFizzServer.resume(
872
- <App />,
873
- JSON.parse(JSON.stringify(prerendered.postponed)),
874
- ),
875
- );
876
-
877
- const html = await readContent(concat(prerendered.prelude, content));
878
- const htmlEndTags = /<\/html\s*>/gi;
879
- const bodyEndTags = /<\/body\s*>/gi;
880
- expect(Array.from(html.matchAll(htmlEndTags)).length).toBe(1);
881
- expect(Array.from(html.matchAll(bodyEndTags)).length).toBe(1);
882
- });
883
-
884
- // @gate enablePostpone
885
- it('can prerender various hoistables and deduped resources', async () => {
886
- let prerendering = true;
887
- function Postpone() {
888
- if (prerendering) {
889
- React.unstable_postpone();
890
- }
891
- return (
892
- <>
893
- <link rel="stylesheet" href="my-style2" precedence="low" />
894
- <link rel="stylesheet" href="my-style1" precedence="high" />
895
- <style precedence="high" href="my-style3">
896
- style
897
- </style>
898
- <img src="my-img" />
899
- </>
900
- );
580
+ it('logs an error if onHeaders throws but continues the prerender', async () => {
581
+ const errors = [];
582
+ function onError(error) {
583
+ errors.push(error.message);
584
}
585
903
- function App() {
904
- ReactDOM.preconnect('example.com');
905
- ReactDOM.preload('my-font', {as: 'font', type: 'font/woff2'});
906
- ReactDOM.preload('my-style0', {as: 'style'});
907
- // This should transfer the props in to the style that loads later.
908
- ReactDOM.preload('my-style2', {
909
- as: 'style',
910
- crossOrigin: 'use-credentials',
911
- });
912
- return (
913
- <div>
914
- <Suspense fallback="Loading...">
915
- <link rel="stylesheet" href="my-style1" precedence="high" />
916
- <img src="my-img" />
917
- <Postpone />
918
- </Suspense>
919
- <title>Hello World</title>
920
- </div>
921
- );
586
+ function onHeaders(x) {
587
+ throw new Error('bad onHeaders');
588
}
589
924
- let calledInit = false;
925
- jest.mock(
926
- 'init.js',
927
- () => {
928
- calledInit = true;
929
- },
930
- {virtual: true},
931
- );
932
-
590
const prerendered = await serverAct(() =>
934
- ReactDOMFizzStatic.prerender(<App />, {
935
- bootstrapScripts: ['init.js'],
591
+ ReactDOMFizzStatic.prerender(<div>hello</div>, {
592
+ onHeaders,
593
+ onError,
594
}),
595
);
938
- expect(prerendered.postponed).not.toBe(null);
939
-
940
- await readIntoContainer(prerendered.prelude);
941
-
942
- expect(getVisibleChildren(container)).toEqual([
943
- <link href="example.com" rel="preconnect" />,
944
- <link
945
- as="font"
946
- crossorigin=""
947
- href="my-font"
948
- rel="preload"
949
- type="font/woff2"
950
- />,
951
- <link as="image" href="my-img" rel="preload" />,
952
- <link data-precedence="high" href="my-style1" rel="stylesheet" />,
953
- <link as="script" fetchpriority="low" href="init.js" rel="preload" />,
954
- <link as="style" href="my-style0" rel="preload" />,
955
- <link
956
- as="style"
957
- crossorigin="use-credentials"
958
- href="my-style2"
959
- rel="preload"
960
- />,
961
- <title>Hello World</title>,
962
- <div>Loading...</div>,
963
- ]);
964
-
965
- prerendering = false;
966
- const content = await serverAct(() =>
967
- ReactDOMFizzServer.resume(
968
- <App />,
969
- JSON.parse(JSON.stringify(prerendered.postponed)),
970
- ),
971
- );
972
-
973
- await readIntoContainer(content);
974
-
975
- expect(calledInit).toBe(true);
976
-
977
- // Dispatch load event to injected stylesheet
978
- const link = document.querySelector(
979
- 'link[rel="stylesheet"][href="my-style2"]',
980
- );
981
- const event = document.createEvent('Events');
982
- event.initEvent('load', true, true);
983
- link.dispatchEvent(event);
984
-
985
- // Wait for the instruction microtasks to flush.
986
- await 0;
987
- await 0;
988
- jest.runAllTimers();
989
-
990
- expect(getVisibleChildren(container)).toEqual([
991
- <link href="example.com" rel="preconnect" />,
992
- <link
993
- as="font"
994
- crossorigin=""
995
- href="my-font"
996
- rel="preload"
997
- type="font/woff2"
998
- />,
999
- <link as="image" href="my-img" rel="preload" />,
1000
- <link data-precedence="high" href="my-style1" rel="stylesheet" />,
1001
- <style data-href="my-style3" data-precedence="high">
1002
- style
1003
- </style>,
1004
- <link
1005
- crossorigin="use-credentials"
1006
- data-precedence="low"
1007
- href="my-style2"
1008
- rel="stylesheet"
1009
- />,
1010
- <link as="script" fetchpriority="low" href="init.js" rel="preload" />,
1011
- <link as="style" href="my-style0" rel="preload" />,
1012
- <link
1013
- as="style"
1014
- crossorigin="use-credentials"
1015
- href="my-style2"
1016
- rel="preload"
1017
- />,
1018
- <title>Hello World</title>,
1019
- <div>
1020
- <img src="my-img" />
1021
- <img src="my-img" />
1022
- </div>,
1023
- ]);
1024
- });
1025
-
1026
- // @gate enablePostpone
1027
- it('can postpone a boundary after it has already been added', async () => {
1028
- let prerendering = true;
1029
- function Postpone() {
1030
- if (prerendering) {
1031
- React.unstable_postpone();
1032
- }
1033
- return 'Hello';
1034
- }
1035
-
1036
- function App() {
1037
- return (
1038
- <div>
1039
- <Suspense fallback="Loading...">
1040
- <Suspense fallback="Loading...">
1041
- <Postpone />
1042
- </Suspense>
1043
- <Postpone />
1044
- <Postpone />
1045
- </Suspense>
1046
- </div>
1047
- );
1048
- }
1049
-
1050
- const prerendered = await serverAct(() =>
1051
- ReactDOMFizzStatic.prerender(<App />),
1052
- );
1053
- expect(prerendered.postponed).not.toBe(null);
1054
-
1055
- prerendering = false;
1056
-
1057
- const resumed = await serverAct(() =>
1058
- ReactDOMFizzServer.resume(
1059
- <App />,
1060
- JSON.parse(JSON.stringify(prerendered.postponed)),
1061
- ),
596
+ expect(prerendered.postponed).toBe(
597
+ gate(flags => flags.enableHalt) ? null : undefined,
598
);
599
+ expect(errors).toEqual(['bad onHeaders']);
600
601
await readIntoContainer(prerendered.prelude);
1065
-
1066
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
1067
-
1068
- await readIntoContainer(resumed);
1069
-
1070
- expect(getVisibleChildren(container)).toEqual(
1071
- <div>{['Hello', 'Hello', 'Hello']}</div>,
1072
- );
1073
- });
1074
-
1075
- // @gate enablePostpone
1076
- it('can postpone in fallback', async () => {
1077
- let prerendering = true;
1078
- function Postpone() {
1079
- if (prerendering) {
1080
- React.unstable_postpone();
1081
- }
1082
- return 'Hello';
1083
- }
1084
-
1085
- const Lazy = React.lazy(async () => {
1086
- await 0;
1087
- return {default: Postpone};
1088
- });
1089
-
1090
- function App() {
1091
- return (
1092
- <div>
1093
- <Suspense fallback="Outer">
1094
- <Suspense fallback={<Postpone />}>
1095
- <Postpone /> World
1096
- </Suspense>
1097
- <Suspense fallback={<Postpone />}>
1098
- <Lazy />
1099
- </Suspense>
1100
- </Suspense>
1101
- </div>
1102
- );
1103
- }
1104
-
1105
- const prerendered = await serverAct(() =>
1106
- ReactDOMFizzStatic.prerender(<App />),
1107
- );
1108
- expect(prerendered.postponed).not.toBe(null);
1109
-
1110
- prerendering = false;
1111
-
1112
- const resumed = await serverAct(() =>
1113
- ReactDOMFizzServer.resume(
1114
- <App />,
1115
- JSON.parse(JSON.stringify(prerendered.postponed)),
1116
- ),
1117
- );
1118
-
1119
- await readIntoContainer(prerendered.prelude);
1120
-
1121
- expect(getVisibleChildren(container)).toEqual(<div>Outer</div>);
1122
-
1123
- await readIntoContainer(resumed);
1124
-
1125
- expect(getVisibleChildren(container)).toEqual(
1126
- <div>
1127
- {'Hello'}
1128
- {' World'}
1129
- {'Hello'}
1130
- </div>,
1131
- );
1132
- });
1133
-
1134
- // @gate enablePostpone
1135
- it('can postpone in fallback without postponing the tree', async () => {
1136
- function Postpone() {
1137
- React.unstable_postpone();
1138
- }
1139
-
1140
- const lazyText = React.lazy(async () => {
1141
- await 0; // causes the fallback to start work
1142
- return {default: 'Hello'};
1143
- });
1144
-
1145
- function App() {
1146
- return (
1147
- <div>
1148
- <Suspense fallback="Outer">
1149
- <Suspense fallback={<Postpone />}>{lazyText}</Suspense>
1150
- </Suspense>
1151
- </div>
1152
- );
1153
- }
1154
-
1155
- const prerendered = await serverAct(() =>
1156
- ReactDOMFizzStatic.prerender(<App />),
1157
- );
1158
- // TODO: This should actually be null because we should've been able to fully
1159
- // resolve the render on the server eventually, even though the fallback postponed.
1160
- // So we should not need to resume.
1161
- expect(prerendered.postponed).not.toBe(null);
1162
-
1163
- await readIntoContainer(prerendered.prelude);
1164
-
1165
- expect(getVisibleChildren(container)).toEqual(<div>Outer</div>);
1166
-
1167
- const resumed = await serverAct(() =>
1168
- ReactDOMFizzServer.resume(
1169
- <App />,
1170
- JSON.parse(JSON.stringify(prerendered.postponed)),
1171
- ),
1172
- );
1173
-
1174
- await readIntoContainer(resumed);
1175
-
1176
- expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
1177
- });
1178
-
1179
- // @gate enablePostpone
1180
- it('errors if the replay does not line up', async () => {
1181
- let prerendering = true;
1182
- function Postpone() {
1183
- if (prerendering) {
1184
- React.unstable_postpone();
1185
- }
1186
- return 'Hello';
1187
- }
1188
-
1189
- function Wrapper({children}) {
1190
- return children;
1191
- }
1192
-
1193
- const lazySpan = React.lazy(async () => {
1194
- await 0;
1195
- return {default: <span />};
1196
- });
1197
-
1198
- function App() {
1199
- const children = (
1200
- <Suspense fallback="Loading...">
1201
- <Postpone />
1202
- </Suspense>
1203
- );
1204
- return (
1205
- <>
1206
- <div>{prerendering ? <Wrapper>{children}</Wrapper> : children}</div>
1207
- <div>
1208
- {prerendering ? (
1209
- <Suspense fallback="Loading...">
1210
- <div>
1211
- <Postpone />
1212
- </div>
1213
- </Suspense>
1214
- ) : (
1215
- lazySpan
1216
- )}
1217
- </div>
1218
- </>
1219
- );
1220
- }
1221
-
1222
- const prerendered = await serverAct(() =>
1223
- ReactDOMFizzStatic.prerender(<App />),
1224
- );
1225
- expect(prerendered.postponed).not.toBe(null);
1226
-
1227
- await readIntoContainer(prerendered.prelude);
1228
-
1229
- expect(getVisibleChildren(container)).toEqual([
1230
- <div>Loading...</div>,
1231
- <div>Loading...</div>,
1232
- ]);
1233
-
1234
- prerendering = false;
1235
-
1236
- const errors = [];
1237
- const resumed = await serverAct(() =>
1238
- ReactDOMFizzServer.resume(
1239
- <App />,
1240
- JSON.parse(JSON.stringify(prerendered.postponed)),
1241
- {
1242
- onError(x) {
1243
- errors.push(x.message);
1244
- },
1245
- },
1246
- ),
1247
- );
1248
-
1249
- expect(errors).toEqual([
1250
- 'Expected the resume to render <Wrapper> in this slot but instead it rendered <Suspense>. ' +
1251
- "The tree doesn't match so React will fallback to client rendering.",
1252
- 'Expected the resume to render <Suspense> in this slot but instead it rendered <span>. ' +
1253
- "The tree doesn't match so React will fallback to client rendering.",
1254
- ]);
1255
-
1256
- // TODO: Test the component stack but we don't expose it to the server yet.
1257
-
1258
- await readIntoContainer(resumed);
1259
-
1260
- // Client rendered
1261
- expect(getVisibleChildren(container)).toEqual([
1262
- <div>Loading...</div>,
1263
- <div>Loading...</div>,
1264
- ]);
1265
- });
1266
-
1267
- // @gate enablePostpone
1268
- it('can abort the resume', async () => {
1269
- let prerendering = true;
1270
- const infinitePromise = new Promise(() => {});
1271
- function Postpone() {
1272
- if (prerendering) {
1273
- React.unstable_postpone();
1274
- }
1275
- return 'Hello';
1276
- }
1277
-
1278
- function App() {
1279
- if (!prerendering) {
1280
- React.use(infinitePromise);
1281
- }
1282
- return (
1283
- <div>
1284
- <Suspense fallback="Loading...">
1285
- <Postpone />
1286
- </Suspense>
1287
- </div>
1288
- );
1289
- }
1290
-
1291
- const prerendered = await serverAct(() =>
1292
- ReactDOMFizzStatic.prerender(<App />),
1293
- );
1294
- expect(prerendered.postponed).not.toBe(null);
1295
-
1296
- await readIntoContainer(prerendered.prelude);
1297
-
1298
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
1299
-
1300
- prerendering = false;
1301
-
1302
- const controller = new AbortController();
1303
-
1304
- const errors = [];
1305
-
1306
- const resumedPromise = serverAct(() =>
1307
- ReactDOMFizzServer.resume(
1308
- <App />,
1309
- JSON.parse(JSON.stringify(prerendered.postponed)),
1310
- {
1311
- signal: controller.signal,
1312
- onError(x) {
1313
- errors.push(x);
1314
- },
1315
- },
1316
- ),
1317
- );
1318
-
1319
- controller.abort('abort');
1320
-
1321
- const resumed = await resumedPromise;
1322
- await resumed.allReady;
1323
-
1324
- expect(errors).toEqual(['abort']);
1325
-
1326
- await readIntoContainer(resumed);
1327
-
1328
- // Client rendered
1329
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
1330
- });
1331
-
1332
- // @gate enablePostpone
1333
- it('can suspend in a replayed component several layers deep', async () => {
1334
- let prerendering = true;
1335
- function Postpone() {
1336
- if (prerendering) {
1337
- React.unstable_postpone();
1338
- }
1339
- return 'Hello';
1340
- }
1341
-
1342
- let resolve;
1343
- const promise = new Promise(r => (resolve = r));
1344
- function Delay({children}) {
1345
- if (!prerendering) {
1346
- React.use(promise);
1347
- }
1348
- return children;
1349
- }
1350
-
1351
- // This wrapper will cause us to do one destructive render past this.
1352
- function Outer({children}) {
1353
- return children;
1354
- }
1355
-
1356
- function App() {
1357
- return (
1358
- <div>
1359
- <Outer>
1360
- <Delay>
1361
- <Suspense fallback="Loading...">
1362
- <Postpone />
1363
- </Suspense>
1364
- </Delay>
1365
- </Outer>
1366
- </div>
1367
- );
1368
- }
1369
-
1370
- const prerendered = await serverAct(() =>
1371
- ReactDOMFizzStatic.prerender(<App />),
1372
- );
1373
- expect(prerendered.postponed).not.toBe(null);
1374
-
1375
- await readIntoContainer(prerendered.prelude);
1376
-
1377
- prerendering = false;
1378
-
1379
- const resumedPromise = serverAct(() =>
1380
- ReactDOMFizzServer.resume(
1381
- <App />,
1382
- JSON.parse(JSON.stringify(prerendered.postponed)),
1383
- ),
1384
- );
1385
-
1386
- await jest.runAllTimers();
1387
-
1388
- expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
1389
-
1390
- await resolve();
1391
-
1392
- await readIntoContainer(await resumedPromise);
1393
-
1394
- expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
1395
- });
1396
-
1397
- // @gate enablePostpone
1398
- it('emits an empty prelude and resumes at the root if we postpone in the shell', async () => {
1399
- let prerendering = true;
1400
- function Postpone() {
1401
- if (prerendering) {
1402
- React.unstable_postpone();
1403
- }
1404
- return 'Hello';
1405
- }
1406
-
1407
- function App() {
1408
- return (
1409
- <html lang="en">
1410
- <body>
1411
- <link rel="stylesheet" href="my-style" precedence="high" />
1412
- <Postpone />
1413
- </body>
1414
- </html>
1415
- );
1416
- }
1417
-
1418
- const prerendered = await serverAct(() =>
1419
- ReactDOMFizzStatic.prerender(<App />),
1420
- );
1421
- expect(prerendered.postponed).not.toBe(null);
1422
-
1423
- prerendering = false;
1424
-
1425
- expect(await readContent(prerendered.prelude)).toBe('');
1426
-
1427
- const content = await serverAct(() =>
1428
- ReactDOMFizzServer.resume(
1429
- <App />,
1430
- JSON.parse(JSON.stringify(prerendered.postponed)),
1431
- ),
1432
- );
1433
-
1434
- expect(await readContent(content)).toBe(
1435
- '<!DOCTYPE html><html lang="en"><head>' +
1436
- '<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
1437
- (gate(flags => flags.enableFizzBlockingRender)
1438
- ? '<link rel="expect" href="#_R_" blocking="render"/>'
1439
- : '') +
1440
- '</head>' +
1441
- '<body>Hello' +
1442
- (gate(flags => flags.enableFizzBlockingRender)
1443
- ? '<template id="_R_"></template>'
1444
- : '') +
1445
- '</body></html>',
1446
- );
1447
- });
1448
-
1449
- // @gate enablePostpone
1450
- it('emits an empty prelude if we have not rendered html or head tags yet', async () => {
1451
- let prerendering = true;
1452
- function Postpone() {
1453
- if (prerendering) {
1454
- React.unstable_postpone();
1455
- }
1456
- return (
1457
- <html lang="en">
1458
- <body>Hello</body>
1459
- </html>
1460
- );
1461
- }
1462
-
1463
- function App() {
1464
- return (
1465
- <>
1466
- <link rel="stylesheet" href="my-style" precedence="high" />
1467
- <Postpone />
1468
- </>
1469
- );
1470
- }
1471
-
1472
- const prerendered = await serverAct(() =>
1473
- ReactDOMFizzStatic.prerender(<App />),
1474
- );
1475
- expect(prerendered.postponed).not.toBe(null);
1476
-
1477
- prerendering = false;
1478
-
1479
- expect(await readContent(prerendered.prelude)).toBe('');
1480
-
1481
- const content = await serverAct(() =>
1482
- ReactDOMFizzServer.resume(
1483
- <App />,
1484
- JSON.parse(JSON.stringify(prerendered.postponed)),
1485
- ),
1486
- );
1487
-
1488
- expect(await readContent(content)).toBe(
1489
- '<!DOCTYPE html><html lang="en"><head>' +
1490
- '<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
1491
- '<link rel="expect" href="#_R_" blocking="render"/></head>' +
1492
- '<body>Hello<template id="_R_"></template></body></html>',
1493
- );
1494
- });
1495
-
1496
- // @gate enablePostpone
1497
- it('emits an empty prelude if a postpone in a promise in the shell', async () => {
1498
- let prerendering = true;
1499
- function Postpone() {
1500
- if (prerendering) {
1501
- React.unstable_postpone();
1502
- }
1503
- return 'Hello';
1504
- }
1505
-
1506
- const Lazy = React.lazy(async () => {
1507
- await 0;
1508
- return {default: Postpone};
1509
- });
1510
-
1511
- function App() {
1512
- return (
1513
- <html>
1514
- <link rel="stylesheet" href="my-style" precedence="high" />
1515
- <body>
1516
- <div>
1517
- <Lazy />
1518
- </div>
1519
- </body>
1520
- </html>
1521
- );
1522
- }
1523
-
1524
- const prerendered = await serverAct(() =>
1525
- ReactDOMFizzStatic.prerender(<App />),
1526
- );
1527
- expect(prerendered.postponed).not.toBe(null);
1528
-
1529
- prerendering = false;
1530
-
1531
- expect(await readContent(prerendered.prelude)).toBe('');
1532
-
1533
- const content = await serverAct(() =>
1534
- ReactDOMFizzServer.resume(
1535
- <App />,
1536
- JSON.parse(JSON.stringify(prerendered.postponed)),
1537
- ),
1538
- );
1539
-
1540
- expect(await readContent(content)).toBe(
1541
- '<!DOCTYPE html><html><head>' +
1542
- '<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
1543
- '<link rel="expect" href="#_R_" blocking="render"/></head>' +
1544
- '<body><div>Hello</div><template id="_R_"></template></body></html>',
1545
- );
1546
- });
1547
-
1548
- // @gate enablePostpone
1549
- it('does not emit preloads during resume for Resources preloaded through onHeaders', async () => {
1550
- let prerendering = true;
1551
-
1552
- let hasLoaded = false;
1553
- let resolve;
1554
- const promise = new Promise(r => (resolve = r));
1555
- function WaitIfResuming({children}) {
1556
- if (!prerendering && !hasLoaded) {
1557
- throw promise;
1558
- }
1559
- return children;
1560
- }
1561
-
1562
- function Postpone() {
1563
- if (prerendering) {
1564
- React.unstable_postpone();
1565
- }
1566
- return null;
1567
- }
1568
-
1569
- let headers;
1570
- function onHeaders(x) {
1571
- headers = x;
1572
- }
1573
-
1574
- function App() {
1575
- ReactDOM.preload('image', {as: 'image', fetchPriority: 'high'});
1576
- return (
1577
- <html>
1578
- <body>
1579
- hello
1580
- <Suspense fallback={null}>
1581
- <WaitIfResuming>
1582
- world
1583
- <link rel="stylesheet" href="style" precedence="default" />
1584
- </WaitIfResuming>
1585
- </Suspense>
1586
- <Postpone />
1587
- </body>
1588
- </html>
1589
- );
1590
- }
1591
-
1592
- const prerendered = await serverAct(() =>
1593
- ReactDOMFizzStatic.prerender(<App />, {
1594
- onHeaders,
1595
- }),
1596
- );
1597
- expect(prerendered.postponed).not.toBe(null);
1598
-
1599
- prerendering = false;
1600
-
1601
- expect(await readContent(prerendered.prelude)).toBe('');
1602
- expect(headers).toEqual(
1603
- new Headers({
1604
- Link: `
1605
-<image>; rel=preload; as="image"; fetchpriority="high",
1606
- <style>; rel=preload; as="style"
1607
-`
1608
- .replaceAll('\n', '')
1609
- .trim(),
1610
- }),
1611
- );
1612
-
1613
- const content = await serverAct(() =>
1614
- ReactDOMFizzServer.resume(
1615
- <App />,
1616
- JSON.parse(JSON.stringify(prerendered.postponed)),
1617
- ),
1618
- );
1619
-
1620
- const decoder = new TextDecoder();
1621
- const reader = content.getReader();
1622
- let {value, done} = await reader.read();
1623
- let result = decoder.decode(value, {stream: true});
1624
-
1625
- expect(result).toBe(
1626
- '<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
1627
- '<body>hello<!--$?--><template id="B:0"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>',
1628
- );
1629
-
1630
- await 1;
1631
- hasLoaded = true;
1632
- await serverAct(resolve);
1633
-
1634
- while (true) {
1635
- ({value, done} = await reader.read());
1636
- if (done) {
1637
- result += decoder.decode(value);
1638
- break;
1639
- }
1640
- result += decoder.decode(value, {stream: true});
1641
- }
1642
-
1643
- // We are mostly just trying to assert that no preload for our stylesheet was emitted
1644
- // prior to sending the segment the stylesheet was for. This test is asserting this
1645
- // because the boundary complete instruction is sent when we are writing the
1646
- const instructionIndex = result.indexOf('$RX');
1647
- expect(instructionIndex > -1).toBe(true);
1648
- const slice = result.slice(0, instructionIndex + '$RX'.length);
1649
-
1650
- expect(slice).toBe(
1651
- '<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
1652
- '<body>hello<!--$?--><template id="B:0"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
1653
- '<div hidden id="S:0">world<!-- --></div><script>$RX',
1654
- );
1655
- });
1656
-
1657
- it('logs an error if onHeaders throws but continues the prerender', async () => {
1658
- const errors = [];
1659
- function onError(error) {
1660
- errors.push(error.message);
1661
- }
1662
-
1663
- function onHeaders(x) {
1664
- throw new Error('bad onHeaders');
1665
- }
1666
-
1667
- const prerendered = await serverAct(() =>
1668
- ReactDOMFizzStatic.prerender(<div>hello</div>, {
1669
- onHeaders,
1670
- onError,
1671
- }),
1672
- );
1673
- expect(prerendered.postponed).toBe(
1674
- gate(flags => flags.enableHalt || flags.enablePostpone)
1675
- ? null
1676
- : undefined,
1677
- );
1678
- expect(errors).toEqual(['bad onHeaders']);
1679
-
1680
- await readIntoContainer(prerendered.prelude);
1681
- expect(getVisibleChildren(container)).toEqual(<div>hello</div>);
1682
- });
1683
-
1684
- // @gate enablePostpone
1685
- it('does not bootstrap again in a resume if it bootstraps', async () => {
1686
- let prerendering = true;
1687
-
1688
- function Postpone() {
1689
- if (prerendering) {
1690
- React.unstable_postpone();
1691
- }
1692
- return null;
1693
- }
1694
-
1695
- function App() {
1696
- return (
1697
- <html>
1698
- <body>
1699
- <Suspense fallback="loading...">
1700
- <Postpone />
1701
- hello
1702
- </Suspense>
1703
- </body>
1704
- </html>
1705
- );
1706
- }
1707
-
1708
- let inits = 0;
1709
- jest.mock(
1710
- 'init.js',
1711
- () => {
1712
- inits++;
1713
- },
1714
- {virtual: true},
1715
- );
1716
-
1717
- const prerendered = await serverAct(() =>
1718
- ReactDOMFizzStatic.prerender(<App />, {
1719
- bootstrapScripts: ['init.js'],
1720
- }),
1721
- );
1722
-
1723
- const postponedSerializedState = JSON.stringify(prerendered.postponed);
1724
-
1725
- expect(prerendered.postponed).not.toBe(null);
1726
-
1727
- await readIntoContainer(prerendered.prelude);
1728
-
1729
- expect(getVisibleChildren(container)).toEqual([
1730
- <link rel="preload" href="init.js" fetchpriority="low" as="script" />,
1731
- 'loading...',
1732
- ]);
1733
-
1734
- expect(inits).toBe(1);
1735
-
1736
- jest.resetModules();
1737
- jest.mock(
1738
- 'init.js',
1739
- () => {
1740
- inits++;
1741
- },
1742
- {virtual: true},
1743
- );
1744
-
1745
- prerendering = false;
1746
-
1747
- const content = await serverAct(() =>
1748
- ReactDOMFizzServer.resume(<App />, JSON.parse(postponedSerializedState)),
1749
- );
1750
-
1751
- await readIntoContainer(content);
1752
-
1753
- expect(inits).toBe(1);
1754
-
1755
- expect(getVisibleChildren(container)).toEqual([
1756
- <link rel="preload" href="init.js" fetchpriority="low" as="script" />,
1757
- 'hello',
1758
- ]);
1759
- });
1760
-
1761
- // @gate enablePostpone
1762
- it('can render a deep list of single components where one postpones', async () => {
1763
- let isPrerendering = true;
1764
- function Outer({children}) {
1765
- return children;
1766
- }
1767
-
1768
- function Middle({children}) {
1769
- return children;
1770
- }
1771
-
1772
- function Inner() {
1773
- if (isPrerendering) {
1774
- React.unstable_postpone();
1775
- }
1776
- return 'hello';
1777
- }
1778
-
1779
- function App() {
1780
- return (
1781
- <div>
1782
- <Suspense fallback="loading...">
1783
- <Outer>
1784
- <Middle>
1785
- <Inner />
1786
- </Middle>
1787
- </Outer>
1788
- </Suspense>
1789
- </div>
1790
- );
1791
- }
1792
-
1793
- const prerendered = await serverAct(() =>
1794
- ReactDOMFizzStatic.prerender(<App />),
1795
- );
1796
- const postponedState = JSON.stringify(prerendered.postponed);
1797
-
1798
- await readIntoContainer(prerendered.prelude);
1799
- expect(getVisibleChildren(container)).toEqual(<div>loading...</div>);
1800
-
1801
- isPrerendering = false;
1802
-
1803
- const dynamic = await serverAct(() =>
1804
- ReactDOMFizzServer.resume(<App />, JSON.parse(postponedState)),
1805
- );
1806
-
1807
- await readIntoContainer(dynamic);
602
expect(getVisibleChildren(container)).toEqual(<div>hello</div>);
603
});
604
packages/react-dom/src/__tests__/ReactDOMFizzStaticFloat-test.js
deleted
-283
@@ -1,283 +0,0 @@
1
-/**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @emails react-core
8
- */
9
-
10
-'use strict';
11
-
12
-import {patchMessageChannel} from '../../../../scripts/jest/patchMessageChannel';
13
-
14
-import {
15
- getVisibleChildren,
16
- insertNodesAndExecuteScripts,
17
-} from '../test-utils/FizzTestUtils';
18
-
19
-// Polyfills for test environment
20
-global.ReadableStream =
21
- require('web-streams-polyfill/ponyfill/es6').ReadableStream;
22
-global.TextEncoder = require('util').TextEncoder;
23
-
24
-let React;
25
-let ReactDOM;
26
-let ReactDOMFizzServer;
27
-let ReactDOMFizzStatic;
28
-let Suspense;
29
-let container;
30
-let act;
31
-let serverAct;
32
-
33
-describe('ReactDOMFizzStaticFloat', () => {
34
- beforeEach(() => {
35
- jest.resetModules();
36
- patchMessageChannel();
37
- act = require('internal-test-utils').act;
38
- serverAct = require('internal-test-utils').serverAct;
39
-
40
- React = require('react');
41
- ReactDOM = require('react-dom');
42
- ReactDOMFizzServer = require('react-dom/server.browser');
43
- ReactDOMFizzStatic = require('react-dom/static.browser');
44
- Suspense = React.Suspense;
45
- container = document.createElement('div');
46
- document.body.appendChild(container);
47
- });
48
-
49
- afterEach(() => {
50
- document.body.removeChild(container);
51
- });
52
-
53
- async function readIntoContainer(stream) {
54
- const reader = stream.getReader();
55
- let result = '';
56
- while (true) {
57
- const {done, value} = await reader.read();
58
- if (done) {
59
- break;
60
- }
61
- result += Buffer.from(value).toString('utf8');
62
- }
63
- const temp = document.createElement('div');
64
- temp.innerHTML = result;
65
- await insertNodesAndExecuteScripts(temp, container, null);
66
- }
67
-
68
- // @gate enablePostpone
69
- it('should transfer connection credentials across prerender and resume for stylesheets, scripts, and moduleScripts', async () => {
70
- let prerendering = true;
71
- function Postpone() {
72
- if (prerendering) {
73
- React.unstable_postpone();
74
- }
75
- return (
76
- <>
77
- <link rel="stylesheet" href="style creds" precedence="default" />
78
- <script async={true} src="script creds" data-meaningful="" />
79
- <script
80
- type="module"
81
- async={true}
82
- src="module creds"
83
- data-meaningful=""
84
- />
85
- <link rel="stylesheet" href="style anon" precedence="default" />
86
- <script async={true} src="script anon" data-meaningful="" />
87
- <script
88
- type="module"
89
- async={true}
90
- src="module default"
91
- data-meaningful=""
92
- />
93
- </>
94
- );
95
- }
96
-
97
- function App() {
98
- ReactDOM.preload('style creds', {
99
- as: 'style',
100
- crossOrigin: 'use-credentials',
101
- });
102
- ReactDOM.preload('script creds', {
103
- as: 'script',
104
- crossOrigin: 'use-credentials',
105
- integrity: 'script-hash',
106
- });
107
- ReactDOM.preloadModule('module creds', {
108
- crossOrigin: 'use-credentials',
109
- integrity: 'module-hash',
110
- });
111
- ReactDOM.preload('style anon', {
112
- as: 'style',
113
- crossOrigin: 'anonymous',
114
- });
115
- ReactDOM.preload('script anon', {
116
- as: 'script',
117
- crossOrigin: 'foobar',
118
- });
119
- ReactDOM.preloadModule('module default', {
120
- integrity: 'module-hash',
121
- });
122
- return (
123
- <div>
124
- <Suspense fallback="Loading...">
125
- <Postpone />
126
- </Suspense>
127
- </div>
128
- );
129
- }
130
-
131
- jest.mock('script creds', () => {}, {
132
- virtual: true,
133
- });
134
- jest.mock('module creds', () => {}, {
135
- virtual: true,
136
- });
137
- jest.mock('script anon', () => {}, {
138
- virtual: true,
139
- });
140
- jest.mock('module default', () => {}, {
141
- virtual: true,
142
- });
143
-
144
- const prerendered = await serverAct(() =>
145
- ReactDOMFizzStatic.prerender(<App />),
146
- );
147
- expect(prerendered.postponed).not.toBe(null);
148
-
149
- await readIntoContainer(prerendered.prelude);
150
-
151
- expect(getVisibleChildren(container)).toEqual([
152
- <link
153
- rel="preload"
154
- as="style"
155
- href="style creds"
156
- crossorigin="use-credentials"
157
- />,
158
- <link
159
- rel="preload"
160
- as="script"
161
- href="script creds"
162
- crossorigin="use-credentials"
163
- integrity="script-hash"
164
- />,
165
- <link
166
- rel="modulepreload"
167
- href="module creds"
168
- crossorigin="use-credentials"
169
- integrity="module-hash"
170
- />,
171
- <link rel="preload" as="style" href="style anon" crossorigin="" />,
172
- <link rel="preload" as="script" href="script anon" crossorigin="" />,
173
- <link
174
- rel="modulepreload"
175
- href="module default"
176
- integrity="module-hash"
177
- />,
178
- <div>Loading...</div>,
179
- ]);
180
-
181
- prerendering = false;
182
- const content = await serverAct(() =>
183
- ReactDOMFizzServer.resume(
184
- <App />,
185
- JSON.parse(JSON.stringify(prerendered.postponed)),
186
- ),
187
- );
188
-
189
- await readIntoContainer(content);
190
-
191
- await act(() => {
192
- // Dispatch load event to injected stylesheet
193
- const linkCreds = document.querySelector(
194
- 'link[rel="stylesheet"][href="style creds"]',
195
- );
196
- const linkAnon = document.querySelector(
197
- 'link[rel="stylesheet"][href="style anon"]',
198
- );
199
- const event = document.createEvent('Events');
200
- event.initEvent('load', true, true);
201
- linkCreds.dispatchEvent(event);
202
- linkAnon.dispatchEvent(event);
203
- });
204
-
205
- expect(getVisibleChildren(document)).toEqual(
206
- <html>
207
- <head>
208
- <link
209
- rel="stylesheet"
210
- data-precedence="default"
211
- href="style creds"
212
- crossorigin="use-credentials"
213
- />
214
- <link
215
- rel="stylesheet"
216
- data-precedence="default"
217
- href="style anon"
218
- crossorigin=""
219
- />
220
- </head>
221
- <body>
222
- <div>
223
- <link
224
- rel="preload"
225
- as="style"
226
- href="style creds"
227
- crossorigin="use-credentials"
228
- />
229
- <link
230
- rel="preload"
231
- as="script"
232
- href="script creds"
233
- crossorigin="use-credentials"
234
- integrity="script-hash"
235
- />
236
- <link
237
- rel="modulepreload"
238
- href="module creds"
239
- crossorigin="use-credentials"
240
- integrity="module-hash"
241
- />
242
- <link rel="preload" as="style" href="style anon" crossorigin="" />
243
- <link rel="preload" as="script" href="script anon" crossorigin="" />
244
- <link
245
- rel="modulepreload"
246
- href="module default"
247
- integrity="module-hash"
248
- />
249
- <div />
250
- <script
251
- async=""
252
- src="script creds"
253
- crossorigin="use-credentials"
254
- integrity="script-hash"
255
- data-meaningful=""
256
- />
257
- <script
258
- type="module"
259
- async=""
260
- src="module creds"
261
- crossorigin="use-credentials"
262
- integrity="module-hash"
263
- data-meaningful=""
264
- />
265
- <script
266
- async=""
267
- src="script anon"
268
- crossorigin=""
269
- data-meaningful=""
270
- />
271
- <script
272
- type="module"
273
- async=""
274
- src="module default"
275
- integrity="module-hash"
276
- data-meaningful=""
277
- />
278
- </div>
279
- </body>
280
- </html>,
281
- );
282
- });
283
-});
packages/react-dom/src/__tests__/ReactDOMFizzStaticNode-test.js
+14
-14
@@ -56,7 +56,7 @@ describe('ReactDOMFizzStaticNode', () => {
56
}
57
}
58
59
- // @gate enableHalt || enablePostpone
59
+ // @gate enableHalt
60
it('should call prerenderToNodeStream', async () => {
61
const result = await ReactDOMFizzStatic.prerenderToNodeStream(
62
<div>hello world</div>,
@@ -65,14 +65,14 @@ describe('ReactDOMFizzStaticNode', () => {
65
expect(prelude).toMatchInlineSnapshot(`"<div>hello world</div>"`);
66
});
67
68
- // @gate enableHalt || enablePostpone
68
+ // @gate enableHalt
69
it('should suppport web streams', async () => {
70
const result = await ReactDOMFizzStatic.prerender(<div>hello world</div>);
71
const prelude = await readContentWeb(result.prelude);
72
expect(prelude).toMatchInlineSnapshot(`"<div>hello world</div>"`);
73
});
74
75
- // @gate enableHalt || enablePostpone
75
+ // @gate enableHalt
76
it('should emit DOCTYPE at the root of the document', async () => {
77
const result = await ReactDOMFizzStatic.prerenderToNodeStream(
78
<html>
@@ -91,7 +91,7 @@ describe('ReactDOMFizzStaticNode', () => {
91
}
92
});
93
94
- // @gate enableHalt || enablePostpone
94
+ // @gate enableHalt
95
it('should emit bootstrap script src at the end', async () => {
96
const result = await ReactDOMFizzStatic.prerenderToNodeStream(
97
<div>hello world</div>,
@@ -107,7 +107,7 @@ describe('ReactDOMFizzStaticNode', () => {
107
);
108
});
109
110
- // @gate enableHalt || enablePostpone
110
+ // @gate enableHalt
111
it('emits all HTML as one unit', async () => {
112
let hasLoaded = false;
113
let resolve;
@@ -137,7 +137,7 @@ describe('ReactDOMFizzStaticNode', () => {
137
expect(prelude).toMatchInlineSnapshot(`"<div><!--$-->Done<!--/$--></div>"`);
138
});
139
140
- // @gate enableHalt || enablePostpone
140
+ // @gate enableHalt
141
it('should reject the promise when an error is thrown at the root', async () => {
142
const reportedErrors = [];
143
let caughtError = null;
@@ -159,7 +159,7 @@ describe('ReactDOMFizzStaticNode', () => {
159
expect(reportedErrors).toEqual([theError]);
160
});
161
162
- // @gate enableHalt || enablePostpone
162
+ // @gate enableHalt
163
it('should reject the promise when an error is thrown inside a fallback', async () => {
164
const reportedErrors = [];
165
let caughtError = null;
@@ -183,7 +183,7 @@ describe('ReactDOMFizzStaticNode', () => {
183
expect(reportedErrors).toEqual([theError]);
184
});
185
186
- // @gate enableHalt || enablePostpone
186
+ // @gate enableHalt
187
it('should not error the stream when an error is thrown inside suspense boundary', async () => {
188
const reportedErrors = [];
189
const result = await ReactDOMFizzStatic.prerenderToNodeStream(
@@ -204,7 +204,7 @@ describe('ReactDOMFizzStaticNode', () => {
204
expect(reportedErrors).toEqual([theError]);
205
});
206
207
- // @gate enableHalt || enablePostpone
207
+ // @gate enableHalt
208
it('should be able to complete by aborting even if the promise never resolves', async () => {
209
const errors = [];
210
const controller = new AbortController();
@@ -234,7 +234,7 @@ describe('ReactDOMFizzStaticNode', () => {
234
expect(errors).toEqual(['This operation was aborted']);
235
});
236
237
- // @gate enableHalt || enablePostpone
237
+ // @gate enableHalt
238
// @gate !enableHalt
239
it('should reject if aborting before the shell is complete and enableHalt is disabled', async () => {
240
const errors = [];
@@ -300,7 +300,7 @@ describe('ReactDOMFizzStaticNode', () => {
300
expect(content).toBe('');
301
});
302
303
- // @gate enableHalt || enablePostpone
303
+ // @gate enableHalt
304
it('should be able to abort before something suspends', async () => {
305
const errors = [];
306
const controller = new AbortController();
@@ -341,7 +341,7 @@ describe('ReactDOMFizzStaticNode', () => {
341
}
342
});
343
344
- // @gate enableHalt || enablePostpone
344
+ // @gate enableHalt
345
// @gate !enableHalt
346
it('should reject if passing an already aborted signal and enableHalt is disabled', async () => {
347
const errors = [];
@@ -412,7 +412,7 @@ describe('ReactDOMFizzStaticNode', () => {
412
expect(content).toBe('');
413
});
414
415
- // @gate enableHalt || enablePostpone
415
+ // @gate enableHalt
416
it('supports custom abort reasons with a string', async () => {
417
const promise = new Promise(r => {});
418
function Wait() {
@@ -454,7 +454,7 @@ describe('ReactDOMFizzStaticNode', () => {
454
expect(errors).toEqual(['foobar', 'foobar']);
455
});
456
457
- // @gate enableHalt || enablePostpone
457
+ // @gate enableHalt
458
it('supports custom abort reasons with an Error', async () => {
459
const promise = new Promise(r => {});
460
function Wait() {
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+1
-9
@@ -7,11 +7,7 @@
7
* @flow
8
*/
9
10
-import type {
11
- PostponedState,
12
- ErrorInfo,
13
- PostponeInfo,
14
-} from 'react-server/src/ReactFizzServer';
10
+import type {PostponedState, ErrorInfo} from 'react-server/src/ReactFizzServer';
11
import type {ReactNodeList, ReactFormState} from 'shared/ReactTypes';
12
import type {
13
BootstrapScriptDescriptor,
@@ -57,7 +53,6 @@ type Options = {
53
progressiveChunkSize?: number,
54
signal?: AbortSignal,
55
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
60
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
56
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
57
importMap?: ImportMap,
58
formState?: ReactFormState<any, any> | null,
@@ -69,7 +64,6 @@ type ResumeOptions = {
64
nonce?: NonceOption,
65
signal?: AbortSignal,
66
onError?: (error: mixed) => ?string,
72
- onPostpone?: (reason: string) => void,
67
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
68
};
69
@@ -150,7 +144,6 @@ function renderToReadableStream(
144
onShellReady,
145
onShellError,
146
onFatalError,
153
- options ? options.onPostpone : undefined,
147
options ? options.formState : undefined,
148
);
149
if (options && options.signal) {
@@ -220,7 +213,6 @@ function resume(
213
onShellReady,
214
onShellError,
215
onFatalError,
223
- options ? options.onPostpone : undefined,
216
);
217
if (options && options.signal) {
218
const signal = options.signal;
packages/react-dom/src/server/ReactDOMFizzServerBun.js
+1
-3
@@ -13,7 +13,7 @@ import type {
13
HeadersDescriptor,
14
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
15
import type {ImportMap} from '../shared/ReactDOMTypes';
16
-import type {ErrorInfo, PostponeInfo} from 'react-server/src/ReactFizzServer';
16
+import type {ErrorInfo} from 'react-server/src/ReactFizzServer';
17
18
import ReactVersion from 'shared/ReactVersion';
19
@@ -49,7 +49,6 @@ type Options = {
49
progressiveChunkSize?: number,
50
signal?: AbortSignal,
51
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
52
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
52
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
53
importMap?: ImportMap,
54
formState?: ReactFormState<any, any> | null,
@@ -135,7 +134,6 @@ function renderToReadableStream(
134
onShellReady,
135
onShellError,
136
onFatalError,
138
- options ? options.onPostpone : undefined,
137
options ? options.formState : undefined,
138
);
139
if (options && options.signal) {
packages/react-dom/src/server/ReactDOMFizzServerEdge.js
+1
-9
@@ -7,11 +7,7 @@
7
* @flow
8
*/
9
10
-import type {
11
- PostponedState,
12
- ErrorInfo,
13
- PostponeInfo,
14
-} from 'react-server/src/ReactFizzServer';
10
+import type {PostponedState, ErrorInfo} from 'react-server/src/ReactFizzServer';
11
import type {ReactNodeList, ReactFormState} from 'shared/ReactTypes';
12
import type {
13
BootstrapScriptDescriptor,
@@ -57,7 +53,6 @@ type Options = {
53
progressiveChunkSize?: number,
54
signal?: AbortSignal,
55
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
60
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
56
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
57
importMap?: ImportMap,
58
formState?: ReactFormState<any, any> | null,
@@ -69,7 +64,6 @@ type ResumeOptions = {
64
nonce?: NonceOption,
65
signal?: AbortSignal,
66
onError?: (error: mixed) => ?string,
72
- onPostpone?: (reason: string) => void,
67
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
68
};
69
@@ -150,7 +144,6 @@ function renderToReadableStream(
144
onShellReady,
145
onShellError,
146
onFatalError,
153
- options ? options.onPostpone : undefined,
147
options ? options.formState : undefined,
148
);
149
if (options && options.signal) {
@@ -220,7 +213,6 @@ function resume(
213
onShellReady,
214
onShellError,
215
onFatalError,
223
- options ? options.onPostpone : undefined,
216
);
217
if (options && options.signal) {
218
const signal = options.signal;
packages/react-dom/src/server/ReactDOMFizzServerNode.js
-7
@@ -11,7 +11,6 @@ import type {
11
Request,
12
PostponedState,
13
ErrorInfo,
14
- PostponeInfo,
14
} from 'react-server/src/ReactFizzServer';
15
import type {ReactNodeList, ReactFormState} from 'shared/ReactTypes';
16
import type {Writable} from 'stream';
@@ -77,7 +76,6 @@ type Options = {
76
onShellError?: (error: mixed) => void,
77
onAllReady?: () => void,
78
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
80
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
79
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
80
importMap?: ImportMap,
81
formState?: ReactFormState<any, any> | null,
@@ -91,7 +89,6 @@ type ResumeOptions = {
89
onShellError?: (error: mixed) => void,
90
onAllReady?: () => void,
91
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
94
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
92
};
93
94
type PipeableStream = {
@@ -127,7 +124,6 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) {
124
options ? options.onShellReady : undefined,
125
options ? options.onShellError : undefined,
126
undefined,
130
- options ? options.onPostpone : undefined,
127
options ? options.formState : undefined,
128
);
129
}
@@ -285,7 +281,6 @@ function renderToReadableStream(
281
onShellReady,
282
onShellError,
283
onFatalError,
288
- options ? options.onPostpone : undefined,
284
options ? options.formState : undefined,
285
);
286
if (options && options.signal) {
@@ -321,7 +316,6 @@ function resumeRequestImpl(
316
options ? options.onShellReady : undefined,
317
options ? options.onShellError : undefined,
318
undefined,
324
- options ? options.onPostpone : undefined,
319
);
320
}
321
@@ -423,7 +417,6 @@ function resume(
417
onShellReady,
418
onShellError,
419
onFatalError,
426
- options ? options.onPostpone : undefined,
420
);
421
if (options && options.signal) {
422
const signal = options.signal;
packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
+10
-19
@@ -12,11 +12,7 @@ import type {
12
BootstrapScriptDescriptor,
13
HeadersDescriptor,
14
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
15
-import type {
16
- PostponedState,
17
- ErrorInfo,
18
- PostponeInfo,
19
-} from 'react-server/src/ReactFizzServer';
15
+import type {PostponedState, ErrorInfo} from 'react-server/src/ReactFizzServer';
16
import type {ImportMap} from '../shared/ReactDOMTypes';
17
18
import ReactVersion from 'shared/ReactVersion';
@@ -38,7 +34,7 @@ import {
34
createRootFormatContext,
35
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
36
41
-import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
37
+import {enableHalt} from 'shared/ReactFeatureFlags';
38
39
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
40
ensureCorrectIsomorphicReactVersion();
@@ -59,7 +55,6 @@ type Options = {
55
progressiveChunkSize?: number,
56
signal?: AbortSignal,
57
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
62
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
58
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
59
importMap?: ImportMap,
60
onHeaders?: (headers: Headers) => void,
@@ -94,15 +89,14 @@ function prerender(
89
{highWaterMark: 0},
90
);
91
97
- const result: StaticResult =
98
- enablePostpone || enableHalt
99
- ? {
100
- postponed: getPostponedState(request),
101
- prelude: stream,
102
- }
103
- : ({
104
- prelude: stream,
105
- }: any);
92
+ const result: StaticResult = enableHalt
93
+ ? {
94
+ postponed: getPostponedState(request),
95
+ prelude: stream,
96
+ }
97
+ : ({
98
+ prelude: stream,
99
+ }: any);
100
resolve(result);
101
}
102
@@ -139,7 +133,6 @@ function prerender(
133
undefined,
134
undefined,
135
onFatalError,
142
- options ? options.onPostpone : undefined,
136
);
137
if (options && options.signal) {
138
const signal = options.signal;
@@ -161,7 +154,6 @@ type ResumeOptions = {
154
nonce?: NonceOption,
155
signal?: AbortSignal,
156
onError?: (error: mixed) => ?string,
164
- onPostpone?: (reason: string) => void,
157
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
158
};
159
@@ -205,7 +197,6 @@ function resumeAndPrerender(
197
undefined,
198
undefined,
199
onFatalError,
208
- options ? options.onPostpone : undefined,
200
);
201
if (options && options.signal) {
202
const signal = options.signal;
packages/react-dom/src/server/ReactDOMFizzStaticEdge.js
+10
-19
@@ -12,11 +12,7 @@ import type {
12
BootstrapScriptDescriptor,
13
HeadersDescriptor,
14
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
15
-import type {
16
- PostponedState,
17
- ErrorInfo,
18
- PostponeInfo,
19
-} from 'react-server/src/ReactFizzServer';
15
+import type {PostponedState, ErrorInfo} from 'react-server/src/ReactFizzServer';
16
import type {ImportMap} from '../shared/ReactDOMTypes';
17
18
import ReactVersion from 'shared/ReactVersion';
@@ -38,7 +34,7 @@ import {
34
createRootFormatContext,
35
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
36
41
-import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
37
+import {enableHalt} from 'shared/ReactFeatureFlags';
38
39
import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
40
ensureCorrectIsomorphicReactVersion();
@@ -59,7 +55,6 @@ type Options = {
55
progressiveChunkSize?: number,
56
signal?: AbortSignal,
57
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
62
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
58
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
59
importMap?: ImportMap,
60
onHeaders?: (headers: Headers) => void,
@@ -94,15 +89,14 @@ function prerender(
89
{highWaterMark: 0},
90
);
91
97
- const result: StaticResult =
98
- enablePostpone || enableHalt
99
- ? {
100
- postponed: getPostponedState(request),
101
- prelude: stream,
102
- }
103
- : ({
104
- prelude: stream,
105
- }: any);
92
+ const result: StaticResult = enableHalt
93
+ ? {
94
+ postponed: getPostponedState(request),
95
+ prelude: stream,
96
+ }
97
+ : ({
98
+ prelude: stream,
99
+ }: any);
100
resolve(result);
101
}
102
@@ -138,7 +132,6 @@ function prerender(
132
undefined,
133
undefined,
134
onFatalError,
141
- options ? options.onPostpone : undefined,
135
);
136
if (options && options.signal) {
137
const signal = options.signal;
@@ -160,7 +153,6 @@ type ResumeOptions = {
153
nonce?: NonceOption,
154
signal?: AbortSignal,
155
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
163
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
156
};
157
158
function resumeAndPrerender(
@@ -203,7 +195,6 @@ function resumeAndPrerender(
195
undefined,
196
undefined,
197
onFatalError,
206
- options ? options.onPostpone : undefined,
198
);
199
if (options && options.signal) {
200
const signal = options.signal;
packages/react-dom/src/server/ReactDOMFizzStaticNode.js
+18
-30
@@ -12,11 +12,7 @@ import type {
12
BootstrapScriptDescriptor,
13
HeadersDescriptor,
14
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
15
-import type {
16
- PostponedState,
17
- ErrorInfo,
18
- PostponeInfo,
19
-} from 'react-server/src/ReactFizzServer';
15
+import type {PostponedState, ErrorInfo} from 'react-server/src/ReactFizzServer';
16
import type {ImportMap} from '../shared/ReactDOMTypes';
17
18
import {Writable, Readable} from 'stream';
@@ -40,7 +36,7 @@ import {
36
createRootFormatContext,
37
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
38
43
-import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
39
+import {enableHalt} from 'shared/ReactFeatureFlags';
40
41
import {textEncoder} from 'react-server/src/ReactServerStreamConfigNode';
42
@@ -63,7 +59,6 @@ type Options = {
59
progressiveChunkSize?: number,
60
signal?: AbortSignal,
61
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
66
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
62
unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
63
importMap?: ImportMap,
64
onHeaders?: (headers: HeadersDescriptor) => void,
@@ -135,15 +130,14 @@ function prerenderToNodeStream(
130
});
131
const writable = createFakeWritableFromReadable(readable);
132
138
- const result: StaticResult =
139
- enablePostpone || enableHalt
140
- ? {
141
- postponed: getPostponedState(request),
142
- prelude: readable,
143
- }
144
- : ({
145
- prelude: readable,
146
- }: any);
133
+ const result: StaticResult = enableHalt
134
+ ? {
135
+ postponed: getPostponedState(request),
136
+ prelude: readable,
137
+ }
138
+ : ({
139
+ prelude: readable,
140
+ }: any);
141
resolve(result);
142
}
143
const resumableState = createResumableState(
@@ -171,7 +165,6 @@ function prerenderToNodeStream(
165
undefined,
166
undefined,
167
onFatalError,
174
- options ? options.onPostpone : undefined,
168
);
169
if (options && options.signal) {
170
const signal = options.signal;
@@ -222,15 +215,14 @@ function prerender(
215
{highWaterMark: 0},
216
);
217
225
- const result =
226
- enablePostpone || enableHalt
227
- ? {
228
- postponed: getPostponedState(request),
229
- prelude: stream,
230
- }
231
- : ({
232
- prelude: stream,
233
- }: any);
218
+ const result = enableHalt
219
+ ? {
220
+ postponed: getPostponedState(request),
221
+ prelude: stream,
222
+ }
223
+ : ({
224
+ prelude: stream,
225
+ }: any);
226
resolve(result);
227
}
228
@@ -266,7 +258,6 @@ function prerender(
258
undefined,
259
undefined,
260
onFatalError,
269
- options ? options.onPostpone : undefined,
261
);
262
if (options && options.signal) {
263
const signal = options.signal;
@@ -288,7 +279,6 @@ type ResumeOptions = {
279
nonce?: NonceOption,
280
signal?: AbortSignal,
281
onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
291
- onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,
282
};
283
284
function resumeAndPrerenderToNodeStream(
@@ -322,7 +312,6 @@ function resumeAndPrerenderToNodeStream(
312
undefined,
313
undefined,
314
onFatalError,
325
- options ? options.onPostpone : undefined,
315
);
316
if (options && options.signal) {
317
const signal = options.signal;
@@ -388,7 +377,6 @@ function resumeAndPrerender(
377
undefined,
378
undefined,
379
onFatalError,
391
- options ? options.onPostpone : undefined,
380
);
381
if (options && options.signal) {
382
const signal = options.signal;
packages/react-markup/src/ReactMarkupClient.js
-1
@@ -85,7 +85,6 @@ export function experimental_renderToHTML(
85
undefined,
86
undefined,
87
undefined,
88
- undefined,
88
);
89
if (options && options.signal) {
90
const signal = options.signal;
packages/react-markup/src/ReactMarkupServer.js
-2
@@ -184,7 +184,6 @@ export function experimental_renderToHTML(
184
handleFlightError,
185
options ? options.identifierPrefix : undefined,
186
undefined,
187
- undefined,
187
'Markup',
188
undefined,
189
false,
@@ -214,7 +213,6 @@ export function experimental_renderToHTML(
213
undefined,
214
undefined,
215
undefined,
217
- undefined,
216
);
217
if (options && options.signal) {
218
const signal = options.signal;
packages/react-noop-renderer/src/ReactNoopFlightServer.js
-2
@@ -73,7 +73,6 @@ type Options = {
73
signal?: AbortSignal,
74
debugChannel?: {onMessage?: (message: string) => void},
75
onError?: (error: mixed) => void,
76
- onPostpone?: (reason: string) => void,
76
};
77
78
function render(model: ReactClientValue, options?: Options): Destination {
@@ -84,7 +83,6 @@ function render(model: ReactClientValue, options?: Options): Destination {
83
bundlerConfig,
84
options ? options.onError : undefined,
85
options ? options.identifierPrefix : undefined,
87
- options ? options.onPostpone : undefined,
86
undefined,
87
__DEV__ && options ? options.environmentName : undefined,
88
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-reconciler/src/ReactFiberBeginWork.js
+17
-21
@@ -115,7 +115,6 @@ import {
115
enableTransitionTracing,
116
enableLegacyHidden,
117
enableCPUSuspense,
118
- enablePostpone,
118
disableLegacyMode,
119
enableHydrationLaneScheduling,
120
enableViewTransition,
@@ -2973,28 +2972,25 @@ function updateDehydratedSuspenseComponent(
2972
({digest} = getSuspenseInstanceFallbackErrorDetails(suspenseInstance));
2973
}
2974
2976
- // TODO: Figure out a better signal than encoding a magic digest value.
2977
- if (!enablePostpone || digest !== 'POSTPONE') {
2978
- let error: Error;
2979
- if (__DEV__ && message) {
2980
- // eslint-disable-next-line react-internal/prod-error-codes
2981
- error = new Error(message);
2982
- } else {
2983
- error = new Error(
2984
- 'The server could not finish this Suspense boundary, likely ' +
2985
- 'due to an error during server rendering. ' +
2986
- 'Switched to client rendering.',
2987
- );
2988
- }
2989
- // Replace the stack with the server stack
2990
- error.stack = (__DEV__ && stack) || '';
2991
- (error: any).digest = digest;
2992
- const capturedValue = createCapturedValueFromError(
2993
- error,
2994
- componentStack === undefined ? null : componentStack,
2975
+ let error: Error;
2976
+ if (__DEV__ && message) {
2977
+ // eslint-disable-next-line react-internal/prod-error-codes
2978
+ error = new Error(message);
2979
+ } else {
2980
+ error = new Error(
2981
+ 'The server could not finish this Suspense boundary, likely ' +
2982
+ 'due to an error during server rendering. ' +
2983
+ 'Switched to client rendering.',
2984
);
2996
- queueHydrationError(capturedValue);
2985
}
2986
+ // Replace the stack with the server stack
2987
+ error.stack = (__DEV__ && stack) || '';
2988
+ (error: any).digest = digest;
2989
+ const capturedValue = createCapturedValueFromError(
2990
+ error,
2991
+ componentStack === undefined ? null : componentStack,
2992
+ );
2993
+ queueHydrationError(capturedValue);
2994
return retrySuspenseComponentWithoutHydrating(
2995
current,
2996
workInProgress,
packages/react-reconciler/src/ReactFiberCommitWork.js
+2
-2
@@ -3767,7 +3767,7 @@ function commitPassiveMountOnFiber(
3767
inHydratedSubtree = false;
3768
const hydrationErrors = prevState.hydrationErrors;
3769
// If there were no hydration errors, that suggests that this was an intentional client
3770
- // rendered boundary. Such as postpone.
3770
+ // rendered boundary.
3771
if (hydrationErrors !== null) {
3772
const startTime: number = (finishedWork.actualStartTime: any);
3773
logComponentErrored(
@@ -3825,7 +3825,7 @@ function commitPassiveMountOnFiber(
3825
inHydratedSubtree = false;
3826
const hydrationErrors = prevState.hydrationErrors;
3827
// If there were no hydration errors, that suggests that this was an intentional client
3828
- // rendered boundary. Such as postpone.
3828
+ // rendered boundary.
3829
if (hydrationErrors !== null) {
3830
const startTime: number = (finishedWork.actualStartTime: any);
3831
logComponentErrored(
packages/react-reconciler/src/ReactFiberThrow.js
-6
@@ -42,7 +42,6 @@ import {
42
import {NoMode, ConcurrentMode} from './ReactTypeOfMode';
43
import {
44
enableUpdaterTracking,
45
- enablePostpone,
45
disableLegacyMode,
46
} from 'shared/ReactFeatureFlags';
47
import {createCapturedValueAtFiber} from './ReactCapturedValue';
@@ -85,7 +84,6 @@ import {
84
} from './ReactFiberHydrationContext';
85
import {ConcurrentRoot} from './ReactRootTags';
86
import {noopSuspenseyCommitThenable} from './ReactFiberThenable';
88
-import {REACT_POSTPONE_TYPE} from 'shared/ReactSymbols';
87
import {runWithFiberInDEV} from './ReactCurrentFiber';
88
import {callComponentDidCatchInDEV} from './ReactFiberCallUserSpace';
89
@@ -378,10 +376,6 @@ function throwException(
376
}
377
378
if (value !== null && typeof value === 'object') {
381
- if (enablePostpone && value.$$typeof === REACT_POSTPONE_TYPE) {
382
- // Act as if this is an infinitely suspending promise.
383
- value = {then: function () {}};
384
- }
379
if (typeof value.then === 'function') {
380
// This is a wakeable. The component suspended.
381
const wakeable: Wakeable = (value: any);
packages/react-server-dom-esm/src/server/ReactFlightDOMServerNode.js
-4
@@ -144,7 +144,6 @@ type Options = {
144
environmentName?: string | (() => string),
145
filterStackFrame?: (url: string, functionName: string) => boolean,
146
onError?: (error: mixed) => void,
147
- onPostpone?: (reason: string) => void,
147
identifierPrefix?: string,
148
temporaryReferences?: TemporaryReferenceSet,
149
};
@@ -183,7 +182,6 @@ function renderToPipeableStream(
182
moduleBasePath,
183
options ? options.onError : undefined,
184
options ? options.identifierPrefix : undefined,
186
- options ? options.onPostpone : undefined,
185
options ? options.temporaryReferences : undefined,
186
__DEV__ && options ? options.environmentName : undefined,
187
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -271,7 +269,6 @@ type PrerenderOptions = {
269
environmentName?: string | (() => string),
270
filterStackFrame?: (url: string, functionName: string) => boolean,
271
onError?: (error: mixed) => void,
274
- onPostpone?: (reason: string) => void,
272
identifierPrefix?: string,
273
temporaryReferences?: TemporaryReferenceSet,
274
signal?: AbortSignal,
@@ -305,7 +302,6 @@ function prerenderToNodeStream(
302
onFatalError,
303
options ? options.onError : undefined,
304
options ? options.identifierPrefix : undefined,
308
- options ? options.onPostpone : undefined,
305
options ? options.temporaryReferences : undefined,
306
__DEV__ && options ? options.environmentName : undefined,
307
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerBrowser.js
-3
@@ -67,7 +67,6 @@ type Options = {
67
signal?: AbortSignal,
68
temporaryReferences?: TemporaryReferenceSet,
69
onError?: (error: mixed) => void,
70
- onPostpone?: (reason: string) => void,
70
};
71
72
function startReadingFromDebugChannelReadableStream(
@@ -128,7 +127,6 @@ export function renderToReadableStream(
127
null,
128
options ? options.onError : undefined,
129
options ? options.identifierPrefix : undefined,
131
- options ? options.onPostpone : undefined,
130
options ? options.temporaryReferences : undefined,
131
__DEV__ && options ? options.environmentName : undefined,
132
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -216,7 +214,6 @@ export function prerender(
214
onFatalError,
215
options ? options.onError : undefined,
216
options ? options.identifierPrefix : undefined,
219
- options ? options.onPostpone : undefined,
217
options ? options.temporaryReferences : undefined,
218
__DEV__ && options ? options.environmentName : undefined,
219
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerEdge.js
-3
@@ -72,7 +72,6 @@ type Options = {
72
signal?: AbortSignal,
73
temporaryReferences?: TemporaryReferenceSet,
74
onError?: (error: mixed) => void,
75
- onPostpone?: (reason: string) => void,
75
};
76
77
function startReadingFromDebugChannelReadableStream(
@@ -133,7 +132,6 @@ export function renderToReadableStream(
132
null,
133
options ? options.onError : undefined,
134
options ? options.identifierPrefix : undefined,
136
- options ? options.onPostpone : undefined,
135
options ? options.temporaryReferences : undefined,
136
__DEV__ && options ? options.environmentName : undefined,
137
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -221,7 +219,6 @@ export function prerender(
219
onFatalError,
220
options ? options.onError : undefined,
221
options ? options.identifierPrefix : undefined,
224
- options ? options.onPostpone : undefined,
222
options ? options.temporaryReferences : undefined,
223
__DEV__ && options ? options.environmentName : undefined,
224
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-parcel/src/server/ReactFlightDOMServerNode.js
-6
@@ -157,7 +157,6 @@ type Options = {
157
environmentName?: string | (() => string),
158
filterStackFrame?: (url: string, functionName: string) => boolean,
159
onError?: (error: mixed) => void,
160
- onPostpone?: (reason: string) => void,
160
identifierPrefix?: string,
161
temporaryReferences?: TemporaryReferenceSet,
162
};
@@ -195,7 +194,6 @@ export function renderToPipeableStream(
194
null,
195
options ? options.onError : undefined,
196
options ? options.identifierPrefix : undefined,
198
- options ? options.onPostpone : undefined,
197
options ? options.temporaryReferences : undefined,
198
__DEV__ && options ? options.environmentName : undefined,
199
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -353,7 +351,6 @@ export function renderToReadableStream(
351
null,
352
options ? options.onError : undefined,
353
options ? options.identifierPrefix : undefined,
356
- options ? options.onPostpone : undefined,
354
options ? options.temporaryReferences : undefined,
355
__DEV__ && options ? options.environmentName : undefined,
356
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -434,7 +431,6 @@ type PrerenderOptions = {
431
environmentName?: string | (() => string),
432
filterStackFrame?: (url: string, functionName: string) => boolean,
433
onError?: (error: mixed) => void,
437
- onPostpone?: (reason: string) => void,
434
identifierPrefix?: string,
435
temporaryReferences?: TemporaryReferenceSet,
436
signal?: AbortSignal,
@@ -467,7 +463,6 @@ export function prerenderToNodeStream(
463
onFatalError,
464
options ? options.onError : undefined,
465
options ? options.identifierPrefix : undefined,
470
- options ? options.onPostpone : undefined,
466
options ? options.temporaryReferences : undefined,
467
__DEV__ && options ? options.environmentName : undefined,
468
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -530,7 +525,6 @@ export function prerender(
525
onFatalError,
526
options ? options.onError : undefined,
527
options ? options.identifierPrefix : undefined,
533
- options ? options.onPostpone : undefined,
528
options ? options.temporaryReferences : undefined,
529
__DEV__ && options ? options.environmentName : undefined,
530
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-turbopack/src/server/ReactFlightDOMServerBrowser.js
-3
@@ -64,7 +64,6 @@ type Options = {
64
signal?: AbortSignal,
65
temporaryReferences?: TemporaryReferenceSet,
66
onError?: (error: mixed) => void,
67
- onPostpone?: (reason: string) => void,
67
};
68
69
function startReadingFromDebugChannelReadableStream(
@@ -126,7 +125,6 @@ function renderToReadableStream(
125
turbopackMap,
126
options ? options.onError : undefined,
127
options ? options.identifierPrefix : undefined,
129
- options ? options.onPostpone : undefined,
128
options ? options.temporaryReferences : undefined,
129
__DEV__ && options ? options.environmentName : undefined,
130
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -215,7 +213,6 @@ function prerender(
213
onFatalError,
214
options ? options.onError : undefined,
215
options ? options.identifierPrefix : undefined,
218
- options ? options.onPostpone : undefined,
216
options ? options.temporaryReferences : undefined,
217
__DEV__ && options ? options.environmentName : undefined,
218
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-turbopack/src/server/ReactFlightDOMServerEdge.js
-3
@@ -69,7 +69,6 @@ type Options = {
69
signal?: AbortSignal,
70
temporaryReferences?: TemporaryReferenceSet,
71
onError?: (error: mixed) => void,
72
- onPostpone?: (reason: string) => void,
72
};
73
74
function startReadingFromDebugChannelReadableStream(
@@ -131,7 +130,6 @@ function renderToReadableStream(
130
turbopackMap,
131
options ? options.onError : undefined,
132
options ? options.identifierPrefix : undefined,
134
- options ? options.onPostpone : undefined,
133
options ? options.temporaryReferences : undefined,
134
__DEV__ && options ? options.environmentName : undefined,
135
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -220,7 +218,6 @@ function prerender(
218
onFatalError,
219
options ? options.onError : undefined,
220
options ? options.identifierPrefix : undefined,
223
- options ? options.onPostpone : undefined,
221
options ? options.temporaryReferences : undefined,
222
__DEV__ && options ? options.environmentName : undefined,
223
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-turbopack/src/server/ReactFlightDOMServerNode.js
-6
@@ -150,7 +150,6 @@ type Options = {
150
environmentName?: string | (() => string),
151
filterStackFrame?: (url: string, functionName: string) => boolean,
152
onError?: (error: mixed) => void,
153
- onPostpone?: (reason: string) => void,
153
identifierPrefix?: string,
154
temporaryReferences?: TemporaryReferenceSet,
155
};
@@ -189,7 +188,6 @@ function renderToPipeableStream(
188
turbopackMap,
189
options ? options.onError : undefined,
190
options ? options.identifierPrefix : undefined,
192
- options ? options.onPostpone : undefined,
191
options ? options.temporaryReferences : undefined,
192
__DEV__ && options ? options.environmentName : undefined,
193
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -348,7 +346,6 @@ function renderToReadableStream(
346
turbopackMap,
347
options ? options.onError : undefined,
348
options ? options.identifierPrefix : undefined,
351
- options ? options.onPostpone : undefined,
349
options ? options.temporaryReferences : undefined,
350
__DEV__ && options ? options.environmentName : undefined,
351
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -429,7 +426,6 @@ type PrerenderOptions = {
426
environmentName?: string | (() => string),
427
filterStackFrame?: (url: string, functionName: string) => boolean,
428
onError?: (error: mixed) => void,
432
- onPostpone?: (reason: string) => void,
429
identifierPrefix?: string,
430
temporaryReferences?: TemporaryReferenceSet,
431
signal?: AbortSignal,
@@ -463,7 +459,6 @@ function prerenderToNodeStream(
459
onFatalError,
460
options ? options.onError : undefined,
461
options ? options.identifierPrefix : undefined,
466
- options ? options.onPostpone : undefined,
462
options ? options.temporaryReferences : undefined,
463
__DEV__ && options ? options.environmentName : undefined,
464
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -527,7 +522,6 @@ function prerender(
522
onFatalError,
523
options ? options.onError : undefined,
524
options ? options.identifierPrefix : undefined,
530
- options ? options.onPostpone : undefined,
525
options ? options.temporaryReferences : undefined,
526
__DEV__ && options ? options.environmentName : undefined,
527
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
+2
-87
@@ -34,7 +34,6 @@ let ReactServerDOMServer;
34
let ReactServerDOMStaticServer;
35
let ReactServerDOMClient;
36
let ReactDOMFizzServer;
37
-let ReactDOMStaticServer;
37
let Suspense;
38
let ErrorBoundary;
39
let JSDOM;
@@ -88,7 +87,6 @@ describe('ReactFlightDOM', () => {
87
Suspense = React.Suspense;
88
ReactDOMClient = require('react-dom/client');
89
ReactDOMFizzServer = require('react-dom/server.node');
91
- ReactDOMStaticServer = require('react-dom/static.node');
90
ReactServerDOMClient = require('react-server-dom-webpack/client');
91
92
ErrorBoundary = class extends React.Component {
@@ -1633,89 +1631,6 @@ describe('ReactFlightDOM', () => {
1631
expect(getMeaningfulChildren(container)).toEqual(<p>hello world</p>);
1632
});
1633
1636
- // @gate enablePostpone
1637
- it('should allow postponing in Flight through a serialized promise', async () => {
1638
- const Context = React.createContext();
1639
- const ContextProvider = Context.Provider;
1640
-
1641
- function Foo() {
1642
- const value = React.use(React.useContext(Context));
1643
- return <span>{value}</span>;
1644
- }
1645
-
1646
- const ClientModule = clientExports({
1647
- ContextProvider,
1648
- Foo,
1649
- });
1650
-
1651
- async function getFoo() {
1652
- React.unstable_postpone('foo');
1653
- }
1654
-
1655
- function App() {
1656
- return (
1657
- <ClientModule.ContextProvider value={getFoo()}>
1658
- <div>
1659
- <Suspense fallback="loading...">
1660
- <ClientModule.Foo />
1661
- </Suspense>
1662
- </div>
1663
- </ClientModule.ContextProvider>
1664
- );
1665
- }
1666
-
1667
- const {writable, readable} = getTestStream();
1668
-
1669
- const {pipe} = await serverAct(() =>
1670
- ReactServerDOMServer.renderToPipeableStream(<App />, webpackMap),
1671
- );
1672
- pipe(writable);
1673
-
1674
- let response = null;
1675
- function getResponse() {
1676
- if (response === null) {
1677
- response = ReactServerDOMClient.createFromReadableStream(readable);
1678
- }
1679
- return response;
1680
- }
1681
-
1682
- function Response() {
1683
- return getResponse();
1684
- }
1685
-
1686
- const errors = [];
1687
- function onError(error, errorInfo) {
1688
- errors.push(error, errorInfo);
1689
- }
1690
- const result = await serverAct(() =>
1691
- ReactDOMStaticServer.prerenderToNodeStream(<Response />, {
1692
- onError,
1693
- }),
1694
- );
1695
-
1696
- const prelude = await new Promise((resolve, reject) => {
1697
- let content = '';
1698
- result.prelude.on('data', chunk => {
1699
- content += Buffer.from(chunk).toString('utf8');
1700
- });
1701
- result.prelude.on('error', error => {
1702
- reject(error);
1703
- });
1704
- result.prelude.on('end', () => resolve(content));
1705
- });
1706
-
1707
- expect(errors).toEqual([]);
1708
- const doc = new JSDOM(prelude).window.document;
1709
- expect(getMeaningfulChildren(doc)).toEqual(
1710
- <html>
1711
- <head />
1712
- <body>
1713
- <div>loading...</div>
1714
- </body>
1715
- </html>,
1716
- );
1717
- });
1718
-
1634
it('should support float methods when rendering in Fizz', async () => {
1635
function Component() {
1636
return <p>hello world</p>;
@@ -2870,7 +2785,7 @@ describe('ReactFlightDOM', () => {
2785
);
2786
});
2787
2873
- // @gate enableHalt || enablePostpone
2788
+ // @gate enableHalt
2789
it('can prerender', async () => {
2790
let resolveGreeting;
2791
const greetingPromise = new Promise(resolve => {
@@ -3016,7 +2931,7 @@ describe('ReactFlightDOM', () => {
2931
});
2932
2933
// This could be a bug. Discovered while making enableAsyncDebugInfo dynamic for www.
3019
- // @gate enableHalt || enablePostpone || (enableAsyncDebugInfo && __DEV__)
2934
+ // @gate enableHalt || (enableAsyncDebugInfo && __DEV__)
2935
it('will leave async iterables in an incomplete state when halting', async () => {
2936
let resolve;
2937
const wait = new Promise(r => (resolve = r));
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+5
-111
@@ -1934,52 +1934,6 @@ describe('ReactFlightDOMBrowser', () => {
1934
);
1935
});
1936
1937
- // @gate enablePostpone
1938
- it('supports postpone in Server Components', async () => {
1939
- function Server() {
1940
- React.unstable_postpone('testing postpone');
1941
- return 'Not shown';
1942
- }
1943
-
1944
- let postponed = null;
1945
-
1946
- const stream = await serverAct(() =>
1947
- ReactServerDOMServer.renderToReadableStream(
1948
- <Suspense fallback="Loading...">
1949
- <Server />
1950
- </Suspense>,
1951
- null,
1952
- {
1953
- onPostpone(reason) {
1954
- postponed = reason;
1955
- },
1956
- },
1957
- ),
1958
- );
1959
- const response = ReactServerDOMClient.createFromReadableStream(stream);
1960
-
1961
- function Client() {
1962
- return use(response);
1963
- }
1964
-
1965
- const container = document.createElement('div');
1966
- const root = ReactDOMClient.createRoot(container);
1967
- await act(async () => {
1968
- root.render(
1969
- <div>
1970
- Shell: <Client />
1971
- </div>,
1972
- );
1973
- });
1974
- // We should have reserved the shell already. Which means that the Server
1975
- // Component should've been a lazy component.
1976
- expect(container.innerHTML).toContain('Shell:');
1977
- expect(container.innerHTML).toContain('Loading...');
1978
- expect(container.innerHTML).not.toContain('Not shown');
1979
-
1980
- expect(postponed).toBe('testing postpone');
1981
- });
1982
-
1937
it('should not continue rendering after the reader cancels', async () => {
1938
let hasLoaded = false;
1939
let resolve;
@@ -2031,66 +1985,6 @@ describe('ReactFlightDOMBrowser', () => {
1985
]);
1986
});
1987
2034
- // @gate enablePostpone
2035
- it('postpones when abort passes a postpone signal', async () => {
2036
- const infinitePromise = new Promise(() => {});
2037
- function Server() {
2038
- return infinitePromise;
2039
- }
2040
-
2041
- let postponed = null;
2042
- let error = null;
2043
-
2044
- const controller = new AbortController();
2045
- const stream = await serverAct(() =>
2046
- ReactServerDOMServer.renderToReadableStream(
2047
- <Suspense fallback="Loading...">
2048
- <Server />
2049
- </Suspense>,
2050
- null,
2051
- {
2052
- onError(x) {
2053
- error = x;
2054
- },
2055
- onPostpone(reason) {
2056
- postponed = reason;
2057
- },
2058
- signal: controller.signal,
2059
- },
2060
- ),
2061
- );
2062
-
2063
- try {
2064
- React.unstable_postpone('testing postpone');
2065
- } catch (reason) {
2066
- controller.abort(reason);
2067
- }
2068
-
2069
- const response = ReactServerDOMClient.createFromReadableStream(stream);
2070
-
2071
- function Client() {
2072
- return use(response);
2073
- }
2074
-
2075
- const container = document.createElement('div');
2076
- const root = ReactDOMClient.createRoot(container);
2077
- await act(() => {
2078
- root.render(
2079
- <div>
2080
- Shell: <Client />
2081
- </div>,
2082
- );
2083
- });
2084
- // We should have reserved the shell already. Which means that the Server
2085
- // Component should've been a lazy component.
2086
- expect(container.innerHTML).toContain('Shell:');
2087
- expect(container.innerHTML).toContain('Loading...');
2088
- expect(container.innerHTML).not.toContain('Not shown');
2089
-
2090
- expect(postponed).toBe('testing postpone');
2091
- expect(error).toBe(null);
2092
- });
2093
-
1988
function passThrough(stream) {
1989
// Simulate more realistic network by splitting up and rejoining some chunks.
1990
// This lets us test that we don't accidentally rely on particular bounds of the chunks.
@@ -2495,7 +2389,7 @@ describe('ReactFlightDOMBrowser', () => {
2389
expect(errors).toEqual([reason]);
2390
});
2391
2498
- // @gate enableHalt || enablePostpone
2392
+ // @gate enableHalt
2393
it('can prerender', async () => {
2394
let resolveGreeting;
2395
const greetingPromise = new Promise(resolve => {
@@ -3009,9 +2903,9 @@ describe('ReactFlightDOMBrowser', () => {
2903
[
2904
"",
2905
"/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js",
3012
- 2935,
2906
+ 2829,
2907
27,
3014
- 2929,
2908
+ 2823,
2909
34,
2910
],
2911
[
@@ -3025,9 +2919,9 @@ describe('ReactFlightDOMBrowser', () => {
2919
[
2920
"Object.<anonymous>",
2921
"/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js",
3028
- 2929,
2922
+ 2823,
2923
18,
3030
- 2916,
2924
+ 2810,
2925
89,
2926
],
2927
],
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js
+7
-7
@@ -1365,7 +1365,7 @@ describe('ReactFlightDOMEdge', () => {
1365
]);
1366
});
1367
1368
- // @gate enableHalt || enablePostpone
1368
+ // @gate enableHalt
1369
it('can prerender', async () => {
1370
let resolveGreeting;
1371
const greetingPromise = new Promise(resolve => {
@@ -1547,7 +1547,7 @@ describe('ReactFlightDOMEdge', () => {
1547
expect(error.message).toBe('Connection closed.');
1548
});
1549
1550
- // @gate enableHalt || enablePostpone
1550
+ // @gate enableHalt
1551
it('should be able to handle a rejected promise in prerender', async () => {
1552
const expectedError = new Error('Bam!');
1553
const errors = [];
@@ -1586,7 +1586,7 @@ describe('ReactFlightDOMEdge', () => {
1586
expect(error.message).toBe(expectedMessage);
1587
});
1588
1589
- // @gate enableHalt || enablePostpone
1589
+ // @gate enableHalt
1590
it('should be able to handle an erroring async iterable in prerender', async () => {
1591
const expectedError = new Error('Bam!');
1592
const errors = [];
@@ -1633,7 +1633,7 @@ describe('ReactFlightDOMEdge', () => {
1633
expect(error.message).toBe(expectedMessage);
1634
});
1635
1636
- // @gate enableHalt || enablePostpone
1636
+ // @gate enableHalt
1637
it('should be able to handle an erroring readable stream in prerender', async () => {
1638
const expectedError = new Error('Bam!');
1639
const errors = [];
@@ -1681,7 +1681,7 @@ describe('ReactFlightDOMEdge', () => {
1681
expect(error.message).toBe(expectedMessage);
1682
});
1683
1684
- // @gate enableHalt || enablePostpone
1684
+ // @gate enableHalt
1685
it('can prerender an async iterable', async () => {
1686
const errors = [];
1687
@@ -1725,7 +1725,7 @@ describe('ReactFlightDOMEdge', () => {
1725
expect(text).toBe('hello world');
1726
});
1727
1728
- // @gate enableHalt || enablePostpone
1728
+ // @gate enableHalt
1729
it('can prerender a readable stream', async () => {
1730
const errors = [];
1731
@@ -1759,7 +1759,7 @@ describe('ReactFlightDOMEdge', () => {
1759
expect(result).toBe('hello world');
1760
});
1761
1762
- // @gate enableHalt || enablePostpone
1762
+ // @gate enableHalt
1763
it('does not return a prerender prelude early when an error is emitted and there are still pending tasks', async () => {
1764
let rejectPromise;
1765
const rejectingPromise = new Promise(
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js
+2
-2
@@ -500,7 +500,7 @@ describe('ReactFlightDOMNode', () => {
500
expect(errors).toEqual([reason]);
501
});
502
503
- // @gate enableHalt || enablePostpone
503
+ // @gate enableHalt
504
it('can prerender', async () => {
505
let resolveGreeting;
506
const greetingPromise = new Promise(resolve => {
@@ -920,7 +920,7 @@ describe('ReactFlightDOMNode', () => {
920
}
921
});
922
923
- // @gate enableHalt || enablePostpone
923
+ // @gate enableHalt
924
// @gate enableHalt
925
it('can handle an empty prelude when prerendering', async () => {
926
function App() {
packages/react-server-dom-webpack/src/server/ReactFlightDOMServerBrowser.js
-3
@@ -64,7 +64,6 @@ type Options = {
64
signal?: AbortSignal,
65
temporaryReferences?: TemporaryReferenceSet,
66
onError?: (error: mixed) => void,
67
- onPostpone?: (reason: string) => void,
67
};
68
69
function startReadingFromDebugChannelReadableStream(
@@ -126,7 +125,6 @@ function renderToReadableStream(
125
webpackMap,
126
options ? options.onError : undefined,
127
options ? options.identifierPrefix : undefined,
129
- options ? options.onPostpone : undefined,
128
options ? options.temporaryReferences : undefined,
129
__DEV__ && options ? options.environmentName : undefined,
130
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -215,7 +213,6 @@ function prerender(
213
onFatalError,
214
options ? options.onError : undefined,
215
options ? options.identifierPrefix : undefined,
218
- options ? options.onPostpone : undefined,
216
options ? options.temporaryReferences : undefined,
217
__DEV__ && options ? options.environmentName : undefined,
218
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-webpack/src/server/ReactFlightDOMServerEdge.js
-3
@@ -69,7 +69,6 @@ type Options = {
69
signal?: AbortSignal,
70
temporaryReferences?: TemporaryReferenceSet,
71
onError?: (error: mixed) => void,
72
- onPostpone?: (reason: string) => void,
72
};
73
74
function startReadingFromDebugChannelReadableStream(
@@ -131,7 +130,6 @@ function renderToReadableStream(
130
webpackMap,
131
options ? options.onError : undefined,
132
options ? options.identifierPrefix : undefined,
134
- options ? options.onPostpone : undefined,
133
options ? options.temporaryReferences : undefined,
134
__DEV__ && options ? options.environmentName : undefined,
135
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -220,7 +218,6 @@ function prerender(
218
onFatalError,
219
options ? options.onError : undefined,
220
options ? options.identifierPrefix : undefined,
223
- options ? options.onPostpone : undefined,
221
options ? options.temporaryReferences : undefined,
222
__DEV__ && options ? options.environmentName : undefined,
223
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server-dom-webpack/src/server/ReactFlightDOMServerNode.js
-6
@@ -150,7 +150,6 @@ type Options = {
150
environmentName?: string | (() => string),
151
filterStackFrame?: (url: string, functionName: string) => boolean,
152
onError?: (error: mixed) => void,
153
- onPostpone?: (reason: string) => void,
153
identifierPrefix?: string,
154
temporaryReferences?: TemporaryReferenceSet,
155
};
@@ -189,7 +188,6 @@ function renderToPipeableStream(
188
webpackMap,
189
options ? options.onError : undefined,
190
options ? options.identifierPrefix : undefined,
192
- options ? options.onPostpone : undefined,
191
options ? options.temporaryReferences : undefined,
192
__DEV__ && options ? options.environmentName : undefined,
193
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -348,7 +346,6 @@ function renderToReadableStream(
346
webpackMap,
347
options ? options.onError : undefined,
348
options ? options.identifierPrefix : undefined,
351
- options ? options.onPostpone : undefined,
349
options ? options.temporaryReferences : undefined,
350
__DEV__ && options ? options.environmentName : undefined,
351
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -429,7 +426,6 @@ type PrerenderOptions = {
426
environmentName?: string | (() => string),
427
filterStackFrame?: (url: string, functionName: string) => boolean,
428
onError?: (error: mixed) => void,
432
- onPostpone?: (reason: string) => void,
429
identifierPrefix?: string,
430
temporaryReferences?: TemporaryReferenceSet,
431
signal?: AbortSignal,
@@ -463,7 +459,6 @@ function prerenderToNodeStream(
459
onFatalError,
460
options ? options.onError : undefined,
461
options ? options.identifierPrefix : undefined,
466
- options ? options.onPostpone : undefined,
462
options ? options.temporaryReferences : undefined,
463
__DEV__ && options ? options.environmentName : undefined,
464
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -527,7 +522,6 @@ function prerender(
522
onFatalError,
523
options ? options.onError : undefined,
524
options ? options.identifierPrefix : undefined,
530
- options ? options.onPostpone : undefined,
525
options ? options.temporaryReferences : undefined,
526
__DEV__ && options ? options.environmentName : undefined,
527
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server/README.md
-2
@@ -43,7 +43,6 @@ function render(
43
clientManifest,
44
options ? options.onError : undefined,
45
options ? options.identifierPrefix : undefined,
46
- options ? options.onPostpone : undefined,
46
options ? options.temporaryReferences : undefined,
47
__DEV__ && options ? options.environmentName : undefined,
48
__DEV__ && options ? options.filterStackFrame : undefined,
@@ -214,7 +213,6 @@ function prerender(
213
onFatalError,
214
options ? options.onError : undefined,
215
options ? options.identifierPrefix : undefined,
217
- options ? options.onPostpone : undefined,
216
options ? options.temporaryReferences : undefined,
217
__DEV__ && options ? options.environmentName : undefined,
218
__DEV__ && options ? options.filterStackFrame : undefined,
packages/react-server/src/ReactFizzServer.js
+36
-322
@@ -168,7 +168,6 @@ import {
168
REACT_CONTEXT_TYPE,
169
REACT_CONSUMER_TYPE,
170
REACT_SCOPE_TYPE,
171
- REACT_POSTPONE_TYPE,
171
REACT_VIEW_TRANSITION_TYPE,
172
REACT_ACTIVITY_TYPE,
173
} from 'shared/ReactSymbols';
@@ -177,7 +176,6 @@ import {
176
disableLegacyContext,
177
disableLegacyContextForFunctionComponents,
178
enableScopeAPI,
180
- enablePostpone,
179
enableHalt,
180
enableAsyncIterableChildren,
181
enableViewTransition,
@@ -190,7 +188,6 @@ import noop from 'shared/noop';
188
import getComponentNameFromType from 'shared/getComponentNameFromType';
189
import isArray from 'shared/isArray';
190
import {SuspenseException, getSuspendedThenable} from './ReactFizzThenable';
193
-import type {Postpone} from 'react/src/ReactPostpone';
191
192
// Linked list representing the identity of a component given the component/tag name and key.
193
// The name might be minified but we assume that it's going to be the same generated name. Typically
@@ -391,9 +388,6 @@ export opaque type Request = {
388
// emit a different response to the stream instead.
389
onShellError: (error: mixed) => void,
390
onFatalError: (error: mixed) => void,
394
- // onPostpone is called when postpone() is called anywhere in the tree, which will defer
395
- // rendering - e.g. to the client. This is considered intentional and not an error.
396
- onPostpone: (reason: string, postponeInfo: ThrownInfo) => void,
391
// Form state that was the result of an MPA submission, if it was provided.
392
formState: null | ReactFormState<any, any>,
393
// DEV-only, warning dedupe
@@ -496,7 +490,6 @@ function RequestInstance(
490
onShellReady: void | (() => void),
491
onShellError: void | ((error: mixed) => void),
492
onFatalError: void | ((error: mixed) => void),
499
- onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void),
493
formState: void | null | ReactFormState<any, any>,
494
) {
495
const pingedTasks: Array<Task> = [];
@@ -525,7 +518,6 @@ function RequestInstance(
518
this.partialBoundaries = ([]: Array<SuspenseBoundary>);
519
this.trackedPostpones = null;
520
this.onError = onError === undefined ? defaultErrorHandler : onError;
528
- this.onPostpone = onPostpone === undefined ? noop : onPostpone;
521
this.onAllReady = onAllReady === undefined ? noop : onAllReady;
522
this.onShellReady = onShellReady === undefined ? noop : onShellReady;
523
this.onShellError = onShellError === undefined ? noop : onShellError;
@@ -547,7 +539,6 @@ export function createRequest(
539
onShellReady: void | (() => void),
540
onShellError: void | ((error: mixed) => void),
541
onFatalError: void | ((error: mixed) => void),
550
- onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void),
542
formState: void | null | ReactFormState<any, any>,
543
): Request {
544
if (__DEV__) {
@@ -565,7 +556,6 @@ export function createRequest(
556
onShellReady,
557
onShellError,
558
onFatalError,
568
- onPostpone,
559
formState,
560
);
561
@@ -616,7 +606,6 @@ export function createPrerenderRequest(
606
onShellReady: void | (() => void),
607
onShellError: void | ((error: mixed) => void),
608
onFatalError: void | ((error: mixed) => void),
619
- onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void),
609
): Request {
610
const request = createRequest(
611
children,
@@ -629,7 +618,6 @@ export function createPrerenderRequest(
618
onShellReady,
619
onShellError,
620
onFatalError,
632
- onPostpone,
621
undefined,
622
);
623
// Start tracking postponed holes during this render.
@@ -650,7 +638,6 @@ export function resumeRequest(
638
onShellReady: void | (() => void),
639
onShellError: void | ((error: mixed) => void),
640
onFatalError: void | ((error: mixed) => void),
653
- onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void),
641
): Request {
642
if (__DEV__) {
643
resetOwnerStackLimit();
@@ -667,7 +654,6 @@ export function resumeRequest(
654
onShellReady,
655
onShellError,
656
onFatalError,
670
- onPostpone,
657
null,
658
);
659
request.nextSegmentId = postponedState.nextSegmentId;
@@ -746,7 +732,6 @@ export function resumeAndPrerenderRequest(
732
onShellReady: void | (() => void),
733
onShellError: void | ((error: mixed) => void),
734
onFatalError: void | ((error: mixed) => void),
749
- onPostpone: void | ((reason: string, postponeInfo: PostponeInfo) => void),
735
): Request {
736
const request = resumeRequest(
737
children,
@@ -757,7 +742,6 @@ export function resumeAndPrerenderRequest(
742
onShellReady,
743
onShellError,
744
onFatalError,
760
- onPostpone,
745
);
746
// Start tracking postponed holes during this render.
747
request.trackedPostpones = {
@@ -1137,7 +1121,6 @@ type ThrownInfo = {
1121
componentStack?: string,
1122
};
1123
export type ErrorInfo = ThrownInfo;
1140
-export type PostponeInfo = ThrownInfo;
1124
1125
function getThrownInfo(node: null | ComponentStackNode): ThrownInfo {
1126
const errorInfo: ThrownInfo = {};
@@ -1191,22 +1174,6 @@ function encodeErrorForBoundary(
1174
}
1175
}
1176
1194
-function logPostpone(
1195
- request: Request,
1196
- reason: string,
1197
- postponeInfo: ThrownInfo,
1198
- debugTask: null | ConsoleTask,
1199
-): void {
1200
- // If this callback errors, we intentionally let that error bubble up to become a fatal error
1201
- // so that someone fixes the error reporting instead of hiding it.
1202
- const onPostpone = request.onPostpone;
1203
- if (__DEV__ && debugTask) {
1204
- debugTask.run(onPostpone.bind(null, reason, postponeInfo));
1205
- } else {
1206
- onPostpone(reason, postponeInfo);
1207
- }
1208
-}
1209
-
1177
function logRecoverableError(
1178
request: Request,
1179
error: any,
@@ -1525,30 +1492,12 @@ function renderSuspenseBoundary(
1492
}
1493
1494
const thrownInfo = getThrownInfo(task.componentStack);
1528
- let errorDigest;
1529
- if (
1530
- enablePostpone &&
1531
- typeof error === 'object' &&
1532
- error !== null &&
1533
- error.$$typeof === REACT_POSTPONE_TYPE
1534
- ) {
1535
- const postponeInstance: Postpone = (error: any);
1536
- logPostpone(
1537
- request,
1538
- postponeInstance.message,
1539
- thrownInfo,
1540
- __DEV__ ? task.debugTask : null,
1541
- );
1542
- // TODO: Figure out a better signal than a magic digest value.
1543
- errorDigest = 'POSTPONE';
1544
- } else {
1545
- errorDigest = logRecoverableError(
1546
- request,
1547
- error,
1548
- thrownInfo,
1549
- __DEV__ ? task.debugTask : null,
1550
- );
1551
- }
1495
+ const errorDigest = logRecoverableError(
1496
+ request,
1497
+ error,
1498
+ thrownInfo,
1499
+ __DEV__ ? task.debugTask : null,
1500
+ );
1501
encodeErrorForBoundary(
1502
newBoundary,
1503
errorDigest,
@@ -1696,30 +1645,12 @@ function replaySuspenseBoundary(
1645
} catch (error: mixed) {
1646
resumedBoundary.status = CLIENT_RENDERED;
1647
const thrownInfo = getThrownInfo(task.componentStack);
1699
- let errorDigest;
1700
- if (
1701
- enablePostpone &&
1702
- typeof error === 'object' &&
1703
- error !== null &&
1704
- error.$$typeof === REACT_POSTPONE_TYPE
1705
- ) {
1706
- const postponeInstance: Postpone = (error: any);
1707
- logPostpone(
1708
- request,
1709
- postponeInstance.message,
1710
- thrownInfo,
1711
- __DEV__ ? task.debugTask : null,
1712
- );
1713
- // TODO: Figure out a better signal than a magic digest value.
1714
- errorDigest = 'POSTPONE';
1715
- } else {
1716
- errorDigest = logRecoverableError(
1717
- request,
1718
- error,
1719
- thrownInfo,
1720
- __DEV__ ? task.debugTask : null,
1721
- );
1722
- }
1648
+ const errorDigest = logRecoverableError(
1649
+ request,
1650
+ error,
1651
+ thrownInfo,
1652
+ __DEV__ ? task.debugTask : null,
1653
+ );
1654
encodeErrorForBoundary(
1655
resumedBoundary,
1656
errorDigest,
@@ -4025,32 +3956,6 @@ function untrackBoundary(request: Request, boundary: SuspenseBoundary) {
3956
// we don't replay the path to it.
3957
}
3958
4028
-function injectPostponedHole(
4029
- request: Request,
4030
- task: RenderTask,
4031
- reason: string,
4032
- thrownInfo: ThrownInfo,
4033
-): Segment {
4034
- logPostpone(request, reason, thrownInfo, __DEV__ ? task.debugTask : null);
4035
- // Something suspended, we'll need to create a new segment and resolve it later.
4036
- const segment = task.blockedSegment;
4037
- const insertionIndex = segment.chunks.length;
4038
- const newSegment = createPendingSegment(
4039
- request,
4040
- insertionIndex,
4041
- null,
4042
- task.formatContext,
4043
- // Adopt the parent segment's leading text embed
4044
- segment.lastPushedText,
4045
- // Assume we are text embedded at the trailing edge
4046
- true,
4047
- );
4048
- segment.children.push(newSegment);
4049
- // Reset lastPushedText for current Segment since the new Segment "consumed" it
4050
- segment.lastPushedText = false;
4051
- return newSegment;
4052
-}
4053
-
3959
function spawnNewSuspendedReplayTask(
3960
request: Request,
3961
task: ReplayTask,
@@ -4297,45 +4202,6 @@ function renderNode(
4202
switchContext(previousContext);
4203
return;
4204
}
4300
- if (
4301
- enablePostpone &&
4302
- x.$$typeof === REACT_POSTPONE_TYPE &&
4303
- request.trackedPostpones !== null &&
4304
- task.blockedBoundary !== null // bubble if we're postponing in the shell
4305
- ) {
4306
- // If we're tracking postpones, we inject a hole here and continue rendering
4307
- // sibling. Similar to suspending. If we're not tracking, we treat it more like
4308
- // an error. Notably this doesn't spawn a new task since nothing will fill it
4309
- // in during this prerender.
4310
- const trackedPostpones = request.trackedPostpones;
4311
-
4312
- const postponeInstance: Postpone = (x: any);
4313
- const thrownInfo = getThrownInfo(task.componentStack);
4314
- const postponedSegment = injectPostponedHole(
4315
- request,
4316
- ((task: any): RenderTask), // We don't use ReplayTasks in prerenders.
4317
- postponeInstance.message,
4318
- thrownInfo,
4319
- );
4320
- trackPostpone(request, trackedPostpones, task, postponedSegment);
4321
-
4322
- // Restore the context. We assume that this will be restored by the inner
4323
- // functions in case nothing throws so we don't use "finally" here.
4324
- task.formatContext = previousFormatContext;
4325
- if (!disableLegacyContext) {
4326
- task.legacyContext = previousLegacyContext;
4327
- }
4328
- task.context = previousContext;
4329
- task.keyPath = previousKeyPath;
4330
- task.treeContext = previousTreeContext;
4331
- task.componentStack = previousComponentStack;
4332
- if (__DEV__) {
4333
- task.debugTask = previousDebugTask;
4334
- }
4335
- // Restore all active ReactContexts to what they were before.
4336
- switchContext(previousContext);
4337
- return;
4338
- }
4205
if (x.message === 'Maximum call stack size exceeded') {
4206
// This was a stack overflow. We do a lot of recursion in React by default for
4207
// performance but it can lead to stack overflows in extremely deep trees.
@@ -4411,20 +4277,7 @@ function erroredReplay(
4277
// that doesn't error the parent Suspense boundary.
4278
// This might be a bit strange that the error in a parent gets thrown at a child.
4279
// We log it only once and reuse the digest.
4414
- let errorDigest;
4415
- if (
4416
- enablePostpone &&
4417
- typeof error === 'object' &&
4418
- error !== null &&
4419
- error.$$typeof === REACT_POSTPONE_TYPE
4420
- ) {
4421
- const postponeInstance: Postpone = (error: any);
4422
- logPostpone(request, postponeInstance.message, errorInfo, debugTask);
4423
- // TODO: Figure out a better signal than a magic digest value.
4424
- errorDigest = 'POSTPONE';
4425
- } else {
4426
- errorDigest = logRecoverableError(request, error, errorInfo, debugTask);
4427
- }
4280
+ const errorDigest = logRecoverableError(request, error, errorInfo, debugTask);
4281
abortRemainingReplayNodes(
4282
request,
4283
boundary,
@@ -4454,23 +4307,10 @@ function erroredTask(
4307
request.allPendingTasks--;
4308
4309
// Report the error to a global handler.
4457
- let errorDigest;
4310
// We don't handle halts here because we only halt when prerendering and
4311
// when prerendering we should be finishing tasks not erroring them when
4312
// they halt or postpone
4461
- if (
4462
- enablePostpone &&
4463
- typeof error === 'object' &&
4464
- error !== null &&
4465
- error.$$typeof === REACT_POSTPONE_TYPE
4466
- ) {
4467
- const postponeInstance: Postpone = (error: any);
4468
- logPostpone(request, postponeInstance.message, errorInfo, debugTask);
4469
- // TODO: Figure out a better signal than a magic digest value.
4470
- errorDigest = 'POSTPONE';
4471
- } else {
4472
- errorDigest = logRecoverableError(request, error, errorInfo, debugTask);
4473
- }
4313
+ const errorDigest = logRecoverableError(request, error, errorInfo, debugTask);
4314
if (boundary === null) {
4315
fatalError(request, error, errorInfo, debugTask);
4316
} else {
@@ -4691,34 +4531,6 @@ function abortTask(task: Task, request: Request, error: mixed): void {
4531
// We didn't complete the root so we have nothing to show. We can close
4532
// the request;
4533
if (
4694
- enablePostpone &&
4695
- typeof error === 'object' &&
4696
- error !== null &&
4697
- error.$$typeof === REACT_POSTPONE_TYPE
4698
- ) {
4699
- const postponeInstance: Postpone = (error: any);
4700
- const trackedPostpones = request.trackedPostpones;
4701
-
4702
- if (trackedPostpones !== null && segment !== null) {
4703
- // We are prerendering. We don't want to fatal when the shell postpones
4704
- // we just need to mark it as postponed.
4705
- logPostpone(
4706
- request,
4707
- postponeInstance.message,
4708
- errorInfo,
4709
- task.debugTask,
4710
- );
4711
- trackPostpone(request, trackedPostpones, task, segment);
4712
- finishedTask(request, null, task.row, segment);
4713
- } else {
4714
- const fatal = new Error(
4715
- 'The render was aborted with postpone when the shell is incomplete. Reason: ' +
4716
- postponeInstance.message,
4717
- );
4718
- logRecoverableError(request, fatal, errorInfo, task.debugTask);
4719
- fatalError(request, fatal, errorInfo, task.debugTask);
4720
- }
4721
- } else if (
4534
enableHalt &&
4535
request.trackedPostpones !== null &&
4536
segment !== null
@@ -4740,25 +4552,12 @@ function abortTask(task: Task, request: Request, error: mixed): void {
4552
// the ReplaySet.
4553
replay.pendingTasks--;
4554
if (replay.pendingTasks === 0 && replay.nodes.length > 0) {
4743
- let errorDigest;
4744
- if (
4745
- enablePostpone &&
4746
- typeof error === 'object' &&
4747
- error !== null &&
4748
- error.$$typeof === REACT_POSTPONE_TYPE
4749
- ) {
4750
- const postponeInstance: Postpone = (error: any);
4751
- logPostpone(
4752
- request,
4753
- postponeInstance.message,
4754
- errorInfo,
4755
- task.debugTask,
4756
- );
4757
- // TODO: Figure out a better signal than a magic digest value.
4758
- errorDigest = 'POSTPONE';
4759
- } else {
4760
- errorDigest = logRecoverableError(request, error, errorInfo, null);
4761
- }
4555
+ const errorDigest = logRecoverableError(
4556
+ request,
4557
+ error,
4558
+ errorInfo,
4559
+ null,
4560
+ );
4561
abortRemainingReplayNodes(
4562
request,
4563
null,
@@ -4783,25 +4582,9 @@ function abortTask(task: Task, request: Request, error: mixed): void {
4582
if (boundary.status !== CLIENT_RENDERED) {
4583
if (enableHalt) {
4584
if (trackedPostpones !== null && segment !== null) {
4786
- // We are aborting a prerender
4787
- if (
4788
- enablePostpone &&
4789
- typeof error === 'object' &&
4790
- error !== null &&
4791
- error.$$typeof === REACT_POSTPONE_TYPE
4792
- ) {
4793
- const postponeInstance: Postpone = (error: any);
4794
- logPostpone(
4795
- request,
4796
- postponeInstance.message,
4797
- errorInfo,
4798
- task.debugTask,
4799
- );
4800
- } else {
4801
- // We are aborting a prerender and must halt this boundary.
4802
- // We treat this like other postpones during prerendering
4803
- logRecoverableError(request, error, errorInfo, task.debugTask);
4804
- }
4585
+ // We are aborting a prerender and must halt this boundary.
4586
+ // We treat this like other postpones during prerendering
4587
+ logRecoverableError(request, error, errorInfo, task.debugTask);
4588
trackPostpone(request, trackedPostpones, task, segment);
4589
// If this boundary was still pending then we haven't already cancelled its fallbacks.
4590
// We'll need to abort the fallbacks, which will also error that parent boundary.
@@ -4815,42 +4598,12 @@ function abortTask(task: Task, request: Request, error: mixed): void {
4598
boundary.status = CLIENT_RENDERED;
4599
// We are aborting a render or resume which should put boundaries
4600
// into an explicitly client rendered state
4818
- let errorDigest;
4819
- if (
4820
- enablePostpone &&
4821
- typeof error === 'object' &&
4822
- error !== null &&
4823
- error.$$typeof === REACT_POSTPONE_TYPE
4824
- ) {
4825
- const postponeInstance: Postpone = (error: any);
4826
- logPostpone(
4827
- request,
4828
- postponeInstance.message,
4829
- errorInfo,
4830
- task.debugTask,
4831
- );
4832
- if (request.trackedPostpones !== null && segment !== null) {
4833
- trackPostpone(request, request.trackedPostpones, task, segment);
4834
- finishedTask(request, task.blockedBoundary, task.row, segment);
4835
-
4836
- // If this boundary was still pending then we haven't already cancelled its fallbacks.
4837
- // We'll need to abort the fallbacks, which will also error that parent boundary.
4838
- boundary.fallbackAbortableTasks.forEach(fallbackTask =>
4839
- abortTask(fallbackTask, request, error),
4840
- );
4841
- boundary.fallbackAbortableTasks.clear();
4842
- return;
4843
- }
4844
- // TODO: Figure out a better signal than a magic digest value.
4845
- errorDigest = 'POSTPONE';
4846
- } else {
4847
- errorDigest = logRecoverableError(
4848
- request,
4849
- error,
4850
- errorInfo,
4851
- task.debugTask,
4852
- );
4853
- }
4601
+ const errorDigest = logRecoverableError(
4602
+ request,
4603
+ error,
4604
+ errorInfo,
4605
+ task.debugTask,
4606
+ );
4607
boundary.status = CLIENT_RENDERED;
4608
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo, true);
4609
@@ -5254,27 +5007,12 @@ function retryRenderTask(
5007
const thrownInfo = getThrownInfo(task.componentStack);
5008
task.abortSet.delete(task);
5009
5257
- if (
5258
- enablePostpone &&
5259
- typeof x === 'object' &&
5260
- x !== null &&
5261
- x.$$typeof === REACT_POSTPONE_TYPE
5262
- ) {
5263
- const postponeInstance: Postpone = (x: any);
5264
- logPostpone(
5265
- request,
5266
- postponeInstance.message,
5267
- thrownInfo,
5268
- __DEV__ ? task.debugTask : null,
5269
- );
5270
- } else {
5271
- logRecoverableError(
5272
- request,
5273
- x,
5274
- thrownInfo,
5275
- __DEV__ ? task.debugTask : null,
5276
- );
5277
- }
5010
+ logRecoverableError(
5011
+ request,
5012
+ x,
5013
+ thrownInfo,
5014
+ __DEV__ ? task.debugTask : null,
5015
+ );
5016
5017
trackPostpone(request, trackedPostpones, task, segment);
5018
finishedTask(request, task.blockedBoundary, task.row, segment);
@@ -5294,28 +5032,6 @@ function retryRenderTask(
5032
// We've asserted that x is a thenable above
5033
(x: any).then(ping, ping);
5034
return;
5297
- } else if (
5298
- enablePostpone &&
5299
- request.trackedPostpones !== null &&
5300
- x.$$typeof === REACT_POSTPONE_TYPE
5301
- ) {
5302
- // If we're tracking postpones, we mark this segment as postponed and finish
5303
- // the task without filling it in. If we're not tracking, we treat it more like
5304
- // an error.
5305
- const trackedPostpones = request.trackedPostpones;
5306
- task.abortSet.delete(task);
5307
- const postponeInstance: Postpone = (x: any);
5308
-
5309
- const postponeInfo = getThrownInfo(task.componentStack);
5310
- logPostpone(
5311
- request,
5312
- postponeInstance.message,
5313
- postponeInfo,
5314
- __DEV__ ? task.debugTask : null,
5315
- );
5316
- trackPostpone(request, trackedPostpones, task, segment);
5317
- finishedTask(request, task.blockedBoundary, task.row, segment);
5318
- return;
5035
}
5036
}
5037
@@ -6166,9 +5882,7 @@ function flushCompletedQueues(
5882
request.flushScheduled = false;
5883
// We write the trailing tags but only if don't have any data to resume.
5884
// If we need to resume we'll write the postamble in the resume instead.
6169
- if (!enablePostpone || request.trackedPostpones === null) {
6170
- writePostamble(destination, request.resumableState);
6171
- }
5885
+ writePostamble(destination, request.resumableState);
5886
completeWriting(destination);
5887
flushBuffered(destination);
5888
if (__DEV__) {
packages/react-server/src/ReactFlightServer.js
+18
-133
@@ -9,12 +9,9 @@
9
10
import type {Chunk, BinaryChunk, Destination} from './ReactServerStreamConfig';
11
12
-import type {Postpone} from 'react/src/ReactPostpone';
13
-
12
import type {TemporaryReferenceSet} from './ReactFlightServerTemporaryReferences';
13
14
import {
17
- enablePostpone,
15
enableHalt,
16
enableTaint,
17
enableProfilerTimer,
@@ -138,7 +135,6 @@ import {
135
REACT_FRAGMENT_TYPE,
136
REACT_LAZY_TYPE,
137
REACT_MEMO_TYPE,
141
- REACT_POSTPONE_TYPE,
138
ASYNC_ITERATOR,
139
} from 'shared/ReactSymbols';
140
@@ -593,7 +589,6 @@ export type Request = {
589
identifierCount: number,
590
taintCleanupQueue: Array<string | bigint>,
591
onError: (error: mixed) => ?string,
596
- onPostpone: (reason: string) => void,
592
onAllReady: () => void,
593
onFatalError: mixed => void,
594
// Profiling-only
@@ -649,15 +644,12 @@ function defaultErrorHandler(error: mixed) {
644
// Don't transform to our wrapper
645
}
646
652
-const defaultPostponeHandler: (reason: string) => void = noop;
653
-
647
function RequestInstance(
648
this: $FlowFixMe,
649
type: 20 | 21,
650
model: ReactClientValue,
651
bundlerConfig: ClientManifest,
652
onError: void | ((error: mixed) => ?string),
660
- onPostpone: void | ((reason: string) => void),
653
onAllReady: () => void,
654
onFatalError: (error: mixed) => void,
655
identifierPrefix?: string,
@@ -715,8 +707,6 @@ function RequestInstance(
707
this.identifierCount = 1;
708
this.taintCleanupQueue = cleanupQueue;
709
this.onError = onError === undefined ? defaultErrorHandler : onError;
718
- this.onPostpone =
719
- onPostpone === undefined ? defaultPostponeHandler : onPostpone;
710
this.onAllReady = onAllReady;
711
this.onFatalError = onFatalError;
712
@@ -787,7 +777,6 @@ export function createRequest(
777
bundlerConfig: ClientManifest,
778
onError: void | ((error: mixed) => ?string),
779
identifierPrefix: void | string,
790
- onPostpone: void | ((reason: string) => void),
780
temporaryReferences: void | TemporaryReferenceSet,
781
environmentName: void | string | (() => string), // DEV-only
782
filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only
@@ -803,7 +792,6 @@ export function createRequest(
792
model,
793
bundlerConfig,
794
onError,
806
- onPostpone,
795
noop,
796
noop,
797
identifierPrefix,
@@ -821,7 +809,6 @@ export function createPrerenderRequest(
809
onFatalError: () => void,
810
onError: void | ((error: mixed) => ?string),
811
identifierPrefix: void | string,
824
- onPostpone: void | ((reason: string) => void),
812
temporaryReferences: void | TemporaryReferenceSet,
813
environmentName: void | string | (() => string), // DEV-only
814
filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only
@@ -837,7 +824,6 @@ export function createPrerenderRequest(
824
model,
825
bundlerConfig,
826
onError,
840
- onPostpone,
827
onAllReady,
828
onFatalError,
829
identifierPrefix,
@@ -3392,28 +3378,15 @@ function renderModel(
3378
// Something errored. We'll still send everything we have up until this point.
3379
request.pendingChunks++;
3380
const errorId = request.nextChunkId++;
3395
- if (
3396
- enablePostpone &&
3397
- typeof x === 'object' &&
3398
- x !== null &&
3399
- x.$$typeof === REACT_POSTPONE_TYPE
3400
- ) {
3401
- // Something postponed. We'll still send everything we have up until this point.
3402
- // We'll replace this element with a lazy reference that postpones on the client.
3403
- const postponeInstance: Postpone = (x: any);
3404
- logPostpone(request, postponeInstance.message, task);
3405
- emitPostponeChunk(request, errorId, postponeInstance);
3406
- } else {
3407
- const digest = logRecoverableError(request, x, task);
3408
- emitErrorChunk(
3409
- request,
3410
- errorId,
3411
- digest,
3412
- x,
3413
- false,
3414
- __DEV__ ? task.debugOwner : null,
3415
- );
3416
- }
3381
+ const digest = logRecoverableError(request, x, task);
3382
+ emitErrorChunk(
3383
+ request,
3384
+ errorId,
3385
+ digest,
3386
+ x,
3387
+ false,
3388
+ __DEV__ ? task.debugOwner : null,
3389
+ );
3390
if (wasReactNode) {
3391
// We'll replace this element with a lazy reference that throws on the client
3392
// once it gets rendered.
@@ -4030,41 +4003,6 @@ function renderModelDestructive(
4003
);
4004
}
4005
4033
-function logPostpone(
4034
- request: Request,
4035
- reason: string,
4036
- task: Task | null, // DEV-only
4037
-): void {
4038
- const prevRequest = currentRequest;
4039
- // We clear the request context so that console.logs inside the callback doesn't
4040
- // get forwarded to the client.
4041
- currentRequest = null;
4042
- try {
4043
- const onPostpone = request.onPostpone;
4044
- if (__DEV__ && task !== null) {
4045
- if (supportsRequestStorage) {
4046
- requestStorage.run(
4047
- undefined,
4048
- callWithDebugContextInDEV,
4049
- request,
4050
- task,
4051
- onPostpone,
4052
- reason,
4053
- );
4054
- } else {
4055
- callWithDebugContextInDEV(request, task, onPostpone, reason);
4056
- }
4057
- } else if (supportsRequestStorage) {
4058
- // Exit the request context while running callbacks.
4059
- requestStorage.run(undefined, onPostpone, reason);
4060
- } else {
4061
- onPostpone(reason);
4062
- }
4063
- } finally {
4064
- currentRequest = prevRequest;
4065
- }
4066
-}
4067
-
4006
function logRecoverableError(
4007
request: Request,
4008
error: mixed,
@@ -4131,32 +4069,6 @@ function fatalError(request: Request, error: mixed): void {
4069
request.cacheController.abort(abortReason);
4070
}
4071
4134
-function emitPostponeChunk(
4135
- request: Request,
4136
- id: number,
4137
- postponeInstance: Postpone,
4138
-): void {
4139
- let row;
4140
- if (__DEV__) {
4141
- let reason = '';
4142
- let stack: ReactStackTrace;
4143
- const env = request.environmentName();
4144
- try {
4145
- // eslint-disable-next-line react-internal/safe-string-coercion
4146
- reason = String(postponeInstance.message);
4147
- stack = filterStackTrace(request, parseStackTrace(postponeInstance, 0));
4148
- } catch (x) {
4149
- stack = [];
4150
- }
4151
- row = serializeRowHeader('P', id) + stringify({reason, stack, env}) + '\n';
4152
- } else {
4153
- // No reason included in prod.
4154
- row = serializeRowHeader('P', id) + '\n';
4155
- }
4156
- const processedChunk = stringToChunk(row);
4157
- request.completedErrorChunks.push(processedChunk);
4158
-}
4159
-
4072
function serializeErrorValue(request: Request, error: Error): string {
4073
if (__DEV__) {
4074
let name: string = 'Error';
@@ -5716,26 +5628,15 @@ function erroredTask(request: Request, task: Task, error: mixed): void {
5628
}
5629
}
5630
task.status = ERRORED;
5719
- if (
5720
- enablePostpone &&
5721
- typeof error === 'object' &&
5722
- error !== null &&
5723
- error.$$typeof === REACT_POSTPONE_TYPE
5724
- ) {
5725
- const postponeInstance: Postpone = (error: any);
5726
- logPostpone(request, postponeInstance.message, task);
5727
- emitPostponeChunk(request, task.id, postponeInstance);
5728
- } else {
5729
- const digest = logRecoverableError(request, error, task);
5730
- emitErrorChunk(
5731
- request,
5732
- task.id,
5733
- digest,
5734
- error,
5735
- false,
5736
- __DEV__ ? task.debugOwner : null,
5737
- );
5738
- }
5631
+ const digest = logRecoverableError(request, error, task);
5632
+ emitErrorChunk(
5633
+ request,
5634
+ task.id,
5635
+ digest,
5636
+ error,
5637
+ false,
5638
+ __DEV__ ? task.debugOwner : null,
5639
+ );
5640
request.abortableTasks.delete(task);
5641
callOnAllReadyIfReady(request);
5642
}
@@ -6274,22 +6175,6 @@ export function abort(request: Request, reason: mixed): void {
6175
// and leave the reference unfulfilled.
6176
abortableTasks.forEach(task => haltTask(task, request));
6177
scheduleWork(() => finishHalt(request, abortableTasks));
6277
- } else if (
6278
- enablePostpone &&
6279
- typeof reason === 'object' &&
6280
- reason !== null &&
6281
- (reason: any).$$typeof === REACT_POSTPONE_TYPE
6282
- ) {
6283
- const postponeInstance: Postpone = (reason: any);
6284
- logPostpone(request, postponeInstance.message, null);
6285
- // When rendering we produce a shared postpone chunk and then
6286
- // fulfill each task with a reference to that chunk.
6287
- const errorId = request.nextChunkId++;
6288
- request.fatalError = errorId;
6289
- request.pendingChunks++;
6290
- emitPostponeChunk(request, errorId, postponeInstance);
6291
- abortableTasks.forEach(task => abortTask(task, request, errorId));
6292
- scheduleWork(() => finishAbort(request, abortableTasks, errorId));
6178
} else {
6179
const error =
6180
reason === undefined
packages/react/src/ReactPostpone.js
deleted
-23
@@ -1,23 +0,0 @@
1
-/**
2
- * Copyright (c) Meta Platforms, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import {REACT_POSTPONE_TYPE} from 'shared/ReactSymbols';
11
-
12
-declare class Postpone extends Error {
13
- $$typeof: symbol;
14
-}
15
-
16
-export type {Postpone};
17
-
18
-export function postpone(reason: string): void {
19
- // eslint-disable-next-line react-internal/prod-error-codes
20
- const postponeInstance: Postpone = (new Error(reason): any);
21
- postponeInstance.$$typeof = REACT_POSTPONE_TYPE;
22
- throw postponeInstance;
23
-}
packages/shared/ReactFeatureFlags.js
-2
@@ -80,8 +80,6 @@ export const enableAsyncIterableChildren = __EXPERIMENTAL__;
80
81
export const enableTaint = __EXPERIMENTAL__;
82
83
-export const enablePostpone: boolean = false; // Probably won't ship in this form.
84
-
83
export const enableHalt: boolean = true;
84
85
export const enableViewTransition: boolean = true;
packages/shared/ReactSymbols.js
-2
@@ -44,8 +44,6 @@ export const REACT_MEMO_CACHE_SENTINEL: symbol = Symbol.for(
44
'react.memo_cache_sentinel',
45
);
46
47
-export const REACT_POSTPONE_TYPE: symbol = Symbol.for('react.postpone');
48
-
47
export const REACT_VIEW_TRANSITION_TYPE: symbol = Symbol.for(
48
'react.view_transition',
49
);
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -51,7 +51,6 @@ export const enableLegacyCache: boolean = false;
51
export const enableLegacyFBSupport: boolean = false;
52
export const enableLegacyHidden: boolean = false;
53
export const enableNoCloningMemoCache: boolean = false;
54
-export const enablePostpone: boolean = false;
54
export const enableProfilerCommitHooks: boolean = __PROFILE__;
55
export const enableProfilerNestedUpdatePhase: boolean = __PROFILE__;
56
export const enableProfilerTimer: boolean = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -38,7 +38,6 @@ export const enableLegacyFBSupport: boolean = false;
38
export const enableLegacyHidden: boolean = false;
39
export const enableNoCloningMemoCache: boolean = false;
40
export const enableObjectFiber: boolean = false;
41
-export const enablePostpone: boolean = false;
41
export const enableReactTestRendererWarning: boolean = false;
42
export const enableRetryLaneExpiration: boolean = false;
43
export const enableComponentPerformanceTrack: boolean = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -21,7 +21,6 @@ export const enableUpdaterTracking: boolean = false;
21
export const enableLegacyCache: boolean = __EXPERIMENTAL__;
22
export const enableAsyncIterableChildren: boolean = false;
23
export const enableTaint: boolean = true;
24
-export const enablePostpone: boolean = false;
24
export const enableHalt: boolean = true;
25
export const disableCommentsAsDOMContainers: boolean = true;
26
export const disableInputAttributeSyncing: boolean = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
-1
@@ -33,7 +33,6 @@ export const enableLegacyFBSupport = false;
33
export const enableLegacyHidden = false;
34
export const enableNoCloningMemoCache = false;
35
export const enableObjectFiber = false;
36
-export const enablePostpone = false;
36
export const enableProfilerCommitHooks = __PROFILE__;
37
export const enableProfilerNestedUpdatePhase = __PROFILE__;
38
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -21,7 +21,6 @@ export const enableUpdaterTracking: boolean = false;
21
export const enableLegacyCache: boolean = true;
22
export const enableAsyncIterableChildren: boolean = false;
23
export const enableTaint: boolean = true;
24
-export const enablePostpone: boolean = false;
24
export const enableHalt: boolean = true;
25
export const disableCommentsAsDOMContainers: boolean = true;
26
export const disableInputAttributeSyncing: boolean = false;
packages/shared/forks/ReactFeatureFlags.www.js
-2
@@ -73,8 +73,6 @@ export const enableAsyncIterableChildren: boolean = false;
73
74
export const enableTaint: boolean = false;
75
76
-export const enablePostpone: boolean = false;
77
-
76
export const enableHalt: boolean = true;
77
78
// TODO: www currently relies on this feature. It's disabled in open source.