@samitouri / QOS-React-2 / commits / fadc97167f

[Flight] Add Client Infrastructure (#17234)

* Change demo to server * Expose client in package.json * Reorganize tests We don't want unit tests but instead test how both server and clients work together. So this merges server/client test files. * Fill in the client implementation a bit * Use new client in fixture * Add Promise/Uint8Array to lint rule I'll probably end up deleting these deps later but they're here for now.

Sebastian Markbåge committed Nov 1, 2019 at 16:05 UTC fadc97167f8d2387dfc2c7b2493ba9ffbbb35fcf
17 files changed +239 -377
fixtures/flight-browser/index.html
+22 -19
@@ -18,9 +18,12 @@
18 </div>
19 <script src="../../build/dist/react.development.js"></script>
20 <script src="../../build/dist/react-dom.development.js"></script>
21 + <script src="../../build/dist/react-dom-unstable-flight-server.browser.development.js"></script>
22 <script src="../../build/dist/react-dom-unstable-flight-client.development.js"></script>
23 <script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
24 <script type="text/babel">
25 + let Suspense = React.Suspense;
26 +
27 function Text({children}) {
28 return <span>{children}</span>;
29 }
@@ -40,7 +43,7 @@
43 }
44 };
45
43 - let stream = ReactFlightDOMClient.renderToReadableStream(model);
46 + let stream = ReactFlightDOMServer.renderToReadableStream(model);
47 let response = new Response(stream, {
48 headers: {'Content-Type': 'text/html'},
49 });
@@ -49,35 +52,35 @@
52 async function display(responseToDisplay) {
53 let blob = await responseToDisplay.blob();
54 let url = URL.createObjectURL(blob);
52 - let response = await fetch(url);
53 - let body = await response.body;
55
55 - let reader = body.getReader();
56 - let charsReceived = 0;
57 - let decoder = new TextDecoder();
56 + let data = ReactFlightDOMClient.readFromFetch(
57 + fetch(url)
58 + );
59 + // The client also supports XHR streaming.
60 + // var xhr = new XMLHttpRequest();
61 + // xhr.open('GET', url);
62 + // let data = ReactFlightDOMClient.readFromXHR(xhr);
63 + // xhr.send();
64
59 - let json = '';
60 - reader.read().then(function processChunk({ done, value }) {
61 - if (done) {
62 - renderResult(json);
63 - return;
64 - }
65 - json += decoder.decode(value);
66 - return reader.read().then(processChunk);
67 - });
65 + renderResult(data);
66 }
67
70 - function Shell({ model }) {
68 + function Shell({ data }) {
69 + let model = data.model;
70 return <div>
71 <h1>{model.title}</h1>
72 <div dangerouslySetInnerHTML={model.content} />
73 </div>;
74 }
75
77 - function renderResult(json) {
78 - let model = JSON.parse(json);
76 + function renderResult(data) {
77 let container = document.getElementById('container');
80 - ReactDOM.render(<Shell model={model} />, container);
78 + ReactDOM.render(
79 + <Suspense fallback="Loading...">
80 + <Shell data={data} />
81 + </Suspense>,
82 + container
83 + );
84 }
85 </script>
86 </body>
packages/react-dom/package.json
+1
@@ -39,6 +39,7 @@
39 "unstable-fizz.js",
40 "unstable-fizz.browser.js",
41 "unstable-fizz.node.js",
42 + "unstable-flight-client.js",
43 "unstable-flight-server.js",
44 "unstable-flight-server.browser.js",
45 "unstable-flight-server.node.js",
packages/react-dom/src/__tests__/ReactFlightDOMBrowser-test.js renamed
packages/react-dom/src/__tests__/ReactFlightDOMNode-test.js renamed
packages/react-dom/src/client/flight/ReactFlightDOMClient.js
+68 -16
@@ -7,28 +7,80 @@
7 * @flow
8 */
9
10 -import type {ReactModel} from 'react-flight/src/ReactFlightClient';
10 +import type {ReactModelRoot} from 'react-flight/src/ReactFlightClient';
11
12 import {
13 - createRequest,
14 - startWork,
15 - startFlowing,
16 -} from 'react-flight/inline.dom-browser';
13 + createResponse,
14 + getModelRoot,
15 + reportGlobalError,
16 + processStringChunk,
17 + processBinaryChunk,
18 + complete,
19 +} from 'react-flight/inline.dom';
20
18 -function renderToReadableStream(model: ReactModel): ReadableStream {
19 - let request;
20 - return new ReadableStream({
21 - start(controller) {
22 - request = createRequest(model, controller);
23 - startWork(request);
21 +function startReadingFromStream(response, stream: ReadableStream): void {
22 + let reader = stream.getReader();
23 + function progress({done, value}) {
24 + if (done) {
25 + complete(response);
26 + return;
27 + }
28 + let buffer: Uint8Array = (value: any);
29 + processBinaryChunk(response, buffer, 0);
30 + return reader.read().then(progress, error);
31 + }
32 + function error(e) {
33 + reportGlobalError(response, e);
34 + }
35 + reader.read().then(progress, error);
36 +}
37 +
38 +function readFromReadableStream<T>(stream: ReadableStream): ReactModelRoot<T> {
39 + let response = createResponse(stream);
40 + startReadingFromStream(response, stream);
41 + return getModelRoot(response);
42 +}
43 +
44 +function readFromFetch<T>(
45 + promiseForResponse: Promise<Response>,
46 +): ReactModelRoot<T> {
47 + let response = createResponse(promiseForResponse);
48 + promiseForResponse.then(
49 + function(r) {
50 + startReadingFromStream(response, (r.body: any));
51 },
25 - pull(controller) {
26 - startFlowing(request, controller.desiredSize);
52 + function(e) {
53 + reportGlobalError(response, e);
54 },
28 - cancel(reason) {},
29 - });
55 + );
56 + return getModelRoot(response);
57 +}
58 +
59 +function readFromXHR<T>(request: XMLHttpRequest): ReactModelRoot<T> {
60 + let response = createResponse(request);
61 + let processedLength = 0;
62 + function progress(e: ProgressEvent): void {
63 + let chunk = request.responseText;
64 + processStringChunk(response, chunk, processedLength);
65 + processedLength = chunk.length;
66 + }
67 + function load(e: ProgressEvent): void {
68 + progress(e);
69 + complete(response);
70 + }
71 + function error(e: ProgressEvent): void {
72 + reportGlobalError(response, new TypeError('Network error'));
73 + }
74 + request.addEventListener('progress', progress);
75 + request.addEventListener('load', load);
76 + request.addEventListener('error', error);
77 + request.addEventListener('abort', error);
78 + request.addEventListener('timeout', error);
79 + return getModelRoot(response);
80 }
81
82 export default {
33 - renderToReadableStream,
83 + readFromXHR,
84 + readFromFetch,
85 + readFromReadableStream,
86 };
packages/react-dom/src/client/flight/ReactFlightDOMHostConfig.js
+16 -32
@@ -7,44 +7,28 @@
7 * @flow
8 */
9
10 -export type Destination = ReadableStreamController;
10 +export type Source = Promise<Response> | ReadableStream | XMLHttpRequest;
11
12 -export function scheduleWork(callback: () => void) {
13 - callback();
14 -}
15 -
16 -export function flushBuffered(destination: Destination) {
17 - // WHATWG Streams do not yet have a way to flush the underlying
18 - // transform streams. https://github.com/whatwg/streams/issues/960
19 -}
20 -
21 -export function beginWriting(destination: Destination) {}
12 +export type StringDecoder = TextDecoder;
13
23 -export function writeChunk(destination: Destination, buffer: Uint8Array) {
24 - destination.enqueue(buffer);
25 -}
26 -
27 -export function completeWriting(destination: Destination) {}
14 +export const supportsBinaryStreams = true;
15
29 -export function close(destination: Destination) {
30 - destination.close();
16 +export function createStringDecoder(): StringDecoder {
17 + return new TextDecoder();
18 }
19
33 -const textEncoder = new TextEncoder();
34 -
35 -export function convertStringToBuffer(content: string): Uint8Array {
36 - return textEncoder.encode(content);
37 -}
20 +const decoderOptions = {stream: true};
21
39 -export function formatChunkAsString(type: string, props: Object): string {
40 - let str = '<' + type + '>';
41 - if (typeof props.children === 'string') {
42 - str += props.children;
43 - }
44 - str += '</' + type + '>';
45 - return str;
22 +export function readPartialStringChunk(
23 + decoder: StringDecoder,
24 + buffer: Uint8Array,
25 +): string {
26 + return decoder.decode(buffer, decoderOptions);
27 }
28
48 -export function formatChunk(type: string, props: Object): Uint8Array {
49 - return convertStringToBuffer(formatChunkAsString(type, props));
29 +export function readFinalStringChunk(
30 + decoder: StringDecoder,
31 + buffer: Uint8Array,
32 +): string {
33 + return decoder.decode(buffer);
34 }
packages/react-dom/src/server/flight/__tests__/ReactFlightDOMBrowser-test.js deleted
-61
@@ -1,61 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -// Polyfills for test environment
13 -global.ReadableStream = require('@mattiasbuelens/web-streams-polyfill/ponyfill/es6').ReadableStream;
14 -global.TextEncoder = require('util').TextEncoder;
15 -
16 -let React;
17 -let ReactFlightDOMServer;
18 -
19 -describe('ReactFlightDOM', () => {
20 - beforeEach(() => {
21 - jest.resetModules();
22 - React = require('react');
23 - ReactFlightDOMServer = require('react-dom/unstable-flight-server.browser');
24 - });
25 -
26 - async function readResult(stream) {
27 - let reader = stream.getReader();
28 - let result = '';
29 - while (true) {
30 - let {done, value} = await reader.read();
31 - if (done) {
32 - return result;
33 - }
34 - result += Buffer.from(value).toString('utf8');
35 - }
36 - }
37 -
38 - it('should resolve HTML', async () => {
39 - function Text({children}) {
40 - return <span>{children}</span>;
41 - }
42 - function HTML() {
43 - return (
44 - <div>
45 - <Text>hello</Text>
46 - <Text>world</Text>
47 - </div>
48 - );
49 - }
50 -
51 - let model = {
52 - html: <HTML />,
53 - };
54 - let stream = ReactFlightDOMServer.renderToReadableStream(model);
55 - jest.runAllTimers();
56 - let result = JSON.parse(await readResult(stream));
57 - expect(result).toEqual({
58 - html: '<div><span>hello</span><span>world</span></div>',
59 - });
60 - });
61 -});
packages/react-dom/src/server/flight/__tests__/ReactFlightDOMNode-test.js deleted
-57
@@ -1,57 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - * @jest-environment node
9 - */
10 -
11 -'use strict';
12 -
13 -let Stream;
14 -let React;
15 -let ReactFlightDOMServer;
16 -
17 -describe('ReactFlightDOM', () => {
18 - beforeEach(() => {
19 - jest.resetModules();
20 - React = require('react');
21 - ReactFlightDOMServer = require('react-dom/unstable-flight-server');
22 - Stream = require('stream');
23 - });
24 -
25 - function getTestWritable() {
26 - let writable = new Stream.PassThrough();
27 - writable.setEncoding('utf8');
28 - writable.result = '';
29 - writable.on('data', chunk => (writable.result += chunk));
30 - return writable;
31 - }
32 -
33 - it('should resolve HTML', () => {
34 - function Text({children}) {
35 - return <span>{children}</span>;
36 - }
37 - function HTML() {
38 - return (
39 - <div>
40 - <Text>hello</Text>
41 - <Text>world</Text>
42 - </div>
43 - );
44 - }
45 -
46 - let writable = getTestWritable();
47 - let model = {
48 - html: <HTML />,
49 - };
50 - ReactFlightDOMServer.pipeToNodeWritable(model, writable);
51 - jest.runAllTimers();
52 - let result = JSON.parse(writable.result);
53 - expect(result).toEqual({
54 - html: '<div><span>hello</span><span>world</span></div>',
55 - });
56 - });
57 -});
packages/react-flight/src/ReactFlightClient.js
+89 -112
@@ -7,138 +7,115 @@
7 * @flow
8 */
9
10 -import type {Destination} from './ReactFlightClientHostConfig';
10 +import type {Source, StringDecoder} from './ReactFlightClientHostConfig';
11
12 import {
13 - scheduleWork,
14 - beginWriting,
15 - writeChunk,
16 - completeWriting,
17 - flushBuffered,
18 - close,
19 - convertStringToBuffer,
20 - formatChunkAsString,
13 + supportsBinaryStreams,
14 + createStringDecoder,
15 + readPartialStringChunk,
16 + readFinalStringChunk,
17 } from './ReactFlightClientHostConfig';
22 -import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
18
24 -export type ReactModel =
25 - | React$Element<any>
26 - | string
27 - | boolean
28 - | number
29 - | null
30 - | Iterable<ReactModel>
31 - | ReactModelObject;
19 +export type ReactModelRoot<T> = {|
20 + model: T,
21 +|};
22
33 -type ReactJSONValue =
34 - | string
35 - | boolean
36 - | number
37 - | null
38 - | Array<ReactModel>
39 - | ReactModelObject;
40 -
41 -type ReactModelObject = {
42 - +[key: string]: ReactModel,
43 -};
44 -
45 -type OpaqueRequest = {
46 - destination: Destination,
47 - model: ReactModel,
48 - completedChunks: Array<Uint8Array>,
49 - flowing: boolean,
23 +type OpaqueResponse = {
24 + source: Source,
25 + modelRoot: ReactModelRoot<any>,
26 + partialRow: string,
27 + stringDecoder: StringDecoder,
28 + rootPing: () => void,
29 };
30
52 -export function createRequest(
53 - model: ReactModel,
54 - destination: Destination,
55 -): OpaqueRequest {
56 - return {destination, model, completedChunks: [], flowing: false};
57 -}
31 +export function createResponse(source: Source): OpaqueResponse {
32 + let modelRoot = {};
33 + Object.defineProperty(
34 + modelRoot,
35 + 'model',
36 + ({
37 + configurable: true,
38 + enumerable: true,
39 + get() {
40 + throw rootPromise;
41 + },
42 + }: any),
43 + );
44
59 -function resolveChildToHostFormat(child: ReactJSONValue): string {
60 - if (typeof child === 'string') {
61 - return child;
62 - } else if (typeof child === 'number') {
63 - return '' + child;
64 - } else if (typeof child === 'boolean' || child === null) {
65 - // Booleans are like null when they're React children.
66 - return '';
67 - } else if (Array.isArray(child)) {
68 - return (child: Array<ReactModel>)
69 - .map(c => resolveChildToHostFormat(resolveModelToJSON('', c)))
70 - .join('');
71 - } else {
72 - throw new Error('Object models are not valid as children of host nodes.');
45 + let rootPing;
46 + let rootPromise = new Promise(resolve => {
47 + rootPing = resolve;
48 + });
49 +
50 + let response: OpaqueResponse = ({
51 + source,
52 + modelRoot,
53 + partialRow: '',
54 + rootPing,
55 + }: any);
56 + if (supportsBinaryStreams) {
57 + response.stringDecoder = createStringDecoder();
58 }
59 + return response;
60 }
61
76 -function resolveElementToHostFormat(type: string, props: Object): string {
77 - let child = resolveModelToJSON('', props.children);
78 - let childString = resolveChildToHostFormat(child);
79 - return formatChunkAsString(
80 - type,
81 - Object.assign({}, props, {children: childString}),
62 +// Report that any missing chunks in the model is now going to throw this
63 +// error upon read. Also notify any pending promises.
64 +export function reportGlobalError(
65 + response: OpaqueResponse,
66 + error: Error,
67 +): void {
68 + Object.defineProperty(
69 + response.modelRoot,
70 + 'model',
71 + ({
72 + configurable: true,
73 + enumerable: true,
74 + get() {
75 + throw error;
76 + },
77 + }: any),
78 );
79 + response.rootPing();
80 }
81
85 -function resolveModelToJSON(key: string, value: ReactModel): ReactJSONValue {
86 - while (value && value.$$typeof === REACT_ELEMENT_TYPE) {
87 - let element: React$Element<any> = (value: any);
88 - let type = element.type;
89 - let props = element.props;
90 - if (typeof type === 'function') {
91 - // This is a nested view model.
92 - value = type(props);
93 - continue;
94 - } else if (typeof type === 'string') {
95 - // This is a host element. E.g. HTML.
96 - return resolveElementToHostFormat(type, props);
97 - } else {
98 - throw new Error('Unsupported type.');
99 - }
100 - }
101 - return value;
82 +export function processStringChunk(
83 + response: OpaqueResponse,
84 + chunk: string,
85 + offset: number,
86 +): void {
87 + response.partialRow += chunk.substr(offset);
88 }
89
104 -function performWork(request: OpaqueRequest): void {
105 - let rootModel = request.model;
106 - request.model = null;
107 - let json = JSON.stringify(rootModel, resolveModelToJSON);
108 - request.completedChunks.push(convertStringToBuffer(json));
109 - if (request.flowing) {
110 - flushCompletedChunks(request);
90 +export function processBinaryChunk(
91 + response: OpaqueResponse,
92 + chunk: Uint8Array,
93 + offset: number,
94 +): void {
95 + if (!supportsBinaryStreams) {
96 + throw new Error("This environment don't support binary chunks.");
97 }
112 -
113 - flushBuffered(request.destination);
98 + response.partialRow += readPartialStringChunk(response.stringDecoder, chunk);
99 }
100
116 -function flushCompletedChunks(request: OpaqueRequest) {
117 - let destination = request.destination;
118 - let chunks = request.completedChunks;
119 - request.completedChunks = [];
120 -
121 - beginWriting(destination);
122 - try {
123 - for (let i = 0; i < chunks.length; i++) {
124 - let chunk = chunks[i];
125 - writeChunk(destination, chunk);
126 - }
127 - } finally {
128 - completeWriting(destination);
101 +let emptyBuffer = new Uint8Array(0);
102 +export function complete(response: OpaqueResponse): void {
103 + if (supportsBinaryStreams) {
104 + // This should never be needed since we're expected to have complete
105 + // code units at the end of JSON.
106 + response.partialRow += readFinalStringChunk(
107 + response.stringDecoder,
108 + emptyBuffer,
109 + );
110 }
130 - close(destination);
111 + let modelRoot = response.modelRoot;
112 + let model = JSON.parse(response.partialRow);
113 + Object.defineProperty(modelRoot, 'model', {
114 + value: model,
115 + });
116 + response.rootPing();
117 }
118
133 -export function startWork(request: OpaqueRequest): void {
134 - request.flowing = true;
135 - scheduleWork(() => performWork(request));
136 -}
137 -
138 -export function startFlowing(
139 - request: OpaqueRequest,
140 - desiredBytes: number,
141 -): void {
142 - request.flowing = false;
143 - flushCompletedChunks(request);
119 +export function getModelRoot<T>(response: OpaqueResponse): ReactModelRoot<T> {
120 + return response.modelRoot;
121 }
packages/react-flight/src/__tests__/ReactFlight-test.js renamed
+7 -3
@@ -11,13 +11,15 @@
11 'use strict';
12
13 let React;
14 +let ReactNoopFlightServer;
15 let ReactNoopFlightClient;
16
16 -describe('ReactFlightClient', () => {
17 +describe('ReactFlight', () => {
18 beforeEach(() => {
19 jest.resetModules();
20
21 React = require('react');
22 + ReactNoopFlightServer = require('react-noop-renderer/flight-server');
23 ReactNoopFlightClient = require('react-noop-renderer/flight-client');
24 });
25
@@ -30,9 +32,11 @@ describe('ReactFlightClient', () => {
32 bar: [<Bar text="a" />, <Bar text="b" />],
33 };
34 }
33 - let result = ReactNoopFlightClient.render({
35 + let transport = ReactNoopFlightServer.render({
36 foo: <Foo />,
37 });
36 - expect(result).toEqual([{foo: {bar: ['A', 'B']}}]);
38 + let root = ReactNoopFlightClient.read(transport);
39 + let model = root.model;
40 + expect(model).toEqual({foo: {bar: ['A', 'B']}});
41 });
42 });
packages/react-flight/src/forks/ReactFlightClientHostConfig.custom.js
+6 -10
@@ -24,14 +24,10 @@
24 // really an argument to a top-level wrapping function.
25
26 declare var $$$hostConfig: any;
27 -export opaque type Destination = mixed; // eslint-disable-line no-undef
27 +export opaque type Source = mixed; // eslint-disable-line no-undef
28 +export opaque type StringDecoder = mixed; // eslint-disable-line no-undef
29
29 -export const formatChunkAsString = $$$hostConfig.formatChunkAsString;
30 -export const formatChunk = $$$hostConfig.formatChunk;
31 -export const scheduleWork = $$$hostConfig.scheduleWork;
32 -export const beginWriting = $$$hostConfig.beginWriting;
33 -export const writeChunk = $$$hostConfig.writeChunk;
34 -export const completeWriting = $$$hostConfig.completeWriting;
35 -export const flushBuffered = $$$hostConfig.flushBuffered;
36 -export const close = $$$hostConfig.close;
37 -export const convertStringToBuffer = $$$hostConfig.convertStringToBuffer;
30 +export const supportsBinaryStreams = $$$hostConfig.supportsBinaryStreams;
31 +export const createStringDecoder = $$$hostConfig.createStringDecoder;
32 +export const readPartialStringChunk = $$$hostConfig.readPartialStringChunk;
33 +export const readFinalStringChunk = $$$hostConfig.readFinalStringChunk;
packages/react-noop-renderer/src/ReactNoopFlightClient.js
+17 -28
@@ -14,41 +14,30 @@
14 * environment.
15 */
16
17 -import type {ReactModel} from 'react-flight/inline-typed';
17 +import type {ReactModelRoot} from 'react-flight/inline-typed';
18
19 import ReactFlightClient from 'react-flight';
20
21 -type Destination = Array<string>;
21 +type Source = Array<string>;
22
23 -const ReactNoopFlightClient = ReactFlightClient({
24 - scheduleWork(callback: () => void) {
25 - callback();
26 - },
27 - beginWriting(destination: Destination): void {},
28 - writeChunk(destination: Destination, buffer: Uint8Array): void {
29 - destination.push(JSON.parse(Buffer.from((buffer: any)).toString('utf8')));
30 - },
31 - completeWriting(destination: Destination): void {},
32 - close(destination: Destination): void {},
33 - flushBuffered(destination: Destination): void {},
34 - convertStringToBuffer(content: string): Uint8Array {
35 - return Buffer.from(content, 'utf8');
36 - },
37 - formatChunkAsString(type: string, props: Object): string {
38 - return JSON.stringify({type, props});
39 - },
40 - formatChunk(type: string, props: Object): Uint8Array {
41 - return Buffer.from(JSON.stringify({type, props}), 'utf8');
42 - },
23 +const {
24 + createResponse,
25 + getModelRoot,
26 + processStringChunk,
27 + complete,
28 +} = ReactFlightClient({
29 + supportsBinaryStreams: false,
30 });
31
45 -function render(model: ReactModel): Destination {
46 - let destination: Destination = [];
47 - let request = ReactNoopFlightClient.createRequest(model, destination);
48 - ReactNoopFlightClient.startWork(request);
49 - return destination;
32 +function read<T>(source: Source): ReactModelRoot<T> {
33 + let response = createResponse(source);
34 + for (let i = 0; i < source.length; i++) {
35 + processStringChunk(response, source[i], 0);
36 + }
37 + complete(response);
38 + return getModelRoot(response);
39 }
40
41 export default {
53 - render,
42 + read,
43 };
packages/react-noop-renderer/src/ReactNoopFlightServer.js
+1 -1
@@ -26,7 +26,7 @@ const ReactNoopFlightServer = ReactFlightStreamer({
26 },
27 beginWriting(destination: Destination): void {},
28 writeChunk(destination: Destination, buffer: Uint8Array): void {
29 - destination.push(JSON.parse(Buffer.from((buffer: any)).toString('utf8')));
29 + destination.push(Buffer.from((buffer: any)).toString('utf8'));
30 },
31 completeWriting(destination: Destination): void {},
32 close(destination: Destination): void {},
packages/react-server/src/__tests__/ReactFlightServer-test.js deleted
-38
@@ -1,38 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - * @jest-environment node
9 - */
10 -
11 -'use strict';
12 -
13 -let React;
14 -let ReactNoopFlight;
15 -
16 -describe('ReactFlightServer', () => {
17 - beforeEach(() => {
18 - jest.resetModules();
19 -
20 - React = require('react');
21 - ReactNoopFlight = require('react-noop-renderer/flight-server');
22 - });
23 -
24 - it('can resolve a model', () => {
25 - function Bar({text}) {
26 - return text.toUpperCase();
27 - }
28 - function Foo() {
29 - return {
30 - bar: [<Bar text="a" />, <Bar text="b" />],
31 - };
32 - }
33 - let result = ReactNoopFlight.render({
34 - foo: <Foo />,
35 - });
36 - expect(result).toEqual([{foo: {bar: ['A', 'B']}}]);
37 - });
38 -});
scripts/rollup/validate/eslintrc.cjs.js
+4
@@ -28,6 +28,10 @@ module.exports = {
28 SharedArrayBuffer: true,
29 Int32Array: true,
30 ArrayBuffer: true,
31 +
32 + // Flight
33 + Uint8Array: true,
34 + Promise: true,
35 },
36 parserOptions: {
37 ecmaVersion: 5,
scripts/rollup/validate/eslintrc.fb.js
+4
@@ -29,6 +29,10 @@ module.exports = {
29 SharedArrayBuffer: true,
30 Int32Array: true,
31 ArrayBuffer: true,
32 +
33 + // Flight
34 + Uint8Array: true,
35 + Promise: true,
36 },
37 parserOptions: {
38 ecmaVersion: 5,
scripts/rollup/validate/eslintrc.umd.js
+4
@@ -31,6 +31,10 @@ module.exports = {
31 SharedArrayBuffer: true,
32 Int32Array: true,
33 ArrayBuffer: true,
34 +
35 + // Flight
36 + Uint8Array: true,
37 + Promise: true,
38 },
39 parserOptions: {
40 ecmaVersion: 5,