Add Relay Flight Build (#18242)

* Rename to clarify that it's client-only

* Rename FizzStreamer to FizzServer for consistency

* Rename react-flight to react-client/flight

For consistency with react-server. Currently this just includes flight
but it could be expanded to include the whole reconciler.

* Add Relay Flight Build

* Rename ReactServerHostConfig to ReactServerStreamConfig

This will be the config specifically for streaming purposes.
There will be other configs for other purposes.
This commit is contained in:
Sebastian Markbåge 2020-03-07 11:23:30 -08:00 committed by GitHub
parent 7a1691cdff
commit bdc5cc4635
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 311 additions and 48 deletions

View File

@ -1,4 +1,4 @@
# react-flight
# react-client
This is an experimental package for consuming custom React streaming models.

7
packages/react-client/npm/flight.js vendored Normal file
View File

@ -0,0 +1,7 @@
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-client-flight.production.min.js');
} else {
module.exports = require('./cjs/react-client-flight.development.js');
}

View File

@ -1,5 +1,5 @@
{
"name": "react-flight",
"name": "react-client",
"description": "React package for consuming streaming models.",
"version": "0.1.0",
"private": true,
@ -12,14 +12,13 @@
"files": [
"LICENSE",
"README.md",
"index.js",
"flight.js",
"cjs/"
],
"main": "index.js",
"repository": {
"type" : "git",
"url" : "https://github.com/facebook/react.git",
"directory": "packages/react-flight"
"directory": "packages/react-client"
},
"engines": {
"node": ">=0.10.0"

View File

@ -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-client/src/ReactFlightClientHostConfigBrowser';

View File

@ -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-flight-dom-relay/src/ReactFlightDOMRelayClientHostConfig';

View File

@ -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-client/src/ReactFlightClientHostConfigBrowser';

View File

@ -13,7 +13,7 @@ import {
createRequest,
startWork,
startFlowing,
} from 'react-server/src/ReactFizzStreamer';
} from 'react-server/src/ReactFizzServer';
function renderToReadableStream(children: ReactNodeList): ReadableStream {
let request;

View File

@ -14,7 +14,7 @@ import {
createRequest,
startWork,
startFlowing,
} from 'react-server/src/ReactFizzStreamer';
} from 'react-server/src/ReactFizzServer';
function createDrainHandler(destination, request) {
return () => startFlowing(request);

View File

@ -7,4 +7,4 @@
* @flow
*/
export * from '../ReactServerHostConfigBrowser';
export * from './src/ReactFlightDOMRelayClient';

View File

@ -0,0 +1,18 @@
{
"name": "react-flight-dom-relay",
"version": "0.1.0",
"private": true,
"repository": {
"type" : "git",
"url" : "https://github.com/facebook/react.git",
"directory": "packages/react-flight-dom-relay"
},
"dependencies": {
"object-assign": "^4.1.1",
"scheduler": "^0.11.0"
},
"peerDependencies": {
"react": "^16.0.0",
"react-dom": "^16.0.0"
}
}

View File

@ -7,4 +7,4 @@
* @flow
*/
export * from 'react-flight/src/ReactFlightHostConfigBrowser';
export * from './src/ReactFlightDOMRelayServer';

View File

@ -0,0 +1,30 @@
/**
* 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 {ReactModelRoot} from 'react-client/src/ReactFlightClient';
import {
createResponse,
getModelRoot,
processStringChunk,
complete,
} from 'react-client/src/ReactFlightClient';
type EncodedData = Array<string>;
function read<T>(data: EncodedData): ReactModelRoot<T> {
let response = createResponse(data);
for (let i = 0; i < data.length; i++) {
processStringChunk(response, data[i], 0);
}
complete(response);
return getModelRoot(response);
}
export {read};

View File

@ -0,0 +1,32 @@
/**
* 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 Source = Array<string>;
export type StringDecoder = void;
export const supportsBinaryStreams = false;
export function createStringDecoder(): void {
throw new Error('Should never be called');
}
export function readPartialStringChunk(
decoder: StringDecoder,
buffer: Uint8Array,
): string {
throw new Error('Should never be called');
}
export function readFinalStringChunk(
decoder: StringDecoder,
buffer: Uint8Array,
): string {
throw new Error('Should never be called');
}

View File

@ -0,0 +1,23 @@
/**
* 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 {ReactModel} from 'react-server/src/ReactFlightServer';
import {createRequest, startWork} from 'react-server/src/ReactFlightServer';
type EncodedData = Array<string>;
function render(model: ReactModel): EncodedData {
let data: EncodedData = [];
let request = createRequest(model, data);
startWork(request);
return data;
}
export {render};

View File

@ -0,0 +1,34 @@
/**
* 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 = Array<string>;
export function scheduleWork(callback: () => void) {
callback();
}
export function flushBuffered(destination: Destination) {}
export function beginWriting(destination: Destination) {}
export function writeChunk(
destination: Destination,
buffer: Uint8Array,
): boolean {
destination.push(Buffer.from((buffer: any)).toString('utf8'));
return true;
}
export function completeWriting(destination: Destination) {}
export function close(destination: Destination) {}
export function convertStringToBuffer(content: string): Uint8Array {
return Buffer.from(content, 'utf8');
}

View File

@ -0,0 +1,42 @@
/**
* 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.
*/
'use strict';
// Polyfills for test environment
global.TextDecoder = require('util').TextDecoder;
let React;
let ReactDOMFlightRelayServer;
let ReactDOMFlightRelayClient;
describe('ReactFlightDOMRelay', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOMFlightRelayServer = require('react-flight-dom-relay/server');
ReactDOMFlightRelayClient = require('react-flight-dom-relay');
});
it('can resolve a model', () => {
function Bar({text}) {
return text.toUpperCase();
}
function Foo() {
return {
bar: [<Bar text="a" />, <Bar text="b" />],
};
}
let data = ReactDOMFlightRelayServer.render({
foo: <Foo />,
});
let root = ReactDOMFlightRelayClient.read(data);
let model = root.model;
expect(model).toEqual({foo: {bar: ['A', 'B']}});
});
});

View File

@ -7,7 +7,7 @@
* @flow
*/
import type {ReactModelRoot} from 'react-flight/src/ReactFlightClient';
import type {ReactModelRoot} from 'react-client/src/ReactFlightClient';
import {
createResponse,
@ -16,7 +16,7 @@ import {
processStringChunk,
processBinaryChunk,
complete,
} from 'react-flight/src/ReactFlightClient';
} from 'react-client/src/ReactFlightClient';
function startReadingFromStream(response, stream: ReadableStream): void {
let reader = stream.getReader();

View File

@ -1,7 +0,0 @@
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-flight.production.min.js');
} else {
module.exports = require('./cjs/react-flight.development.js');
}

View File

@ -14,7 +14,7 @@
"object-assign": "^4.1.1",
"regenerator-runtime": "^0.11.0",
"react-reconciler": "*",
"react-flight": "*",
"react-client": "*",
"react-server": "*"
},
"peerDependencies": {

View File

@ -14,9 +14,9 @@
* environment.
*/
import type {ReactModelRoot} from 'react-flight';
import type {ReactModelRoot} from 'react-client/flight';
import ReactFlightClient from 'react-flight';
import ReactFlightClient from 'react-client/flight';
type Source = Array<string>;

View File

@ -14,11 +14,11 @@
* environment.
*/
import ReactFizzStreamer from 'react-server';
import ReactFizzServer from 'react-server';
type Destination = Array<string>;
const ReactNoopServer = ReactFizzStreamer({
const ReactNoopServer = ReactFizzServer({
scheduleWork(callback: () => void) {
callback();
},

View File

@ -7,4 +7,4 @@
* @flow
*/
export * from 'react-flight/src/ReactFlightHostConfigBrowser';
export * from 'react-dom/src/client/ReactDOMHostConfig';

View File

@ -7,4 +7,4 @@
* @flow
*/
export * from './src/ReactFizzStreamer';
export * from './src/ReactFizzServer';

View File

@ -7,7 +7,7 @@
* @flow
*/
import {convertStringToBuffer} from 'react-server/src/ReactServerHostConfig';
import {convertStringToBuffer} from 'react-server/src/ReactServerStreamConfig';
import {renderToStaticMarkup} from 'react-dom/server';

View File

@ -7,7 +7,7 @@
* @flow
*/
import type {Destination} from './ReactServerHostConfig';
import type {Destination} from './ReactServerStreamConfig';
import type {ReactNodeList} from 'shared/ReactTypes';
import {
@ -17,7 +17,7 @@ import {
completeWriting,
flushBuffered,
close,
} from './ReactServerHostConfig';
} from './ReactServerStreamConfig';
import {formatChunk} from './ReactServerFormatConfig';
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';

View File

@ -7,7 +7,7 @@
* @flow
*/
import type {Destination} from './ReactServerHostConfig';
import type {Destination} from './ReactServerStreamConfig';
import {
scheduleWork,
@ -17,7 +17,7 @@ import {
flushBuffered,
close,
convertStringToBuffer,
} from './ReactServerHostConfig';
} from './ReactServerStreamConfig';
import {renderHostChildrenToString} from './ReactServerFormatConfig';
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';

View File

@ -7,4 +7,4 @@
* @flow
*/
export * from '../ReactServerHostConfigNode';
export * from '../ReactDOMServerFormatConfig';

View File

@ -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 '../ReactServerStreamConfigBrowser';

View File

@ -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-flight-dom-relay/src/ReactFlightDOMRelayServerHostConfig';

View File

@ -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 '../ReactServerStreamConfigNode';

View File

@ -49,7 +49,7 @@ function writeConfig(renderer, rendererInfo, isServerSupported) {
'%REACT_RENDERER_FLOW_OPTIONS%',
`
module.name_mapper='ReactFiberHostConfig$$' -> 'forks/ReactFiberHostConfig.${renderer}'
module.name_mapper='ReactServerHostConfig$$' -> 'forks/ReactServerHostConfig.${serverRenderer}'
module.name_mapper='ReactServerStreamConfig$$' -> 'forks/ReactServerStreamConfig.${serverRenderer}'
module.name_mapper='ReactServerFormatConfig$$' -> 'forks/ReactServerFormatConfig.${serverRenderer}'
module.name_mapper='ReactFlightClientHostConfig$$' -> 'forks/ReactFlightClientHostConfig.${serverRenderer}'
`.trim(),

View File

@ -11,35 +11,35 @@ jest.mock('react-reconciler', () => {
return require.requireActual('react-reconciler');
};
});
const shimServerHostConfigPath = 'react-server/src/ReactServerHostConfig';
const shimServerStreamConfigPath = 'react-server/src/ReactServerStreamConfig';
const shimServerFormatConfigPath = 'react-server/src/ReactServerFormatConfig';
jest.mock('react-server', () => {
return config => {
jest.mock(shimServerHostConfigPath, () => config);
jest.mock(shimServerStreamConfigPath, () => config);
jest.mock(shimServerFormatConfigPath, () => config);
return require.requireActual('react-server');
};
});
jest.mock('react-server/flight', () => {
return config => {
jest.mock(shimServerHostConfigPath, () => config);
jest.mock(shimServerStreamConfigPath, () => config);
jest.mock(shimServerFormatConfigPath, () => config);
return require.requireActual('react-server/flight');
};
});
const shimFlightClientHostConfigPath =
'react-flight/src/ReactFlightClientHostConfig';
jest.mock('react-flight', () => {
'react-client/src/ReactFlightClientHostConfig';
jest.mock('react-client/flight', () => {
return config => {
jest.mock(shimFlightClientHostConfigPath, () => config);
return require.requireActual('react-flight');
return require.requireActual('react-client/flight');
};
});
const configPaths = [
'react-reconciler/src/ReactFiberHostConfig',
'react-flight/src/ReactFlightClientHostConfig',
'react-server/src/ReactServerHostConfig',
'react-client/src/ReactFlightClientHostConfig',
'react-server/src/ReactServerStreamConfig',
'react-server/src/ReactServerFormatConfig',
];

View File

@ -197,6 +197,24 @@ const bundles = [
externals: ['react'],
},
/******* React DOM Flight Server Relay *******/
{
bundleTypes: [FB_WWW_DEV, FB_WWW_PROD],
moduleType: RENDERER,
entry: 'react-flight-dom-relay/server',
global: 'ReactFlightDOMRelayServer',
externals: ['react', 'react-dom/server'],
},
/******* React DOM Flight Client Relay *******/
{
bundleTypes: [FB_WWW_DEV, FB_WWW_PROD],
moduleType: RENDERER,
entry: 'react-flight-dom-relay',
global: 'ReactFlightDOMRelayClient',
externals: ['react'],
},
/******* React ART *******/
{
bundleTypes: [
@ -384,7 +402,7 @@ const bundles = [
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: RECONCILER,
entry: 'react-flight',
entry: 'react-client/flight',
global: 'ReactFlightClient',
externals: ['react'],
},

View File

@ -298,7 +298,7 @@ const forks = Object.freeze({
);
},
'react-server/src/ReactServerHostConfig': (
'react-server/src/ReactServerStreamConfig': (
bundleType,
entry,
dependencies,
@ -316,11 +316,11 @@ const forks = Object.freeze({
if (!rendererInfo.isServerSupported) {
return null;
}
return `react-server/src/forks/ReactServerHostConfig.${rendererInfo.shortName}.js`;
return `react-server/src/forks/ReactServerStreamConfig.${rendererInfo.shortName}.js`;
}
}
throw new Error(
'Expected ReactServerHostConfig to always be replaced with a shim, but ' +
'Expected ReactServerStreamConfig to always be replaced with a shim, but ' +
`found no mention of "${entry}" entry point in ./scripts/shared/inlinedHostConfigs.js. ` +
'Did you mean to add it there to associate it with a specific renderer?'
);
@ -354,13 +354,13 @@ const forks = Object.freeze({
);
},
'react-flight/src/ReactFlightClientHostConfig': (
'react-client/src/ReactFlightClientHostConfig': (
bundleType,
entry,
dependencies,
moduleType
) => {
if (dependencies.indexOf('react-flight') !== -1) {
if (dependencies.indexOf('react-client') !== -1) {
return null;
}
if (moduleType !== RENDERER && moduleType !== RECONCILER) {
@ -372,7 +372,7 @@ const forks = Object.freeze({
if (!rendererInfo.isServerSupported) {
return null;
}
return `react-flight/src/forks/ReactFlightClientHostConfig.${rendererInfo.shortName}.js`;
return `react-client/src/forks/ReactFlightClientHostConfig.${rendererInfo.shortName}.js`;
}
}
throw new Error(

View File

@ -78,11 +78,18 @@ module.exports = [
isFlowTyped: true,
isServerSupported: false,
},
{
shortName: 'dom-relay',
entryPoints: ['react-flight-dom-relay', 'react-flight-dom-relay/server'],
paths: ['react-dom', 'react-flight-dom-relay'],
isFlowTyped: true,
isServerSupported: true,
},
{
shortName: 'custom',
entryPoints: [
'react-reconciler',
'react-flight',
'react-client/flight',
'react-server',
'react-server/flight',
],