Files
react/packages/react-stream/src/ReactFizzStreamer.js
T
Sebastian Markbåge 1d25aa5787 [Fizz] New Server Rendering Infra (#14144)
* [Fizz] Add Flow/Jest/Rollup build infra

Add a new package for react-stream which allows for custom server renderer
outputs. I picked the name because it's a reasonable name but also
because the npm name is currently owned by a friend of the project.

The react-dom build has its own inlined server renderer under the
name `react-dom/fizz`.

There is also a noop renderer to be used for testing. At some point
we might add a public one to test-renderer but for now I don't want to have
to think about public API design for the tests.

* Add FormatConfig too

We need to separate the format (DOM, React Native, etc) from the host
running the server (Node, Browser, etc).

* Basic wiring between Node, Noop and DOM configs

The Node DOM API is pipeToNodeStream which accepts a writable stream.

* Merge host and format config in dynamic react-stream entry point

Simpler API this way but also avoids having to fork the wrapper config.

Fixes noop builds.

* Add setImmediate/Buffer globals to lint config

Used by the server renderer

* Properly include fizz.node.js

Also use forwarding to it from fizz.js in builds so that tests covers
this.

* Make react-stream private since we're not ready to publish

or even name it yet

* Rename Renderer -> Streamer

* Prefix react-dom/fizz with react-dom/unstable-fizz

* Add Fizz Browser host config

This lets Fizz render to WHATWG streams. E.g. for rendering in a
Service Worker.

I added react-dom/unstable-fizz.browser as the entry point for this.

Since we now have two configurations of DOM. I had to add another
inlinedHostConfigs configuration called `dom-browser`. The reconciler
treats this configuration the same as `dom`. For stream it checks
against the ReactFizzHostConfigBrowser instead of the Node one.

* Add Fizz Browser Fixture

This is for testing server rendering - on the client.

* Lower version number to detach it from react-reconciler version
2018-11-30 11:38:22 -08:00

86 lines
2.0 KiB
JavaScript

/**
* 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 {Destination} from './ReactFizzHostConfig';
import type {ReactNodeList} from 'shared/ReactTypes';
import {
scheduleWork,
beginWriting,
writeChunk,
completeWriting,
flushBuffered,
close,
} from './ReactFizzHostConfig';
import {formatChunk} from './ReactFizzFormatConfig';
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
type OpaqueRequest = {
destination: Destination,
children: ReactNodeList,
completedChunks: Array<Uint8Array>,
flowing: boolean,
};
export function createRequest(
children: ReactNodeList,
destination: Destination,
): OpaqueRequest {
return {destination, children, completedChunks: [], flowing: false};
}
function performWork(request: OpaqueRequest): void {
let element = (request.children: any);
request.children = null;
if (element && element.$$typeof !== REACT_ELEMENT_TYPE) {
return;
}
let type = element.type;
let props = element.props;
if (typeof type !== 'string') {
return;
}
request.completedChunks.push(formatChunk(type, props));
if (request.flowing) {
flushCompletedChunks(request);
}
flushBuffered(request.destination);
}
function flushCompletedChunks(request: OpaqueRequest) {
let destination = request.destination;
let chunks = request.completedChunks;
request.completedChunks = [];
beginWriting(destination);
try {
for (let i = 0; i < chunks.length; i++) {
let chunk = chunks[i];
writeChunk(destination, chunk);
}
} finally {
completeWriting(destination);
}
close(destination);
}
export function startWork(request: OpaqueRequest): void {
request.flowing = true;
scheduleWork(() => performWork(request));
}
export function startFlowing(
request: OpaqueRequest,
desiredBytes: number,
): void {
request.flowing = false;
flushCompletedChunks(request);
}