-
Notifications
You must be signed in to change notification settings - Fork 51k
Add Next.js runtime support to ReactFizzServer #22350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
4a94ad9
360b979
405ae1c
4ff9162
f3ce989
83b2d9b
36b6295
fdebe63
b492e9a
2c16fdc
4604704
eedc2de
a290681
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| export * from 'react-client/src/ReactFlightClientHostConfigBrowser'; | ||
| export * from 'react-client/src/ReactFlightClientHostConfigStream'; | ||
| export * from 'react-server-dom-webpack/src/ReactFlightClientWebpackBundlerConfig'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| import type {ReactNodeList} from 'shared/ReactTypes'; | ||
| import type {Destination} from 'react-server/src/ReactServerStreamConfigNext'; | ||
|
|
||
| import ReactVersion from 'shared/ReactVersion'; | ||
|
|
||
| import { | ||
| createRequest, | ||
| startWork, | ||
| startFlowing, | ||
| stopFlowing, | ||
| abort, | ||
| } from 'react-server/src/ReactFizzServer'; | ||
|
|
||
| import { | ||
| createResponseState, | ||
| createRootFormatContext, | ||
| } from './ReactDOMServerFormatConfig'; | ||
|
|
||
| type Options = {| | ||
| identifierPrefix?: string, | ||
| namespaceURI?: string, | ||
| progressiveChunkSize?: number, | ||
| onReadyToStream?: () => void, | ||
| onCompleteAll?: () => void, | ||
| onError?: (error: mixed) => void, | ||
| |}; | ||
|
|
||
| type Controls = {| | ||
| abort(): void, | ||
| startWriting(): void, | ||
| stopWriting(): void, | ||
| |}; | ||
|
|
||
| function createRequestImpl( | ||
| children: ReactNodeList, | ||
| destination: Destination, | ||
| options: void | Options, | ||
| ) { | ||
| return createRequest( | ||
| children, | ||
| destination, | ||
| createResponseState(options ? options.identifierPrefix : undefined), | ||
| createRootFormatContext(options ? options.namespaceURI : undefined), | ||
| options ? options.progressiveChunkSize : undefined, | ||
| options ? options.onError : undefined, | ||
| options ? options.onCompleteAll : undefined, | ||
| options ? options.onReadyToStream : undefined, | ||
| ); | ||
| } | ||
|
|
||
| function renderToNextStream( | ||
| children: ReactNodeList, | ||
| destination: Destination, | ||
| options?: Options, | ||
| ): Controls { | ||
| const request = createRequestImpl(children, destination, options); | ||
| startWork(request); | ||
| return { | ||
| abort() { | ||
| abort(request); | ||
| }, | ||
| startWriting() { | ||
| startFlowing(request); | ||
| }, | ||
| stopWriting() { | ||
| stopFlowing(request); | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export {renderToNextStream, ReactVersion as version}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| export * from 'react-dom/src/client/ReactDOMHostConfig'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -272,7 +272,7 @@ function pingTask(request: Request, task: Task): void { | |
| const pingedTasks = request.pingedTasks; | ||
| pingedTasks.push(task); | ||
| if (pingedTasks.length === 1) { | ||
| scheduleWork(() => performWork(request)); | ||
| scheduleWork(request.destination, () => performWork(request)); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1899,7 +1899,7 @@ function flushCompletedQueues(request: Request): void { | |
| } | ||
|
|
||
| export function startWork(request: Request): void { | ||
| scheduleWork(() => performWork(request)); | ||
| scheduleWork(request.destination, () => performWork(request)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While it might make sense to want to schedule based on the destination, the assumption of this API implies a lot more of a global scheduler like postTask. I worry that this might take us down the wrong assumptions. For sync I/O, it'll also be required that it's an instance (or at least thread) per request so in that world it doesn't matter anyway. What is the intention/purpose of this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eh, just some flexibility. I can revert it and make it use a global instead, that wouldn't be a problem. |
||
| } | ||
|
|
||
| export function startFlowing(request: Request): void { | ||
|
|
@@ -1915,6 +1915,13 @@ export function startFlowing(request: Request): void { | |
| } | ||
| } | ||
|
|
||
| export function stopFlowing(request: Request): void { | ||
| if (request.status === CLOSED) { | ||
| return; | ||
| } | ||
| request.status = BUFFERING; | ||
| } | ||
|
|
||
| // This is called to early terminate a request. It puts all pending boundaries in client rendered state. | ||
| export function abort(request: Request): void { | ||
| try { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| export type Destination = { | ||
| write: (chunk: Uint8Array) => void, | ||
| buffer: (shouldBuffer: boolean) => void, | ||
| flush: () => void, | ||
| close: (error: mixed) => void, | ||
| schedule: (callback: () => void) => void, | ||
| ready: boolean, | ||
| }; | ||
|
|
||
| export type PrecomputedChunk = Uint8Array; | ||
| export type Chunk = Uint8Array; | ||
|
|
||
| export function scheduleWork(destination: Destination, callback: () => void) { | ||
| destination.schedule(callback); | ||
| } | ||
|
|
||
| export function flushBuffered(destination: Destination) { | ||
| destination.flush(); | ||
| } | ||
|
|
||
| export function beginWriting(destination: Destination) { | ||
| destination.buffer(true); | ||
| } | ||
|
|
||
| export function writeChunk( | ||
| destination: Destination, | ||
| chunk: PrecomputedChunk | Chunk, | ||
| ): boolean { | ||
| destination.write(chunk); | ||
| return destination.ready; | ||
| } | ||
|
|
||
| export function completeWriting(destination: Destination) { | ||
| destination.buffer(false); | ||
| } | ||
|
|
||
| export function close(destination: Destination) { | ||
| destination.close(); | ||
| } | ||
|
|
||
| const textEncoder = new TextEncoder(); | ||
|
|
||
| export function stringToChunk(content: string): Chunk { | ||
| return textEncoder.encode(content); | ||
| } | ||
|
|
||
| export function stringToPrecomputedChunk(content: string): PrecomputedChunk { | ||
| return textEncoder.encode(content); | ||
| } | ||
|
|
||
| export function closeWithError(destination: Destination, error: mixed): void { | ||
| destination.close(error); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| export * from '../ReactFlightServerConfigStream'; | ||
| export * from 'react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| export * from 'react-dom/src/server/ReactDOMServerFormatConfig'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow | ||
| */ | ||
|
|
||
| export * from '../ReactServerStreamConfigNext'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning on renaming startWriting so naming this as a pair wouldn't necessarily make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite sure what you mean. Would a single function to toggle on/off be better? Or otherwise could you elaborate?