@samitouri / QOS-React-1 / commits / dd048c3b2d

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.