main
js 342 lines 9.8 KB
Raw
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 * @flow
8 */
9
10 import type {
11 RenderState as BaseRenderState,
12 ResumableState,
13 HoistableState,
14 StyleQueue,
15 Resource,
16 HeadersDescriptor,
17 PreambleState,
18 FormatContext,
19 } from './ReactFizzConfigDOM';
20
21 import {
22 createRenderState as createRenderStateImpl,
23 pushTextInstance as pushTextInstanceImpl,
24 pushSegmentFinale as pushSegmentFinaleImpl,
25 pushStartActivityBoundary as pushStartActivityBoundaryImpl,
26 pushEndActivityBoundary as pushEndActivityBoundaryImpl,
27 writeStartCompletedSuspenseBoundary as writeStartCompletedSuspenseBoundaryImpl,
28 writeStartClientRenderedSuspenseBoundary as writeStartClientRenderedSuspenseBoundaryImpl,
29 writeEndCompletedSuspenseBoundary as writeEndCompletedSuspenseBoundaryImpl,
30 writeEndClientRenderedSuspenseBoundary as writeEndClientRenderedSuspenseBoundaryImpl,
31 writePreambleStart as writePreambleStartImpl,
32 } from './ReactFizzConfigDOM';
33
34 import type {
35 Destination,
36 Chunk,
37 PrecomputedChunk,
38 } from 'react-server/src/ReactServerStreamConfig';
39
40 import type {FormStatus} from '../shared/ReactDOMFormActions';
41
42 import {NotPending} from '../shared/ReactDOMFormActions';
43
44 export const isPrimaryRenderer = false;
45
46 export type RenderState = {
47 // Keep this in sync with ReactFizzConfigDOM
48 placeholderPrefix: PrecomputedChunk,
49 segmentPrefix: PrecomputedChunk,
50 boundaryPrefix: PrecomputedChunk,
51 startInlineScript: PrecomputedChunk,
52 startInlineStyle: PrecomputedChunk,
53 preamble: PreambleState,
54 externalRuntimeScript: null | any,
55 bootstrapChunks: Array<Chunk | PrecomputedChunk>,
56 importMapChunks: Array<Chunk | PrecomputedChunk>,
57 onHeaders: void | ((headers: HeadersDescriptor) => void),
58 headers: null | {
59 preconnects: string,
60 fontPreloads: string,
61 highImagePreloads: string,
62 remainingCapacity: number,
63 },
64 resets: BaseRenderState['resets'],
65 charsetChunks: Array<Chunk | PrecomputedChunk>,
66 viewportChunks: Array<Chunk | PrecomputedChunk>,
67 hoistableChunks: Array<Chunk | PrecomputedChunk>,
68 preconnects: Set<Resource>,
69 fontPreloads: Set<Resource>,
70 highImagePreloads: Set<Resource>,
71 // usedImagePreloads: Set<Resource>,
72 styles: Map<string, StyleQueue>,
73 bootstrapScripts: Set<Resource>,
74 scripts: Set<Resource>,
75 bulkPreloads: Set<Resource>,
76 preloads: {
77 images: Map<string, Resource>,
78 stylesheets: Map<string, Resource>,
79 scripts: Map<string, Resource>,
80 moduleScripts: Map<string, Resource>,
81 },
82 nonce: {
83 script: string | void,
84 style: string | void,
85 },
86 stylesToHoist: boolean,
87 // This is an extra field for the legacy renderer
88 generateStaticMarkup: boolean,
89 };
90
91 export function createRenderState(
92 resumableState: ResumableState,
93 generateStaticMarkup: boolean,
94 ): RenderState {
95 const renderState = createRenderStateImpl(
96 resumableState,
97 undefined,
98 undefined,
99 undefined,
100 undefined,
101 undefined,
102 );
103 return {
104 // Keep this in sync with ReactFizzConfigDOM
105 placeholderPrefix: renderState.placeholderPrefix,
106 segmentPrefix: renderState.segmentPrefix,
107 boundaryPrefix: renderState.boundaryPrefix,
108 startInlineScript: renderState.startInlineScript,
109 startInlineStyle: renderState.startInlineStyle,
110 preamble: renderState.preamble,
111 externalRuntimeScript: renderState.externalRuntimeScript,
112 bootstrapChunks: renderState.bootstrapChunks,
113 importMapChunks: renderState.importMapChunks,
114 onHeaders: renderState.onHeaders,
115 headers: renderState.headers,
116 resets: renderState.resets,
117 charsetChunks: renderState.charsetChunks,
118 viewportChunks: renderState.viewportChunks,
119 hoistableChunks: renderState.hoistableChunks,
120 preconnects: renderState.preconnects,
121 fontPreloads: renderState.fontPreloads,
122 highImagePreloads: renderState.highImagePreloads,
123 // usedImagePreloads: renderState.usedImagePreloads,
124 styles: renderState.styles,
125 bootstrapScripts: renderState.bootstrapScripts,
126 scripts: renderState.scripts,
127 bulkPreloads: renderState.bulkPreloads,
128 preloads: renderState.preloads,
129 nonce: renderState.nonce,
130 stylesToHoist: renderState.stylesToHoist,
131
132 // This is an extra field for the legacy renderer
133 generateStaticMarkup,
134 };
135 }
136
137 import {
138 stringToChunk,
139 stringToPrecomputedChunk,
140 } from 'react-server/src/ReactServerStreamConfig';
141
142 // this chunk is empty on purpose because we do not want to emit the DOCTYPE in legacy mode
143 export const doctypeChunk: PrecomputedChunk = stringToPrecomputedChunk('');
144
145 export type {
146 ResumableState,
147 HoistableState,
148 PreambleState,
149 FormatContext,
150 } from './ReactFizzConfigDOM';
151
152 export {
153 getChildFormatContext,
154 getSuspenseFallbackFormatContext,
155 getSuspenseContentFormatContext,
156 makeId,
157 pushStartInstance,
158 pushEndInstance,
159 pushFormStateMarkerIsMatching,
160 pushFormStateMarkerIsNotMatching,
161 writeStartSegment,
162 writeEndSegment,
163 writeCompletedSegmentInstruction,
164 writeCompletedBoundaryInstruction,
165 writeClientRenderBoundaryInstruction,
166 writeStartPendingSuspenseBoundary,
167 writeEndPendingSuspenseBoundary,
168 writeHoistablesForBoundary,
169 writePlaceholder,
170 writeCompletedRoot,
171 createRootFormatContext,
172 createResumableState,
173 createPreambleState,
174 createHoistableState,
175 writePreambleEnd,
176 writeHoistables,
177 writePostamble,
178 hoistHoistables,
179 resetResumableState,
180 completeResumableState,
181 emitEarlyPreloads,
182 supportsClientAPIs,
183 hoistPreambleState,
184 isPreambleReady,
185 isPreambleContext,
186 } from './ReactFizzConfigDOM';
187
188 import escapeTextForBrowser from './escapeTextForBrowser';
189
190 export function getViewTransitionFormatContext(
191 resumableState: ResumableState,
192 parentContext: FormatContext,
193 update: void | null | 'none' | 'auto' | string,
194 enter: void | null | 'none' | 'auto' | string,
195 exit: void | null | 'none' | 'auto' | string,
196 share: void | null | 'none' | 'auto' | string,
197 parentEnter: void | null | 'none' | 'auto' | string,
198 parentExit: void | null | 'none' | 'auto' | string,
199 hasParentEnterHandler: boolean,
200 hasParentExitHandler: boolean,
201 name: void | null | 'auto' | string,
202 autoName: string, // name or an autogenerated unique name
203 ): FormatContext {
204 // ViewTransition reveals are not supported in legacy renders.
205 return parentContext;
206 }
207
208 export function canHavePreamble(formatContext: FormatContext): boolean {
209 return false;
210 }
211
212 export function pushTextInstance(
213 target: Array<Chunk | PrecomputedChunk>,
214 text: string,
215 renderState: RenderState,
216 textEmbedded: boolean,
217 ): boolean {
218 if (renderState.generateStaticMarkup) {
219 target.push(stringToChunk(escapeTextForBrowser(text)));
220 return false;
221 } else {
222 return pushTextInstanceImpl(target, text, renderState, textEmbedded);
223 }
224 }
225
226 export function pushSegmentFinale(
227 target: Array<Chunk | PrecomputedChunk>,
228 renderState: RenderState,
229 lastPushedText: boolean,
230 textEmbedded: boolean,
231 ): void {
232 if (renderState.generateStaticMarkup) {
233 return;
234 } else {
235 return pushSegmentFinaleImpl(
236 target,
237 renderState,
238 lastPushedText,
239 textEmbedded,
240 );
241 }
242 }
243
244 export function pushStartActivityBoundary(
245 target: Array<Chunk | PrecomputedChunk>,
246 renderState: RenderState,
247 ): void {
248 if (renderState.generateStaticMarkup) {
249 // A completed boundary is done and doesn't need a representation in the HTML
250 // if we're not going to be hydrating it.
251 return;
252 }
253 pushStartActivityBoundaryImpl(target, renderState);
254 }
255
256 export function pushEndActivityBoundary(
257 target: Array<Chunk | PrecomputedChunk>,
258 renderState: RenderState,
259 ): void {
260 if (renderState.generateStaticMarkup) {
261 return;
262 }
263 pushEndActivityBoundaryImpl(target, renderState);
264 }
265
266 export function writeStartCompletedSuspenseBoundary(
267 destination: Destination,
268 renderState: RenderState,
269 ): boolean {
270 if (renderState.generateStaticMarkup) {
271 // A completed boundary is done and doesn't need a representation in the HTML
272 // if we're not going to be hydrating it.
273 return true;
274 }
275 return writeStartCompletedSuspenseBoundaryImpl(destination, renderState);
276 }
277 export function writeStartClientRenderedSuspenseBoundary(
278 destination: Destination,
279 renderState: RenderState,
280 // flushing these error arguments are not currently supported in this legacy streaming format.
281 errorDigest: ?string,
282 errorMessage: ?string,
283 errorStack: ?string,
284 errorComponentStack: ?string,
285 ): boolean {
286 if (renderState.generateStaticMarkup) {
287 // A client rendered boundary is done and doesn't need a representation in the HTML
288 // since we'll never hydrate it. This is arguably an error in static generation.
289 return true;
290 }
291 return writeStartClientRenderedSuspenseBoundaryImpl(
292 destination,
293 renderState,
294 errorDigest,
295 errorMessage,
296 errorStack,
297 errorComponentStack,
298 );
299 }
300 export function writeEndCompletedSuspenseBoundary(
301 destination: Destination,
302 renderState: RenderState,
303 ): boolean {
304 if (renderState.generateStaticMarkup) {
305 return true;
306 }
307 return writeEndCompletedSuspenseBoundaryImpl(destination, renderState);
308 }
309 export function writeEndClientRenderedSuspenseBoundary(
310 destination: Destination,
311 renderState: RenderState,
312 ): boolean {
313 if (renderState.generateStaticMarkup) {
314 return true;
315 }
316 return writeEndClientRenderedSuspenseBoundaryImpl(destination, renderState);
317 }
318
319 export function writePreambleStart(
320 destination: Destination,
321 resumableState: ResumableState,
322 renderState: RenderState,
323 skipBlockingShell: boolean,
324 ): void {
325 return writePreambleStartImpl(
326 destination,
327 resumableState,
328 renderState,
329 true, // skipBlockingShell
330 );
331 }
332
333 export function hasSuspenseyContent(
334 hoistableState: HoistableState,
335 flushingInShell: boolean,
336 ): boolean {
337 // Never outline.
338 return false;
339 }
340
341 export type TransitionStatus = FormStatus;
342 export const NotPendingTransition: TransitionStatus = NotPending;