main
js 258 lines 6.58 KB
Raw
1 import * as React from 'react';
2 import {renderToReadableStream} from 'react-server-dom-unbundled/server';
3 import {createFromReadableStream} from 'react-server-dom-webpack/client';
4 import {PassThrough, Readable} from 'stream';
5 import {ClientContext, ClientReadContext} from './ClientContext.js';
6 import Container from './Container.js';
7
8 import {Counter} from './Counter.js';
9 import {Counter as Counter2} from './Counter2.js';
10 import AsyncModule from './cjs/Counter3.js';
11 const Counter3 = await(AsyncModule);
12
13 import ShowMore from './ShowMore.js';
14 import Button from './Button.js';
15 import Form from './Form.js';
16 import {Dynamic} from './Dynamic.js';
17 import {Client} from './Client.js';
18 import {Navigate} from './Navigate.js';
19
20 import {Note} from './cjs/Note.js';
21
22 import {GenerateImage} from './GenerateImage.js';
23
24 import LargeContent from './LargeContent.js';
25
26 import {like, greet, increment} from './actions.js';
27
28 import {getServerState} from './ServerState.js';
29 import {sdkMethod} from './library.js';
30 import FileReader from './FileReader.js';
31
32 const promisedText = new Promise(resolve =>
33 setTimeout(() => resolve('deferred text'), 50)
34 );
35
36 function Foo({children}) {
37 return <div>{children}</div>;
38 }
39
40 async function delayedError(text, ms) {
41 return new Promise((_, reject) =>
42 setTimeout(() => reject(new Error(text)), ms)
43 );
44 }
45
46 async function delay(text, ms) {
47 return new Promise(resolve => setTimeout(() => resolve(text), ms));
48 }
49
50 async function delayTwice() {
51 try {
52 await delayedError('Delayed exception', 20);
53 } catch (x) {
54 // Ignored
55 }
56 await delay('', 10);
57 }
58
59 async function delayTrice() {
60 const p = delayTwice();
61 await delay('', 40);
62 return p;
63 }
64
65 async function Bar({children}) {
66 await delayTrice();
67 return <div>{children}</div>;
68 }
69
70 async function ThirdPartyComponent() {
71 return await delay('hello from a 3rd party', 30);
72 }
73
74 let cachedThirdPartyStream;
75
76 // We create the Component outside of AsyncLocalStorage so that it has no owner.
77 // That way it gets the owner from the call to createFromNodeStream.
78 const thirdPartyComponent = <ThirdPartyComponent />;
79
80 function simulateFetch(cb, latencyMs) {
81 return new Promise(resolve => {
82 // Request latency
83 setTimeout(() => {
84 const result = cb();
85 // Response latency
86 setTimeout(() => {
87 resolve(result);
88 }, latencyMs);
89 }, latencyMs);
90 });
91 }
92
93 async function fetchThirdParty(noCache) {
94 // We're using the Web Streams APIs for tee'ing convenience.
95 let stream;
96 if (cachedThirdPartyStream && !noCache) {
97 stream = cachedThirdPartyStream;
98 } else {
99 stream = await simulateFetch(
100 () =>
101 renderToReadableStream(
102 thirdPartyComponent,
103 {},
104 {environmentName: 'third-party'}
105 ),
106 25
107 );
108 }
109
110 const [stream1, stream2] = stream.tee();
111 cachedThirdPartyStream = stream1;
112
113 return createFromReadableStream(stream2, {
114 serverConsumerManifest: {
115 moduleMap: {},
116 serverModuleMap: null,
117 moduleLoading: null,
118 },
119 });
120 }
121
122 async function ServerComponent({noCache}) {
123 await delay('deferred text', 50);
124 return await fetchThirdParty(noCache);
125 }
126
127 let veryDeepObject = [
128 {
129 bar: {
130 baz: {
131 a: {},
132 },
133 },
134 },
135 {
136 bar: {
137 baz: {
138 a: {},
139 },
140 },
141 },
142 {
143 bar: {
144 baz: {
145 a: {},
146 },
147 },
148 },
149 {
150 bar: {
151 baz: {
152 a: {
153 b: {
154 c: {
155 d: {
156 e: {
157 f: {
158 g: {
159 h: {
160 i: {
161 j: {
162 k: {
163 l: {
164 m: {
165 yay: 'You reached the end',
166 },
167 },
168 },
169 },
170 },
171 },
172 },
173 },
174 },
175 },
176 },
177 },
178 },
179 },
180 },
181 },
182 ];
183
184 export default async function App({prerender, noCache}) {
185 const res = await fetch('http://localhost:3001/todos');
186 const todos = await res.json();
187 await sdkMethod('http://localhost:3001/todos');
188
189 console.log('Expand me:', veryDeepObject);
190
191 const dedupedChild = <ServerComponent noCache={noCache} />;
192 const message = getServerState();
193 return (
194 <html lang="en">
195 <head>
196 <meta charSet="utf-8" />
197 <meta name="viewport" content="width=device-width, initial-scale=1" />
198 <title>Flight</title>
199 </head>
200 <body>
201 <Container>
202 {prerender ? (
203 <meta data-testid="prerendered" name="prerendered" content="true" />
204 ) : (
205 <meta content="when not prerendering we render this meta tag. When prerendering you will expect to see this tag and the one with data-testid=prerendered because we SSR one and hydrate the other" />
206 )}
207 <h1>{message}</h1>
208 <React.Suspense fallback={null}>
209 <div data-testid="promise-as-a-child-test">
210 Promise as a child hydrates without errors: {promisedText}
211 </div>
212 </React.Suspense>
213 <Counter incrementAction={increment} />
214 <Counter2 incrementAction={increment} />
215 <Counter3 incrementAction={increment} />
216 <ul>
217 {todos.map(todo => (
218 <li key={todo.id}>{todo.text}</li>
219 ))}
220 </ul>
221 <ShowMore>
222 <p>Lorem ipsum</p>
223 </ShowMore>
224 <Form action={greet} />
225 <div>
226 <Button action={like}>Like</Button>
227 </div>
228 <div>
229 loaded statically: <Dynamic />
230 </div>
231 <div>
232 <GenerateImage message={message} />
233 </div>
234 <Client />
235 <Note />
236 <Foo>{dedupedChild}</Foo>
237 <Bar>{Promise.resolve([dedupedChild])}</Bar>
238 <Navigate />
239 <ClientContext value="from server">
240 <div>
241 <ClientReadContext />
242 </div>
243 </ClientContext>
244 {prerender ? null : ( // TODO: prerender is broken for large content for some reason.
245 <React.Suspense fallback={null}>
246 <LargeContent />
247 {/*
248 This text prop is above the threshold, so in the debug info for
249 the element we'll see a placeholder instead of the actual value.
250 */}
251 <FileReader largeText={'a'.repeat(1000001)} />
252 </React.Suspense>
253 )}
254 </Container>
255 </body>
256 </html>
257 );
258 }