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