diff --git a/fixtures/fizz-ssr-browser/index.html b/fixtures/fizz-ssr-browser/index.html
new file mode 100644
index 0000000000000..aa522dfe83653
--- /dev/null
+++ b/fixtures/fizz-ssr-browser/index.html
@@ -0,0 +1,40 @@
+
+
+
+
+ Fizz Example
+
+
+ Fizz 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/package.json b/package.json
index f6829548c0a66..eb5fe1324ba81 100644
--- a/package.json
+++ b/package.json
@@ -84,7 +84,8 @@
"targz": "^1.0.1",
"through2": "^2.0.0",
"tmp": "~0.0.28",
- "typescript": "~1.8.10"
+ "typescript": "~1.8.10",
+ "@mattiasbuelens/web-streams-polyfill": "0.1.0"
},
"devEngines": {
"node": "8.x || 9.x || 10.x"
diff --git a/packages/react-dom/npm/unstable-fizz.browser.js b/packages/react-dom/npm/unstable-fizz.browser.js
new file mode 100644
index 0000000000000..976fac7eca51c
--- /dev/null
+++ b/packages/react-dom/npm/unstable-fizz.browser.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-dom-unstable-fizz.browser.production.min.js');
+} else {
+ module.exports = require('./cjs/react-dom-unstable-fizz.browser.development.js');
+}
diff --git a/packages/react-dom/npm/unstable-fizz.js b/packages/react-dom/npm/unstable-fizz.js
new file mode 100644
index 0000000000000..ca4135d36b87c
--- /dev/null
+++ b/packages/react-dom/npm/unstable-fizz.js
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./unstable-fizz.node');
diff --git a/packages/react-dom/npm/unstable-fizz.node.js b/packages/react-dom/npm/unstable-fizz.node.js
new file mode 100644
index 0000000000000..038a3fd0fb495
--- /dev/null
+++ b/packages/react-dom/npm/unstable-fizz.node.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-dom-unstable-fizz.node.production.min.js');
+} else {
+ module.exports = require('./cjs/react-dom-unstable-fizz.node.development.js');
+}
diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json
index 6c4e6255acc75..c0c283ea04b49 100644
--- a/packages/react-dom/package.json
+++ b/packages/react-dom/package.json
@@ -32,12 +32,16 @@
"server.node.js",
"test-utils.js",
"unstable-fire.js",
+ "unstable-fizz.js",
+ "unstable-fizz.browser.js",
+ "unstable-fizz.node.js",
"unstable-native-dependencies.js",
"cjs/",
"umd/"
],
"browser": {
- "./server.js": "./server.browser.js"
+ "./server.js": "./server.browser.js",
+ "./unstable-fizz.js": "./unstable-fizz.browser.js"
},
"browserify": {
"transform": [
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
new file mode 100644
index 0000000000000..3d34b661104bd
--- /dev/null
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
@@ -0,0 +1,45 @@
+/**
+ * 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 ReactDOMFizzServer;
+
+describe('ReactDOMFizzServer', () => {
+ beforeEach(() => {
+ jest.resetModules();
+ React = require('react');
+ ReactDOMFizzServer = require('react-dom/unstable-fizz.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 call renderToReadableStream', async () => {
+ let stream = ReactDOMFizzServer.renderToReadableStream(
+ hello world
,
+ );
+ let result = await readResult(stream);
+ expect(result).toBe('hello world
');
+ });
+});
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
new file mode 100644
index 0000000000000..79fa80b322b71
--- /dev/null
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
@@ -0,0 +1,39 @@
+/**
+ * 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 ReactDOMFizzServer;
+
+describe('ReactDOMFizzServer', () => {
+ beforeEach(() => {
+ jest.resetModules();
+ React = require('react');
+ ReactDOMFizzServer = require('react-dom/unstable-fizz');
+ 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 call pipeToNodeWritable', () => {
+ let writable = getTestWritable();
+ ReactDOMFizzServer.pipeToNodeWritable(hello world
, writable);
+ jest.runAllTimers();
+ expect(writable.result).toBe('hello world
');
+ });
+});
diff --git a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
new file mode 100644
index 0000000000000..17da2d7e48e87
--- /dev/null
+++ b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.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 {ReactNodeList} from 'shared/ReactTypes';
+
+import {
+ createRequest,
+ startWork,
+ startFlowing,
+} from 'react-stream/inline.dom-browser';
+
+function renderToReadableStream(children: ReactNodeList): ReadableStream {
+ let request;
+ return new ReadableStream({
+ start(controller) {
+ request = createRequest(children, controller);
+ startWork(request);
+ },
+ pull(controller) {
+ startFlowing(request, controller.desiredSize);
+ },
+ cancel(reason) {},
+ });
+}
+
+export default {
+ renderToReadableStream,
+};
diff --git a/packages/react-dom/src/server/ReactDOMFizzServerFormatConfig.js b/packages/react-dom/src/server/ReactDOMFizzServerFormatConfig.js
new file mode 100644
index 0000000000000..a9c6dc79df830
--- /dev/null
+++ b/packages/react-dom/src/server/ReactDOMFizzServerFormatConfig.js
@@ -0,0 +1,19 @@
+/**
+ * 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 {convertStringToBuffer} from 'react-stream/src/ReactFizzHostConfig';
+
+export function formatChunk(type: string, props: Object): Uint8Array {
+ let str = '<' + type + '>';
+ if (typeof props.children === 'string') {
+ str += props.children;
+ }
+ str += '' + type + '>';
+ return convertStringToBuffer(str);
+}
diff --git a/packages/react-dom/src/server/ReactDOMFizzServerNode.js b/packages/react-dom/src/server/ReactDOMFizzServerNode.js
new file mode 100644
index 0000000000000..72b1bcb74272c
--- /dev/null
+++ b/packages/react-dom/src/server/ReactDOMFizzServerNode.js
@@ -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 {ReactNodeList} from 'shared/ReactTypes';
+import type {Writable} from 'stream';
+
+import {createRequest, startWork, startFlowing} from 'react-stream/inline.dom';
+
+function createDrainHandler(destination, request) {
+ return () => startFlowing(request, 0);
+}
+
+function pipeToNodeWritable(
+ children: ReactNodeList,
+ destination: Writable,
+): void {
+ let request = createRequest(children, destination);
+ destination.on('drain', createDrainHandler(destination, request));
+ startWork(request);
+}
+
+export default {
+ pipeToNodeWritable,
+};
diff --git a/packages/react-dom/unstable-fizz.browser.js b/packages/react-dom/unstable-fizz.browser.js
new file mode 100644
index 0000000000000..4fb175ec6825e
--- /dev/null
+++ b/packages/react-dom/unstable-fizz.browser.js
@@ -0,0 +1,16 @@
+/**
+ * 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
+ */
+
+'use strict';
+
+const ReactDOMFizzServerBrowser = require('./src/server/ReactDOMFizzServerBrowser');
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest
+module.exports = ReactDOMFizzServerBrowser.default || ReactDOMFizzServerBrowser;
diff --git a/packages/react-dom/unstable-fizz.js b/packages/react-dom/unstable-fizz.js
new file mode 100644
index 0000000000000..81d1ccee698d1
--- /dev/null
+++ b/packages/react-dom/unstable-fizz.js
@@ -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
+ */
+
+'use strict';
+
+module.exports = require('./unstable-fizz.node');
diff --git a/packages/react-dom/unstable-fizz.node.js b/packages/react-dom/unstable-fizz.node.js
new file mode 100644
index 0000000000000..ef943c74eccf5
--- /dev/null
+++ b/packages/react-dom/unstable-fizz.node.js
@@ -0,0 +1,16 @@
+/**
+ * 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
+ */
+
+'use strict';
+
+const ReactDOMFizzServerNode = require('./src/server/ReactDOMFizzServerNode');
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest
+module.exports = ReactDOMFizzServerNode.default || ReactDOMFizzServerNode;
diff --git a/packages/react-noop-renderer/npm/server.js b/packages/react-noop-renderer/npm/server.js
new file mode 100644
index 0000000000000..1abd91cef128e
--- /dev/null
+++ b/packages/react-noop-renderer/npm/server.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-noop-renderer-server.production.min.js');
+} else {
+ module.exports = require('./cjs/react-noop-renderer-server.development.js');
+}
diff --git a/packages/react-noop-renderer/package.json b/packages/react-noop-renderer/package.json
index 51b668a366b76..87f4ba431beb6 100644
--- a/packages/react-noop-renderer/package.json
+++ b/packages/react-noop-renderer/package.json
@@ -10,7 +10,8 @@
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"regenerator-runtime": "^0.11.0",
- "react-reconciler": "*"
+ "react-reconciler": "*",
+ "react-stream": "*"
},
"peerDependencies": {
"react": "^16.0.0"
@@ -21,6 +22,7 @@
"build-info.json",
"index.js",
"persistent.js",
+ "server.js",
"cjs/"
]
}
diff --git a/packages/react-noop-renderer/server.js b/packages/react-noop-renderer/server.js
new file mode 100644
index 0000000000000..1665a0767a020
--- /dev/null
+++ b/packages/react-noop-renderer/server.js
@@ -0,0 +1,16 @@
+/**
+ * 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
+ */
+
+'use strict';
+
+const ReactNoopServer = require('./src/ReactNoopServer');
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+module.exports = ReactNoopServer.default || ReactNoopServer;
diff --git a/packages/react-noop-renderer/src/ReactNoopServer.js b/packages/react-noop-renderer/src/ReactNoopServer.js
new file mode 100644
index 0000000000000..2077687b9f9b1
--- /dev/null
+++ b/packages/react-noop-renderer/src/ReactNoopServer.js
@@ -0,0 +1,49 @@
+/**
+ * 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
+ */
+
+/**
+ * This is a renderer of React that doesn't have a render target output.
+ * It is useful to demonstrate the internals of the reconciler in isolation
+ * and for testing semantics of reconciliation separate from the host
+ * environment.
+ */
+
+import ReactFizzStreamer from 'react-stream';
+
+type Destination = Array;
+
+const ReactNoopServer = ReactFizzStreamer({
+ scheduleWork(callback: () => void) {
+ callback();
+ },
+ beginWriting(destination: Destination): void {},
+ writeChunk(destination: Destination, buffer: Uint8Array): void {
+ destination.push(JSON.parse(Buffer.from((buffer: any)).toString('utf8')));
+ },
+ completeWriting(destination: Destination): void {},
+ close(destination: Destination): void {},
+ flushBuffered(destination: Destination): void {},
+ convertStringToBuffer(content: string): Uint8Array {
+ return Buffer.from(content, 'utf8');
+ },
+ formatChunk(type: string, props: Object): Uint8Array {
+ return Buffer.from(JSON.stringify({type, props}), 'utf8');
+ },
+});
+
+function render(children: React$Element): Destination {
+ let destination: Destination = [];
+ let request = ReactNoopServer.createRequest(children, destination);
+ ReactNoopServer.startWork(request);
+ return destination;
+}
+
+export default {
+ render,
+};
diff --git a/packages/react-reconciler/inline.dom-browser.js b/packages/react-reconciler/inline.dom-browser.js
new file mode 100644
index 0000000000000..0d0c1546cc9a2
--- /dev/null
+++ b/packages/react-reconciler/inline.dom-browser.js
@@ -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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/src/forks/ReactFiberHostConfig.dom-browser.js b/packages/react-reconciler/src/forks/ReactFiberHostConfig.dom-browser.js
new file mode 100644
index 0000000000000..d830c8501be27
--- /dev/null
+++ b/packages/react-reconciler/src/forks/ReactFiberHostConfig.dom-browser.js
@@ -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';
diff --git a/packages/react-stream/README.md b/packages/react-stream/README.md
new file mode 100644
index 0000000000000..087175bb7dc31
--- /dev/null
+++ b/packages/react-stream/README.md
@@ -0,0 +1,25 @@
+# react-stream
+
+This is an experimental package for creating custom React streaming server renderers.
+
+**Its API is not as stable as that of React, React Native, or React DOM, and does not follow the common versioning scheme.**
+
+**Use it at your own risk.**
+
+## API
+
+```js
+var Renderer = require('react-stream');
+
+var HostConfig = {
+ // You'll need to implement some methods here.
+ // See below for more information and examples.
+};
+
+var MyRenderer = Renderer(HostConfig);
+
+var RendererPublicAPI = {
+};
+
+module.exports = RendererPublicAPI;
+```
diff --git a/packages/react-stream/index.js b/packages/react-stream/index.js
new file mode 100644
index 0000000000000..66445f67bf3eb
--- /dev/null
+++ b/packages/react-stream/index.js
@@ -0,0 +1,26 @@
+/**
+ * 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.
+ */
+
+// This entry point is intentionally not typed. It exists only for third-party
+// renderers. The renderers we ship (such as React DOM) instead import a named
+// "inline" entry point (for example, `react-stream/inline.dom`). It uses
+// the same code, but the Flow configuration redirects the host config to its
+// real implementation so we can check it against exact intended host types.
+//
+// Only one renderer (the one you passed to `yarn flow `) is fully
+// type-checked at any given time. The Flow config maps the
+// `react-stream/inline.` import (which is *not* Flow typed) to
+// `react-stream/inline-typed` (which *is*) for the current renderer.
+// On CI, we run Flow checks for each renderer separately.
+
+'use strict';
+
+const ReactFizzStreamer = require('./src/ReactFizzStreamer');
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+module.exports = ReactFizzStreamer.default || ReactFizzStreamer;
diff --git a/packages/react-stream/inline-typed.js b/packages/react-stream/inline-typed.js
new file mode 100644
index 0000000000000..32358e21e457b
--- /dev/null
+++ b/packages/react-stream/inline-typed.js
@@ -0,0 +1,24 @@
+/**
+ * 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
+ */
+
+// This file must have the Flow annotation.
+//
+// This is the Flow-typed entry point for the renderer. It should not be
+// imported directly in code. Instead, our Flow configuration uses this entry
+// point for the currently checked renderer (the one you passed to `yarn flow`).
+//
+// For example, if you run `yarn flow dom`, `react-stream/inline.dom` points
+// to this module (and thus will be considered Flow-typed). But other renderers
+// (e.g. `react-test-renderer`) will see stream as untyped during the check.
+//
+// We can't make all entry points typed at the same time because different
+// renderers have different host config types. So we check them one by one.
+// We run Flow on all renderers on CI.
+
+export * from './src/ReactFizzStreamer';
diff --git a/packages/react-stream/inline.dom-browser.js b/packages/react-stream/inline.dom-browser.js
new file mode 100644
index 0000000000000..9bae815dca122
--- /dev/null
+++ b/packages/react-stream/inline.dom-browser.js
@@ -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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFizzStreamer';
diff --git a/packages/react-stream/inline.dom.js b/packages/react-stream/inline.dom.js
new file mode 100644
index 0000000000000..9bae815dca122
--- /dev/null
+++ b/packages/react-stream/inline.dom.js
@@ -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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFizzStreamer';
diff --git a/packages/react-stream/npm/index.js b/packages/react-stream/npm/index.js
new file mode 100644
index 0000000000000..fb7a252fd61a1
--- /dev/null
+++ b/packages/react-stream/npm/index.js
@@ -0,0 +1,7 @@
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-stream.production.min.js');
+} else {
+ module.exports = require('./cjs/react-stream.development.js');
+}
diff --git a/packages/react-stream/package.json b/packages/react-stream/package.json
new file mode 100644
index 0000000000000..411dab92c019d
--- /dev/null
+++ b/packages/react-stream/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "react-stream",
+ "description": "React package for creating custom streaming server renderers.",
+ "version": "0.1.0",
+ "private": true,
+ "keywords": [
+ "react"
+ ],
+ "homepage": "https://reactjs.org/",
+ "bugs": "https://github.com/facebook/react/issues",
+ "license": "MIT",
+ "files": [
+ "LICENSE",
+ "README.md",
+ "index.js",
+ "cjs/"
+ ],
+ "main": "index.js",
+ "repository": "facebook/react",
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0"
+ },
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2"
+ },
+ "browserify": {
+ "transform": [
+ "loose-envify"
+ ]
+ }
+}
diff --git a/packages/react-stream/src/ReactFizzFormatConfig.js b/packages/react-stream/src/ReactFizzFormatConfig.js
new file mode 100644
index 0000000000000..ae70ca5cda9f6
--- /dev/null
+++ b/packages/react-stream/src/ReactFizzFormatConfig.js
@@ -0,0 +1,20 @@
+/**
+ * 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 invariant from 'shared/invariant';
+
+// We expect that our Rollup, Jest, and Flow configurations
+// always shim this module with the corresponding host config
+// (either provided by a renderer, or a generic shim for npm).
+//
+// We should never resolve to this file, but it exists to make
+// sure that if we *do* accidentally break the configuration,
+// the failure isn't silent.
+
+invariant(false, 'This module must be shimmed by a specific renderer.');
diff --git a/packages/react-stream/src/ReactFizzHostConfig.js b/packages/react-stream/src/ReactFizzHostConfig.js
new file mode 100644
index 0000000000000..ae70ca5cda9f6
--- /dev/null
+++ b/packages/react-stream/src/ReactFizzHostConfig.js
@@ -0,0 +1,20 @@
+/**
+ * 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 invariant from 'shared/invariant';
+
+// We expect that our Rollup, Jest, and Flow configurations
+// always shim this module with the corresponding host config
+// (either provided by a renderer, or a generic shim for npm).
+//
+// We should never resolve to this file, but it exists to make
+// sure that if we *do* accidentally break the configuration,
+// the failure isn't silent.
+
+invariant(false, 'This module must be shimmed by a specific renderer.');
diff --git a/packages/react-stream/src/ReactFizzHostConfigBrowser.js b/packages/react-stream/src/ReactFizzHostConfigBrowser.js
new file mode 100644
index 0000000000000..390a6efc82749
--- /dev/null
+++ b/packages/react-stream/src/ReactFizzHostConfigBrowser.js
@@ -0,0 +1,37 @@
+/**
+ * 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);
+}
diff --git a/packages/react-stream/src/ReactFizzHostConfigNode.js b/packages/react-stream/src/ReactFizzHostConfigNode.js
new file mode 100644
index 0000000000000..2ee9ddec354d9
--- /dev/null
+++ b/packages/react-stream/src/ReactFizzHostConfigNode.js
@@ -0,0 +1,48 @@
+/**
+ * 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 {Writable} from 'stream';
+
+type MightBeFlushable = {flush?: () => void};
+
+export type Destination = Writable & MightBeFlushable;
+
+export function scheduleWork(callback: () => void) {
+ setImmediate(callback);
+}
+
+export function flushBuffered(destination: Destination) {
+ // If we don't have any more data to send right now.
+ // Flush whatever is in the buffer to the wire.
+ if (typeof destination.flush === 'function') {
+ // By convention the Zlib streams provide a flush function for this purpose.
+ destination.flush();
+ }
+}
+
+export function beginWriting(destination: Destination) {
+ destination.cork();
+}
+
+export function writeChunk(destination: Destination, buffer: Uint8Array) {
+ let nodeBuffer = ((buffer: any): Buffer); // close enough
+ destination.write(nodeBuffer);
+}
+
+export function completeWriting(destination: Destination) {
+ destination.uncork();
+}
+
+export function close(destination: Destination) {
+ destination.end();
+}
+
+export function convertStringToBuffer(content: string): Uint8Array {
+ return Buffer.from(content, 'utf8');
+}
diff --git a/packages/react-stream/src/ReactFizzStreamer.js b/packages/react-stream/src/ReactFizzStreamer.js
new file mode 100644
index 0000000000000..80239287c2160
--- /dev/null
+++ b/packages/react-stream/src/ReactFizzStreamer.js
@@ -0,0 +1,85 @@
+/**
+ * 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,
+ 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);
+}
diff --git a/packages/react-stream/src/__tests__/ReactServer-test.js b/packages/react-stream/src/__tests__/ReactServer-test.js
new file mode 100644
index 0000000000000..0c6c13c44f14e
--- /dev/null
+++ b/packages/react-stream/src/__tests__/ReactServer-test.js
@@ -0,0 +1,28 @@
+/**
+ * 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 React;
+let ReactNoopServer;
+
+describe('ReactServer', () => {
+ beforeEach(() => {
+ jest.resetModules();
+
+ React = require('react');
+ ReactNoopServer = require('react-noop-renderer/server');
+ });
+
+ it('can call render', () => {
+ let result = ReactNoopServer.render(hello world
);
+ expect(result).toEqual([{type: 'div', props: {children: 'hello world'}}]);
+ });
+});
diff --git a/packages/react-stream/src/forks/ReactFizzFormatConfig.custom.js b/packages/react-stream/src/forks/ReactFizzFormatConfig.custom.js
new file mode 100644
index 0000000000000..9c4e181652265
--- /dev/null
+++ b/packages/react-stream/src/forks/ReactFizzFormatConfig.custom.js
@@ -0,0 +1,29 @@
+/**
+ * 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
+ */
+
+// This is a host config that's used for the `react-stream` package on npm.
+// It is only used by third-party renderers.
+//
+// Its API lets you pass the host config as an argument.
+// However, inside the `react-stream` we treat host config as a module.
+// This file is a shim between two worlds.
+//
+// It works because the `react-stream` bundle is wrapped in something like:
+//
+// module.exports = function ($$$config) {
+// /* renderer code */
+// }
+//
+// So `$$$config` looks like a global variable, but it's
+// really an argument to a top-level wrapping function.
+
+declare var $$$hostConfig: any;
+export opaque type Destination = mixed; // eslint-disable-line no-undef
+
+export const formatChunk = $$$hostConfig.formatChunk;
diff --git a/packages/react-stream/src/forks/ReactFizzFormatConfig.dom-browser.js b/packages/react-stream/src/forks/ReactFizzFormatConfig.dom-browser.js
new file mode 100644
index 0000000000000..e3b8782adee7c
--- /dev/null
+++ b/packages/react-stream/src/forks/ReactFizzFormatConfig.dom-browser.js
@@ -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/ReactDOMFizzServerFormatConfig';
diff --git a/packages/react-stream/src/forks/ReactFizzFormatConfig.dom.js b/packages/react-stream/src/forks/ReactFizzFormatConfig.dom.js
new file mode 100644
index 0000000000000..e3b8782adee7c
--- /dev/null
+++ b/packages/react-stream/src/forks/ReactFizzFormatConfig.dom.js
@@ -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/ReactDOMFizzServerFormatConfig';
diff --git a/packages/react-stream/src/forks/ReactFizzHostConfig.custom.js b/packages/react-stream/src/forks/ReactFizzHostConfig.custom.js
new file mode 100644
index 0000000000000..71d876a10e5a1
--- /dev/null
+++ b/packages/react-stream/src/forks/ReactFizzHostConfig.custom.js
@@ -0,0 +1,35 @@
+/**
+ * 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
+ */
+
+// This is a host config that's used for the `react-stream` package on npm.
+// It is only used by third-party renderers.
+//
+// Its API lets you pass the host config as an argument.
+// However, inside the `react-stream` we treat host config as a module.
+// This file is a shim between two worlds.
+//
+// It works because the `react-stream` bundle is wrapped in something like:
+//
+// module.exports = function ($$$config) {
+// /* renderer code */
+// }
+//
+// So `$$$config` looks like a global variable, but it's
+// really an argument to a top-level wrapping function.
+
+declare var $$$hostConfig: any;
+export opaque type Destination = mixed; // eslint-disable-line no-undef
+
+export const scheduleWork = $$$hostConfig.scheduleWork;
+export const beginWriting = $$$hostConfig.beginWriting;
+export const writeChunk = $$$hostConfig.writeChunk;
+export const completeWriting = $$$hostConfig.completeWriting;
+export const flushBuffered = $$$hostConfig.flushBuffered;
+export const close = $$$hostConfig.close;
+export const convertStringToBuffer = $$$hostConfig.convertStringToBuffer;
diff --git a/packages/react-stream/src/forks/ReactFizzHostConfig.dom-browser.js b/packages/react-stream/src/forks/ReactFizzHostConfig.dom-browser.js
new file mode 100644
index 0000000000000..ff949e1c746a1
--- /dev/null
+++ b/packages/react-stream/src/forks/ReactFizzHostConfig.dom-browser.js
@@ -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 '../ReactFizzHostConfigBrowser';
diff --git a/packages/react-stream/src/forks/ReactFizzHostConfig.dom.js b/packages/react-stream/src/forks/ReactFizzHostConfig.dom.js
new file mode 100644
index 0000000000000..88d44bc2e4519
--- /dev/null
+++ b/packages/react-stream/src/forks/ReactFizzHostConfig.dom.js
@@ -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 '../ReactFizzHostConfigNode';
diff --git a/scripts/flow/createFlowConfigs.js b/scripts/flow/createFlowConfigs.js
index d02ba58c9b511..f1ca3523c77d8 100644
--- a/scripts/flow/createFlowConfigs.js
+++ b/scripts/flow/createFlowConfigs.js
@@ -18,15 +18,19 @@ const configTemplate = fs
.readFileSync(__dirname + '/config/flowconfig')
.toString();
-function writeConfig(renderer) {
+function writeConfig(renderer, isFizzSupported) {
const folder = __dirname + '/' + renderer;
mkdirp.sync(folder);
+ const fizzRenderer = isFizzSupported ? renderer : 'custom';
const config = configTemplate.replace(
'%REACT_RENDERER_FLOW_OPTIONS%',
`
module.name_mapper='react-reconciler/inline.${renderer}$$' -> 'react-reconciler/inline-typed'
module.name_mapper='ReactFiberHostConfig$$' -> 'forks/ReactFiberHostConfig.${renderer}'
+module.name_mapper='react-stream/inline.${renderer}$$' -> 'react-stream/inline-typed'
+module.name_mapper='ReactFizzHostConfig$$' -> 'forks/ReactFizzHostConfig.${fizzRenderer}'
+module.name_mapper='ReactFizzFormatConfig$$' -> 'forks/ReactFizzFormatConfig.${fizzRenderer}'
`.trim(),
);
@@ -61,6 +65,6 @@ ${disclaimer}
// so that we can run those checks in parallel if we want.
inlinedHostConfigs.forEach(rendererInfo => {
if (rendererInfo.isFlowTyped) {
- writeConfig(rendererInfo.shortName);
+ writeConfig(rendererInfo.shortName, rendererInfo.isFizzSupported);
}
});
diff --git a/scripts/jest/setupHostConfigs.js b/scripts/jest/setupHostConfigs.js
index 888fade848196..d7f4aab36741e 100644
--- a/scripts/jest/setupHostConfigs.js
+++ b/scripts/jest/setupHostConfigs.js
@@ -17,6 +17,15 @@ jest.mock('react-reconciler/persistent', () => {
return require.requireActual('react-reconciler/persistent');
};
});
+const shimFizzHostConfigPath = 'react-stream/src/ReactFizzHostConfig';
+const shimFizzFormatConfigPath = 'react-stream/src/ReactFizzFormatConfig';
+jest.mock('react-stream', () => {
+ return config => {
+ jest.mock(shimFizzHostConfigPath, () => config);
+ jest.mock(shimFizzFormatConfigPath, () => config);
+ return require.requireActual('react-stream');
+ };
+});
// But for inlined host configs (such as React DOM, Native, etc), we
// mock their named entry points to establish a host config mapping.
@@ -55,6 +64,46 @@ inlinedHostConfigs.forEach(rendererInfo => {
return renderer;
});
+
+ if (rendererInfo.isFizzSupported) {
+ jest.mock(`react-stream/inline.${rendererInfo.shortName}`, () => {
+ let hasImportedShimmedConfig = false;
+
+ // We want the renderer to pick up the host config for this renderer.
+ jest.mock(shimFizzHostConfigPath, () => {
+ hasImportedShimmedConfig = true;
+ return require.requireActual(
+ `react-stream/src/forks/ReactFizzHostConfig.${
+ rendererInfo.shortName
+ }.js`
+ );
+ });
+ jest.mock(shimFizzFormatConfigPath, () => {
+ hasImportedShimmedConfig = true;
+ return require.requireActual(
+ `react-stream/src/forks/ReactFizzFormatConfig.${
+ rendererInfo.shortName
+ }.js`
+ );
+ });
+
+ const renderer = require.requireActual('react-stream');
+ // If the shimmed config factory function above has not run,
+ // it means this test file loads more than one renderer
+ // but doesn't reset modules between them. This won't work.
+ if (!hasImportedShimmedConfig) {
+ throw new Error(
+ `Could not import the "${rendererInfo.shortName}" renderer ` +
+ `in this suite because another renderer has already been ` +
+ `loaded earlier. Call jest.resetModules() before importing any ` +
+ `of the following entry points:\n\n` +
+ rendererInfo.entryPoints.map(entry => ` * ${entry}`)
+ );
+ }
+
+ return renderer;
+ });
+ }
});
// Make it possible to import this module inside
diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js
index c250600b99280..b599ea4055407 100644
--- a/scripts/rollup/bundles.js
+++ b/scripts/rollup/bundles.js
@@ -158,6 +158,22 @@ const bundles = [
externals: ['react', 'stream'],
},
+ /******* React DOM Fizz Server *******/
+ {
+ bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD],
+ moduleType: RENDERER,
+ entry: 'react-dom/unstable-fizz.browser',
+ global: 'ReactDOMFizzServer',
+ externals: ['react'],
+ },
+ {
+ bundleTypes: [NODE_DEV, NODE_PROD, FB_WWW_DEV, FB_WWW_PROD],
+ moduleType: RENDERER,
+ entry: 'react-dom/unstable-fizz.node',
+ global: 'ReactDOMFizzServer',
+ externals: ['react'],
+ },
+
/******* React ART *******/
{
bundleTypes: [
@@ -320,6 +336,28 @@ const bundles = [
}),
},
+ /******* React Noop Server Renderer (used for tests) *******/
+ {
+ bundleTypes: [NODE_DEV, NODE_PROD],
+ moduleType: RENDERER,
+ entry: 'react-noop-renderer/server',
+ global: 'ReactNoopRendererServer',
+ externals: ['react', 'expect'],
+ // React Noop uses generators. However GCC currently
+ // breaks when we attempt to use them in the output.
+ // So we precompile them with regenerator, and include
+ // it as a runtime dependency of React Noop. In practice
+ // this isn't an issue because React Noop is only used
+ // in our tests. We wouldn't want to do this for any
+ // public package though.
+ babel: opts =>
+ Object.assign({}, opts, {
+ plugins: opts.plugins.concat([
+ require.resolve('babel-plugin-transform-regenerator'),
+ ]),
+ }),
+ },
+
/******* React Reconciler *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
@@ -338,6 +376,15 @@ const bundles = [
externals: ['react'],
},
+ /******* React Stream *******/
+ {
+ bundleTypes: [NODE_DEV, NODE_PROD],
+ moduleType: RECONCILER,
+ entry: 'react-stream',
+ global: 'ReactStream',
+ externals: ['react'],
+ },
+
/******* Reflection *******/
{
moduleType: RENDERER_UTILS,
diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js
index bbb22d7a8c950..a6fa873078eb4 100644
--- a/scripts/rollup/forks.js
+++ b/scripts/rollup/forks.js
@@ -272,6 +272,66 @@ const forks = Object.freeze({
);
},
+ 'react-stream/src/ReactFizzHostConfig': (
+ bundleType,
+ entry,
+ dependencies,
+ moduleType
+ ) => {
+ if (dependencies.indexOf('react-stream') !== -1) {
+ return null;
+ }
+ if (moduleType !== RENDERER && moduleType !== RECONCILER) {
+ return null;
+ }
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
+ for (let rendererInfo of inlinedHostConfigs) {
+ if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
+ if (!rendererInfo.isFizzSupported) {
+ return null;
+ }
+ return `react-stream/src/forks/ReactFizzHostConfig.${
+ rendererInfo.shortName
+ }.js`;
+ }
+ }
+ throw new Error(
+ 'Expected ReactFizzHostConfig 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?'
+ );
+ },
+
+ 'react-stream/src/ReactFizzFormatConfig': (
+ bundleType,
+ entry,
+ dependencies,
+ moduleType
+ ) => {
+ if (dependencies.indexOf('react-stream') !== -1) {
+ return null;
+ }
+ if (moduleType !== RENDERER && moduleType !== RECONCILER) {
+ return null;
+ }
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
+ for (let rendererInfo of inlinedHostConfigs) {
+ if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
+ if (!rendererInfo.isFizzSupported) {
+ return null;
+ }
+ return `react-stream/src/forks/ReactFizzFormatConfig.${
+ rendererInfo.shortName
+ }.js`;
+ }
+ }
+ throw new Error(
+ 'Expected ReactFizzFormatConfig 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?'
+ );
+ },
+
// We wrap top-level listeners into guards on www.
'react-dom/src/events/EventListener': (bundleType, entry) => {
switch (bundleType) {
diff --git a/scripts/rollup/results.json b/scripts/rollup/results.json
index dabd5feedda2f..91d4f827bac0d 100644
--- a/scripts/rollup/results.json
+++ b/scripts/rollup/results.json
@@ -46,29 +46,29 @@
"filename": "react-dom.development.js",
"bundleType": "UMD_DEV",
"packageName": "react-dom",
- "size": 725515,
- "gzip": 167737
+ "size": 725813,
+ "gzip": 167799
},
{
"filename": "react-dom.production.min.js",
"bundleType": "UMD_PROD",
"packageName": "react-dom",
- "size": 100307,
- "gzip": 32617
+ "size": 99920,
+ "gzip": 32521
},
{
"filename": "react-dom.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-dom",
- "size": 720713,
- "gzip": 166331
+ "size": 721011,
+ "gzip": 166399
},
{
"filename": "react-dom.production.min.js",
"bundleType": "NODE_PROD",
"packageName": "react-dom",
- "size": 100301,
- "gzip": 32146
+ "size": 99914,
+ "gzip": 32052
},
{
"filename": "ReactDOM-dev.js",
@@ -165,8 +165,8 @@
"filename": "react-dom-server.browser.development.js",
"bundleType": "UMD_DEV",
"packageName": "react-dom",
- "size": 120524,
- "gzip": 32011
+ "size": 122056,
+ "gzip": 32469
},
{
"filename": "react-dom-server.browser.production.min.js",
@@ -179,8 +179,8 @@
"filename": "react-dom-server.browser.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-dom",
- "size": 116562,
- "gzip": 31037
+ "size": 118094,
+ "gzip": 31495
},
{
"filename": "react-dom-server.browser.production.min.js",
@@ -207,8 +207,8 @@
"filename": "react-dom-server.node.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-dom",
- "size": 118530,
- "gzip": 31584
+ "size": 120062,
+ "gzip": 32036
},
{
"filename": "react-dom-server.node.production.min.js",
@@ -221,29 +221,29 @@
"filename": "react-art.development.js",
"bundleType": "UMD_DEV",
"packageName": "react-art",
- "size": 507548,
- "gzip": 112066
+ "size": 507846,
+ "gzip": 112120
},
{
"filename": "react-art.production.min.js",
"bundleType": "UMD_PROD",
"packageName": "react-art",
- "size": 92284,
- "gzip": 28330
+ "size": 91897,
+ "gzip": 28219
},
{
"filename": "react-art.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-art",
- "size": 437685,
- "gzip": 94610
+ "size": 437983,
+ "gzip": 94680
},
{
"filename": "react-art.production.min.js",
"bundleType": "NODE_PROD",
"packageName": "react-art",
- "size": 56431,
- "gzip": 17393
+ "size": 56044,
+ "gzip": 17298
},
{
"filename": "ReactART-dev.js",
@@ -291,29 +291,29 @@
"filename": "react-test-renderer.development.js",
"bundleType": "UMD_DEV",
"packageName": "react-test-renderer",
- "size": 450653,
- "gzip": 97378
+ "size": 450951,
+ "gzip": 97435
},
{
"filename": "react-test-renderer.production.min.js",
"bundleType": "UMD_PROD",
"packageName": "react-test-renderer",
- "size": 57674,
- "gzip": 17698
+ "size": 57293,
+ "gzip": 17617
},
{
"filename": "react-test-renderer.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-test-renderer",
- "size": 445754,
- "gzip": 96206
+ "size": 446052,
+ "gzip": 96263
},
{
"filename": "react-test-renderer.production.min.js",
"bundleType": "NODE_PROD",
"packageName": "react-test-renderer",
- "size": 57342,
- "gzip": 17539
+ "size": 56955,
+ "gzip": 17453
},
{
"filename": "ReactTestRenderer-dev.js",
@@ -375,29 +375,29 @@
"filename": "react-reconciler.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-reconciler",
- "size": 435479,
- "gzip": 93068
+ "size": 435777,
+ "gzip": 93120
},
{
"filename": "react-reconciler.production.min.js",
"bundleType": "NODE_PROD",
"packageName": "react-reconciler",
- "size": 57593,
- "gzip": 17242
+ "size": 57202,
+ "gzip": 17158
},
{
"filename": "react-reconciler-persistent.development.js",
"bundleType": "NODE_DEV",
"packageName": "react-reconciler",
- "size": 433889,
- "gzip": 92426
+ "size": 434187,
+ "gzip": 92481
},
{
"filename": "react-reconciler-persistent.production.min.js",
"bundleType": "NODE_PROD",
"packageName": "react-reconciler",
- "size": 57604,
- "gzip": 17248
+ "size": 57213,
+ "gzip": 17164
},
{
"filename": "react-reconciler-reflection.development.js",
@@ -515,15 +515,15 @@
"filename": "ReactDOM-dev.js",
"bundleType": "FB_WWW_DEV",
"packageName": "react-dom",
- "size": 742105,
- "gzip": 167549
+ "size": 742241,
+ "gzip": 167544
},
{
"filename": "ReactDOM-prod.js",
"bundleType": "FB_WWW_PROD",
"packageName": "react-dom",
- "size": 318358,
- "gzip": 58562
+ "size": 317018,
+ "gzip": 58396
},
{
"filename": "ReactTestUtils-dev.js",
@@ -550,8 +550,8 @@
"filename": "ReactDOMServer-dev.js",
"bundleType": "FB_WWW_DEV",
"packageName": "react-dom",
- "size": 117649,
- "gzip": 30639
+ "size": 119227,
+ "gzip": 31112
},
{
"filename": "ReactDOMServer-prod.js",
@@ -564,78 +564,78 @@
"filename": "ReactART-dev.js",
"bundleType": "FB_WWW_DEV",
"packageName": "react-art",
- "size": 445159,
- "gzip": 93560
+ "size": 445295,
+ "gzip": 93558
},
{
"filename": "ReactART-prod.js",
"bundleType": "FB_WWW_PROD",
"packageName": "react-art",
- "size": 189135,
- "gzip": 32266
+ "size": 187912,
+ "gzip": 32090
},
{
"filename": "ReactNativeRenderer-dev.js",
"bundleType": "RN_FB_DEV",
"packageName": "react-native-renderer",
- "size": 573357,
- "gzip": 124963
+ "size": 573493,
+ "gzip": 124971
},
{
"filename": "ReactNativeRenderer-prod.js",
"bundleType": "RN_FB_PROD",
"packageName": "react-native-renderer",
- "size": 245987,
- "gzip": 43227
+ "size": 244566,
+ "gzip": 43021
},
{
"filename": "ReactNativeRenderer-dev.js",
"bundleType": "RN_OSS_DEV",
"packageName": "react-native-renderer",
- "size": 573046,
- "gzip": 124866
+ "size": 573182,
+ "gzip": 124874
},
{
"filename": "ReactNativeRenderer-prod.js",
"bundleType": "RN_OSS_PROD",
"packageName": "react-native-renderer",
- "size": 231080,
- "gzip": 40077
+ "size": 229659,
+ "gzip": 39880
},
{
"filename": "ReactFabric-dev.js",
"bundleType": "RN_FB_DEV",
"packageName": "react-native-renderer",
- "size": 563440,
- "gzip": 122415
+ "size": 563576,
+ "gzip": 122418
},
{
"filename": "ReactFabric-prod.js",
"bundleType": "RN_FB_PROD",
"packageName": "react-native-renderer",
- "size": 225188,
- "gzip": 38730
+ "size": 223730,
+ "gzip": 38540
},
{
"filename": "ReactFabric-dev.js",
"bundleType": "RN_OSS_DEV",
"packageName": "react-native-renderer",
- "size": 563475,
- "gzip": 122429
+ "size": 563611,
+ "gzip": 122433
},
{
"filename": "ReactFabric-prod.js",
"bundleType": "RN_OSS_PROD",
"packageName": "react-native-renderer",
- "size": 225224,
- "gzip": 38745
+ "size": 223766,
+ "gzip": 38555
},
{
"filename": "ReactTestRenderer-dev.js",
"bundleType": "FB_WWW_DEV",
"packageName": "react-test-renderer",
- "size": 453421,
- "gzip": 95442
+ "size": 453557,
+ "gzip": 95443
},
{
"filename": "ReactShallowRenderer-dev.js",
@@ -718,22 +718,22 @@
"filename": "react-dom.profiling.min.js",
"bundleType": "NODE_PROFILING",
"packageName": "react-dom",
- "size": 103137,
- "gzip": 32687
+ "size": 102985,
+ "gzip": 32673
},
{
"filename": "ReactNativeRenderer-profiling.js",
"bundleType": "RN_OSS_PROFILING",
"packageName": "react-native-renderer",
- "size": 236392,
- "gzip": 41298
+ "size": 235342,
+ "gzip": 41248
},
{
"filename": "ReactFabric-profiling.js",
"bundleType": "RN_OSS_PROFILING",
"packageName": "react-native-renderer",
- "size": 229595,
- "gzip": 40005
+ "size": 228530,
+ "gzip": 39962
},
{
"filename": "Scheduler-dev.js",
@@ -767,22 +767,22 @@
"filename": "ReactDOM-profiling.js",
"bundleType": "FB_WWW_PROFILING",
"packageName": "react-dom",
- "size": 324711,
- "gzip": 59847
+ "size": 323954,
+ "gzip": 59824
},
{
"filename": "ReactNativeRenderer-profiling.js",
"bundleType": "RN_FB_PROFILING",
"packageName": "react-native-renderer",
- "size": 251555,
- "gzip": 44455
+ "size": 250505,
+ "gzip": 44397
},
{
"filename": "ReactFabric-profiling.js",
"bundleType": "RN_FB_PROFILING",
"packageName": "react-native-renderer",
- "size": 229554,
- "gzip": 39988
+ "size": 228489,
+ "gzip": 39945
},
{
"filename": "react.profiling.min.js",
@@ -795,8 +795,8 @@
"filename": "react-dom.profiling.min.js",
"bundleType": "UMD_PROFILING",
"packageName": "react-dom",
- "size": 103046,
- "gzip": 33256
+ "size": 102894,
+ "gzip": 33243
},
{
"filename": "scheduler-tracing.development.js",
@@ -937,6 +937,90 @@
"packageName": "eslint-plugin-react-hooks",
"size": 4943,
"gzip": 1815
+ },
+ {
+ "filename": "ReactDOMFizzServer-dev.js",
+ "bundleType": "FB_WWW_DEV",
+ "packageName": "react-dom",
+ "size": 3772,
+ "gzip": 1432
+ },
+ {
+ "filename": "ReactDOMFizzServer-prod.js",
+ "bundleType": "FB_WWW_PROD",
+ "packageName": "react-dom",
+ "size": 2211,
+ "gzip": 874
+ },
+ {
+ "filename": "react-noop-renderer-server.development.js",
+ "bundleType": "NODE_DEV",
+ "packageName": "react-noop-renderer",
+ "size": 1861,
+ "gzip": 869
+ },
+ {
+ "filename": "react-noop-renderer-server.production.min.js",
+ "bundleType": "NODE_PROD",
+ "packageName": "react-noop-renderer",
+ "size": 804,
+ "gzip": 482
+ },
+ {
+ "filename": "react-stream.development.js",
+ "bundleType": "NODE_DEV",
+ "packageName": "react-stream",
+ "size": 4633,
+ "gzip": 1683
+ },
+ {
+ "filename": "react-stream.production.min.js",
+ "bundleType": "NODE_PROD",
+ "packageName": "react-stream",
+ "size": 1224,
+ "gzip": 655
+ },
+ {
+ "filename": "react-dom-unstable-fizz.browser.development.js",
+ "bundleType": "UMD_DEV",
+ "packageName": "react-dom",
+ "size": 3704,
+ "gzip": 1463
+ },
+ {
+ "filename": "react-dom-unstable-fizz.browser.production.min.js",
+ "bundleType": "UMD_PROD",
+ "packageName": "react-dom",
+ "size": 1227,
+ "gzip": 697
+ },
+ {
+ "filename": "react-dom-unstable-fizz.browser.development.js",
+ "bundleType": "NODE_DEV",
+ "packageName": "react-dom",
+ "size": 3528,
+ "gzip": 1416
+ },
+ {
+ "filename": "react-dom-unstable-fizz.browser.production.min.js",
+ "bundleType": "NODE_PROD",
+ "packageName": "react-dom",
+ "size": 1063,
+ "gzip": 628
+ },
+ {
+ "filename": "react-dom-unstable-fizz.node.development.js",
+ "bundleType": "NODE_DEV",
+ "packageName": "react-dom",
+ "size": 3780,
+ "gzip": 1443
+ },
+ {
+ "filename": "react-dom-unstable-fizz.node.production.min.js",
+ "bundleType": "NODE_PROD",
+ "packageName": "react-dom",
+ "size": 1122,
+ "gzip": 659
}
]
}
\ No newline at end of file
diff --git a/scripts/rollup/validate/eslintrc.cjs.js b/scripts/rollup/validate/eslintrc.cjs.js
index a1b4a7fdfac44..e23b7c68fce15 100644
--- a/scripts/rollup/validate/eslintrc.cjs.js
+++ b/scripts/rollup/validate/eslintrc.cjs.js
@@ -18,6 +18,8 @@ module.exports = {
__REACT_DEVTOOLS_GLOBAL_HOOK__: true,
// CommonJS / Node
process: true,
+ setImmediate: true,
+ Buffer: true,
},
parserOptions: {
ecmaVersion: 5,
diff --git a/scripts/rollup/validate/eslintrc.fb.js b/scripts/rollup/validate/eslintrc.fb.js
index 2e2949444f5da..878a170a31b5e 100644
--- a/scripts/rollup/validate/eslintrc.fb.js
+++ b/scripts/rollup/validate/eslintrc.fb.js
@@ -18,6 +18,9 @@ module.exports = {
__REACT_DEVTOOLS_GLOBAL_HOOK__: true,
// FB
__DEV__: true,
+ // Node.js Server Rendering
+ setImmediate: true,
+ Buffer: true,
},
parserOptions: {
ecmaVersion: 5,
diff --git a/scripts/shared/inlinedHostConfigs.js b/scripts/shared/inlinedHostConfigs.js
index 2892a9abeb06b..66c54963c7f28 100644
--- a/scripts/shared/inlinedHostConfigs.js
+++ b/scripts/shared/inlinedHostConfigs.js
@@ -9,8 +9,15 @@
module.exports = [
{
shortName: 'dom',
- entryPoints: ['react-dom'],
+ entryPoints: ['react-dom', 'react-dom/unstable-fizz.node'],
isFlowTyped: true,
+ isFizzSupported: true,
+ },
+ {
+ shortName: 'dom-browser',
+ entryPoints: ['react-dom/unstable-fizz.browser'],
+ isFlowTyped: true,
+ isFizzSupported: true,
},
{
shortName: 'fire',
@@ -21,25 +28,34 @@ module.exports = [
shortName: 'art',
entryPoints: ['react-art'],
isFlowTyped: false, // TODO: type it.
+ isFizzSupported: false,
},
{
shortName: 'native',
entryPoints: ['react-native-renderer'],
isFlowTyped: true,
+ isFizzSupported: false,
},
{
shortName: 'fabric',
entryPoints: ['react-native-renderer/fabric'],
isFlowTyped: true,
+ isFizzSupported: false,
},
{
shortName: 'test',
entryPoints: ['react-test-renderer'],
isFlowTyped: true,
+ isFizzSupported: false,
},
{
shortName: 'custom',
- entryPoints: ['react-reconciler', 'react-reconciler/persistent'],
+ entryPoints: [
+ 'react-reconciler',
+ 'react-reconciler/persistent',
+ 'react-stream',
+ ],
isFlowTyped: true,
+ isFizzSupported: true,
},
];
diff --git a/yarn.lock b/yarn.lock
index c9ffffa9fd9c8..5bb72e40fd3aa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -94,6 +94,18 @@
lodash "^4.17.10"
to-fast-properties "^2.0.0"
+"@mattiasbuelens/web-streams-polyfill@0.1.0":
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/@mattiasbuelens/web-streams-polyfill/-/web-streams-polyfill-0.1.0.tgz#c06ebfa7e00cc512a878c3aaae4cf113ac89ac24"
+ integrity sha512-oMsvblvOezdM/j1ph0uU8s6Wm0EfCyMZtZcxQ232CqSpjm08lrKPizeMltN0eVv4dQf0DDFaxUFyiz8x51lgAA==
+ dependencies:
+ "@types/whatwg-streams" "^0.0.6"
+
+"@types/whatwg-streams@^0.0.6":
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/@types/whatwg-streams/-/whatwg-streams-0.0.6.tgz#5062c67efb695c886fe3dbb9618df35aac418503"
+ integrity sha512-O4Hat94N1RUCObqAbVUtd6EcucseqBcpfbFXzy12CYF6BQVHWR+ztDA3YPjewCmdKHYZ5VA7TZ5hq2bMyqxiBw==
+
abab@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"