From f4e974d26e273c4eb58c0ffa9b479435bf598ea1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?=
Date: Tue, 29 Oct 2019 14:45:47 -0700
Subject: [PATCH] Add Experimental Flight Infrastructure (#16398)
* Add Flight Build and Unify HostFormat Config between Flight and Fizz
* Add basic resolution of models
* Add basic Flight fixture
Demonstrates the streaming protocol.
* Rename to flight-server to distinguish from the client parts
* Add Flight Client package and entry point
* Fix fixture
---
fixtures/flight-browser/index.html | 84 ++++++++++
.../react-dom/npm/unstable-flight-client.js | 7 +
.../npm/unstable-flight-server.browser.js | 7 +
.../react-dom/npm/unstable-flight-server.js | 3 +
.../npm/unstable-flight-server.node.js | 7 +
packages/react-dom/package.json | 6 +-
.../src/client/flight/ReactFlightDOMClient.js | 34 +++++
.../client/flight/ReactFlightDOMHostConfig.js | 50 ++++++
.../__tests__/ReactFlightDOMBrowser-test.js | 61 ++++++++
.../__tests__/ReactFlightDOMNode-test.js | 57 +++++++
.../src/server/ReactDOMFizzServerBrowser.js | 2 +-
.../src/server/ReactDOMFizzServerNode.js | 2 +-
...onfig.js => ReactDOMServerFormatConfig.js} | 10 +-
.../flight/ReactFlightDOMServerBrowser.js | 34 +++++
.../server/flight/ReactFlightDOMServerNode.js | 31 ++++
.../__tests__/ReactFlightDOMBrowser-test.js | 61 ++++++++
.../__tests__/ReactFlightDOMNode-test.js | 57 +++++++
packages/react-dom/unstable-flight-client.js | 16 ++
.../unstable-flight-server.browser.js | 17 +++
packages/react-dom/unstable-flight-server.js | 12 ++
.../react-dom/unstable-flight-server.node.js | 16 ++
packages/react-flight/README.md | 7 +
packages/react-flight/index.js | 26 ++++
packages/react-flight/inline-typed.js | 24 +++
packages/react-flight/inline.dom-browser.js | 11 ++
packages/react-flight/inline.dom.js | 11 ++
packages/react-flight/npm/index.js | 7 +
packages/react-flight/package.json | 40 +++++
.../react-flight/src/ReactFlightClient.js | 144 ++++++++++++++++++
.../src/ReactFlightClientHostConfig.js} | 0
.../src/__tests__/ReactFlightClient-test.js | 38 +++++
.../ReactFlightClientHostConfig.custom.js | 37 +++++
...ReactFlightClientHostConfig.dom-browser.js | 10 ++
.../forks/ReactFlightClientHostConfig.dom.js | 10 ++
packages/react-noop-renderer/flight-client.js | 16 ++
packages/react-noop-renderer/flight-server.js | 16 ++
.../react-noop-renderer/npm/flight-client.js | 7 +
.../react-noop-renderer/npm/flight-server.js | 7 +
packages/react-noop-renderer/package.json | 7 +-
.../src/ReactNoopFlightClient.js | 54 +++++++
.../src/ReactNoopFlightServer.js | 54 +++++++
.../src/ReactNoopServer.js | 5 +-
packages/react-server/README.md | 7 +
packages/react-server/flight.inline-typed.js | 24 +++
.../react-server/flight.inline.dom-browser.js | 11 ++
packages/react-server/flight.inline.dom.js | 11 ++
packages/react-server/flight.js | 26 ++++
.../{react-stream => react-server}/index.js | 6 +-
.../inline-typed.js | 2 +-
.../inline.dom-browser.js | 0
.../inline.dom.js | 0
packages/react-server/npm/flight.js | 7 +
packages/react-server/npm/index.js | 7 +
.../package.json | 5 +-
.../src/ReactFizzStreamer.js | 6 +-
.../react-server/src/ReactFlightStreamer.js | 144 ++++++++++++++++++
.../src/ReactServerFormatConfig.js} | 0
.../react-server/src/ReactServerHostConfig.js | 22 +++
.../src/ReactServerHostConfigBrowser.js} | 0
.../src/ReactServerHostConfigNode.js} | 0
.../src/__tests__/ReactFlightServer-test.js | 38 +++++
.../src/__tests__/ReactServer-test.js | 0
.../forks/ReactServerFormatConfig.custom.js} | 7 +-
.../ReactServerFormatConfig.dom-browser.js} | 2 +-
.../src/forks/ReactServerFormatConfig.dom.js} | 2 +-
.../forks/ReactServerHostConfig.custom.js} | 6 +-
.../ReactServerHostConfig.dom-browser.js} | 2 +-
.../src/forks/ReactServerHostConfig.dom.js} | 2 +-
packages/react-stream/README.md | 25 ---
packages/react-stream/npm/index.js | 7 -
scripts/flow/createFlowConfigs.js | 15 +-
scripts/jest/setupHostConfigs.js | 109 +++++++++++--
scripts/rollup/bundles.js | 74 ++++++++-
scripts/rollup/forks.js | 50 ++++--
scripts/shared/inlinedHostConfigs.js | 31 ++--
75 files changed, 1651 insertions(+), 102 deletions(-)
create mode 100644 fixtures/flight-browser/index.html
create mode 100644 packages/react-dom/npm/unstable-flight-client.js
create mode 100644 packages/react-dom/npm/unstable-flight-server.browser.js
create mode 100644 packages/react-dom/npm/unstable-flight-server.js
create mode 100644 packages/react-dom/npm/unstable-flight-server.node.js
create mode 100644 packages/react-dom/src/client/flight/ReactFlightDOMClient.js
create mode 100644 packages/react-dom/src/client/flight/ReactFlightDOMHostConfig.js
create mode 100644 packages/react-dom/src/client/flight/__tests__/ReactFlightDOMBrowser-test.js
create mode 100644 packages/react-dom/src/client/flight/__tests__/ReactFlightDOMNode-test.js
rename packages/react-dom/src/server/{ReactDOMFizzServerFormatConfig.js => ReactDOMServerFormatConfig.js} (63%)
create mode 100644 packages/react-dom/src/server/flight/ReactFlightDOMServerBrowser.js
create mode 100644 packages/react-dom/src/server/flight/ReactFlightDOMServerNode.js
create mode 100644 packages/react-dom/src/server/flight/__tests__/ReactFlightDOMBrowser-test.js
create mode 100644 packages/react-dom/src/server/flight/__tests__/ReactFlightDOMNode-test.js
create mode 100644 packages/react-dom/unstable-flight-client.js
create mode 100644 packages/react-dom/unstable-flight-server.browser.js
create mode 100644 packages/react-dom/unstable-flight-server.js
create mode 100644 packages/react-dom/unstable-flight-server.node.js
create mode 100644 packages/react-flight/README.md
create mode 100644 packages/react-flight/index.js
create mode 100644 packages/react-flight/inline-typed.js
create mode 100644 packages/react-flight/inline.dom-browser.js
create mode 100644 packages/react-flight/inline.dom.js
create mode 100644 packages/react-flight/npm/index.js
create mode 100644 packages/react-flight/package.json
create mode 100644 packages/react-flight/src/ReactFlightClient.js
rename packages/{react-stream/src/ReactFizzFormatConfig.js => react-flight/src/ReactFlightClientHostConfig.js} (100%)
create mode 100644 packages/react-flight/src/__tests__/ReactFlightClient-test.js
create mode 100644 packages/react-flight/src/forks/ReactFlightClientHostConfig.custom.js
create mode 100644 packages/react-flight/src/forks/ReactFlightClientHostConfig.dom-browser.js
create mode 100644 packages/react-flight/src/forks/ReactFlightClientHostConfig.dom.js
create mode 100644 packages/react-noop-renderer/flight-client.js
create mode 100644 packages/react-noop-renderer/flight-server.js
create mode 100644 packages/react-noop-renderer/npm/flight-client.js
create mode 100644 packages/react-noop-renderer/npm/flight-server.js
create mode 100644 packages/react-noop-renderer/src/ReactNoopFlightClient.js
create mode 100644 packages/react-noop-renderer/src/ReactNoopFlightServer.js
create mode 100644 packages/react-server/README.md
create mode 100644 packages/react-server/flight.inline-typed.js
create mode 100644 packages/react-server/flight.inline.dom-browser.js
create mode 100644 packages/react-server/flight.inline.dom.js
create mode 100644 packages/react-server/flight.js
rename packages/{react-stream => react-server}/index.js (83%)
rename packages/{react-stream => react-server}/inline-typed.js (93%)
rename packages/{react-stream => react-server}/inline.dom-browser.js (100%)
rename packages/{react-stream => react-server}/inline.dom.js (100%)
create mode 100644 packages/react-server/npm/flight.js
create mode 100644 packages/react-server/npm/index.js
rename packages/{react-stream => react-server}/package.json (89%)
rename packages/{react-stream => react-server}/src/ReactFizzStreamer.js (92%)
create mode 100644 packages/react-server/src/ReactFlightStreamer.js
rename packages/{react-stream/src/ReactFizzHostConfig.js => react-server/src/ReactServerFormatConfig.js} (100%)
create mode 100644 packages/react-server/src/ReactServerHostConfig.js
rename packages/{react-stream/src/ReactFizzHostConfigBrowser.js => react-server/src/ReactServerHostConfigBrowser.js} (100%)
rename packages/{react-stream/src/ReactFizzHostConfigNode.js => react-server/src/ReactServerHostConfigNode.js} (100%)
create mode 100644 packages/react-server/src/__tests__/ReactFlightServer-test.js
rename packages/{react-stream => react-server}/src/__tests__/ReactServer-test.js (100%)
rename packages/{react-stream/src/forks/ReactFizzFormatConfig.custom.js => react-server/src/forks/ReactServerFormatConfig.custom.js} (73%)
rename packages/{react-stream/src/forks/ReactFizzFormatConfig.dom-browser.js => react-server/src/forks/ReactServerFormatConfig.dom-browser.js} (74%)
rename packages/{react-stream/src/forks/ReactFizzFormatConfig.dom.js => react-server/src/forks/ReactServerFormatConfig.dom.js} (74%)
rename packages/{react-stream/src/forks/ReactFizzHostConfig.custom.js => react-server/src/forks/ReactServerHostConfig.custom.js} (84%)
rename packages/{react-stream/src/forks/ReactFizzHostConfig.dom-browser.js => react-server/src/forks/ReactServerHostConfig.dom-browser.js} (80%)
rename packages/{react-stream/src/forks/ReactFizzHostConfig.dom.js => react-server/src/forks/ReactServerHostConfig.dom.js} (81%)
delete mode 100644 packages/react-stream/README.md
delete mode 100644 packages/react-stream/npm/index.js
diff --git a/fixtures/flight-browser/index.html b/fixtures/flight-browser/index.html
new file mode 100644
index 0000000000000..5f522a9b3a1a0
--- /dev/null
+++ b/fixtures/flight-browser/index.html
@@ -0,0 +1,84 @@
+
+
+
+
+ Flight Example
+
+
+ Flight Example
+
+
+ To install React, follow the instructions on
+ GitHub.
+
+
+ If you can see this, React is not working right.
+ If you checked out the source from GitHub make sure to run npm run build
.
+
+
+
+
+
+
+
+
+
diff --git a/packages/react-dom/npm/unstable-flight-client.js b/packages/react-dom/npm/unstable-flight-client.js
new file mode 100644
index 0000000000000..9116c361ebf74
--- /dev/null
+++ b/packages/react-dom/npm/unstable-flight-client.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-dom-unstable-flight-client.production.min.js');
+} else {
+ module.exports = require('./cjs/react-dom-unstable-flight-client.development.js');
+}
diff --git a/packages/react-dom/npm/unstable-flight-server.browser.js b/packages/react-dom/npm/unstable-flight-server.browser.js
new file mode 100644
index 0000000000000..8a6b19b7ab1cf
--- /dev/null
+++ b/packages/react-dom/npm/unstable-flight-server.browser.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-dom-unstable-flight-server.browser.production.min.js');
+} else {
+ module.exports = require('./cjs/react-dom-unstable-flight-server.browser.development.js');
+}
diff --git a/packages/react-dom/npm/unstable-flight-server.js b/packages/react-dom/npm/unstable-flight-server.js
new file mode 100644
index 0000000000000..4b75a8eef3b45
--- /dev/null
+++ b/packages/react-dom/npm/unstable-flight-server.js
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./unstable-flight-server.node');
diff --git a/packages/react-dom/npm/unstable-flight-server.node.js b/packages/react-dom/npm/unstable-flight-server.node.js
new file mode 100644
index 0000000000000..30b967fd425da
--- /dev/null
+++ b/packages/react-dom/npm/unstable-flight-server.node.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-dom-unstable-flight-server.node.production.min.js');
+} else {
+ module.exports = require('./cjs/react-dom-unstable-flight-server.node.development.js');
+}
diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json
index e05cac2ff5103..b413c885be8d0 100644
--- a/packages/react-dom/package.json
+++ b/packages/react-dom/package.json
@@ -39,13 +39,17 @@
"unstable-fizz.js",
"unstable-fizz.browser.js",
"unstable-fizz.node.js",
+ "unstable-flight-server.js",
+ "unstable-flight-server.browser.js",
+ "unstable-flight-server.node.js",
"unstable-native-dependencies.js",
"cjs/",
"umd/"
],
"browser": {
"./server.js": "./server.browser.js",
- "./unstable-fizz.js": "./unstable-fizz.browser.js"
+ "./unstable-fizz.js": "./unstable-fizz.browser.js",
+ "./unstable-flight-server.js": "./unstable-flight-server.browser.js"
},
"browserify": {
"transform": [
diff --git a/packages/react-dom/src/client/flight/ReactFlightDOMClient.js b/packages/react-dom/src/client/flight/ReactFlightDOMClient.js
new file mode 100644
index 0000000000000..18bc4753a5294
--- /dev/null
+++ b/packages/react-dom/src/client/flight/ReactFlightDOMClient.js
@@ -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
+ */
+
+import type {ReactModel} from 'react-flight/src/ReactFlightClient';
+
+import {
+ createRequest,
+ startWork,
+ startFlowing,
+} from 'react-flight/inline.dom-browser';
+
+function renderToReadableStream(model: ReactModel): ReadableStream {
+ let request;
+ return new ReadableStream({
+ start(controller) {
+ request = createRequest(model, controller);
+ startWork(request);
+ },
+ pull(controller) {
+ startFlowing(request, controller.desiredSize);
+ },
+ cancel(reason) {},
+ });
+}
+
+export default {
+ renderToReadableStream,
+};
diff --git a/packages/react-dom/src/client/flight/ReactFlightDOMHostConfig.js b/packages/react-dom/src/client/flight/ReactFlightDOMHostConfig.js
new file mode 100644
index 0000000000000..9dde4993ad6ad
--- /dev/null
+++ b/packages/react-dom/src/client/flight/ReactFlightDOMHostConfig.js
@@ -0,0 +1,50 @@
+/**
+ * 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 = ReadableStreamController;
+
+export function scheduleWork(callback: () => void) {
+ callback();
+}
+
+export function flushBuffered(destination: Destination) {
+ // WHATWG Streams do not yet have a way to flush the underlying
+ // transform streams. https://github.com/whatwg/streams/issues/960
+}
+
+export function beginWriting(destination: Destination) {}
+
+export function writeChunk(destination: Destination, buffer: Uint8Array) {
+ destination.enqueue(buffer);
+}
+
+export function completeWriting(destination: Destination) {}
+
+export function close(destination: Destination) {
+ destination.close();
+}
+
+const textEncoder = new TextEncoder();
+
+export function convertStringToBuffer(content: string): Uint8Array {
+ return textEncoder.encode(content);
+}
+
+export function formatChunkAsString(type: string, props: Object): string {
+ let str = '<' + type + '>';
+ if (typeof props.children === 'string') {
+ str += props.children;
+ }
+ str += '' + type + '>';
+ return str;
+}
+
+export function formatChunk(type: string, props: Object): Uint8Array {
+ return convertStringToBuffer(formatChunkAsString(type, props));
+}
diff --git a/packages/react-dom/src/client/flight/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-dom/src/client/flight/__tests__/ReactFlightDOMBrowser-test.js
new file mode 100644
index 0000000000000..39b0956553558
--- /dev/null
+++ b/packages/react-dom/src/client/flight/__tests__/ReactFlightDOMBrowser-test.js
@@ -0,0 +1,61 @@
+/**
+ * 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.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+// Polyfills for test environment
+global.ReadableStream = require('@mattiasbuelens/web-streams-polyfill/ponyfill/es6').ReadableStream;
+global.TextEncoder = require('util').TextEncoder;
+
+let React;
+let ReactFlightDOMServer;
+
+describe('ReactFlightDOM', () => {
+ beforeEach(() => {
+ jest.resetModules();
+ React = require('react');
+ ReactFlightDOMServer = require('react-dom/unstable-flight-server.browser');
+ });
+
+ async function readResult(stream) {
+ let reader = stream.getReader();
+ let result = '';
+ while (true) {
+ let {done, value} = await reader.read();
+ if (done) {
+ return result;
+ }
+ result += Buffer.from(value).toString('utf8');
+ }
+ }
+
+ it('should resolve HTML', async () => {
+ function Text({children}) {
+ return {children};
+ }
+ function HTML() {
+ return (
+
+ hello
+ world
+
+ );
+ }
+
+ let model = {
+ html: ,
+ };
+ let stream = ReactFlightDOMServer.renderToReadableStream(model);
+ jest.runAllTimers();
+ let result = JSON.parse(await readResult(stream));
+ expect(result).toEqual({
+ html: '
helloworld
',
+ });
+ });
+});
diff --git a/packages/react-dom/src/client/flight/__tests__/ReactFlightDOMNode-test.js b/packages/react-dom/src/client/flight/__tests__/ReactFlightDOMNode-test.js
new file mode 100644
index 0000000000000..79494a49164eb
--- /dev/null
+++ b/packages/react-dom/src/client/flight/__tests__/ReactFlightDOMNode-test.js
@@ -0,0 +1,57 @@
+/**
+ * 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.
+ *
+ * @emails react-core
+ * @jest-environment node
+ */
+
+'use strict';
+
+let Stream;
+let React;
+let ReactFlightDOMServer;
+
+describe('ReactFlightDOM', () => {
+ beforeEach(() => {
+ jest.resetModules();
+ React = require('react');
+ ReactFlightDOMServer = require('react-dom/unstable-flight-server');
+ Stream = require('stream');
+ });
+
+ function getTestWritable() {
+ let writable = new Stream.PassThrough();
+ writable.setEncoding('utf8');
+ writable.result = '';
+ writable.on('data', chunk => (writable.result += chunk));
+ return writable;
+ }
+
+ it('should resolve HTML', () => {
+ function Text({children}) {
+ return {children};
+ }
+ function HTML() {
+ return (
+
+ hello
+ world
+
+ );
+ }
+
+ let writable = getTestWritable();
+ let model = {
+ html: