mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[Fizz/Flight] pipeToNodeWritable(..., writable).startWriting() -> renderToPipeableStream(...).pipe(writable) (#22450)
* Rename pipeToNodeWritable to renderToNodePipe * Add startWriting API to Flight We don't really need it in this case because there's way less reason to delay the stream in Flight. * Pass the destination to startWriting instead of renderToNode * Rename startWriting to pipe This mirrors the ReadableStream API in Node * Error codes * Rename to renderToPipeableStream This mimics the renderToReadableStream API for the browser.
This commit is contained in:
committed by
GitHub
parent
bc84c79555
commit
9f41b5fa3e
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const {pipeToNodeWritable} = require('react-server-dom-webpack/writer');
|
||||
const {renderToPipeableStream} = require('react-server-dom-webpack/writer');
|
||||
const {readFile} = require('fs');
|
||||
const {resolve} = require('path');
|
||||
const React = require('react');
|
||||
@@ -20,7 +20,11 @@ module.exports = function(req, res) {
|
||||
const App = m.default.default || m.default;
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
const moduleMap = JSON.parse(data);
|
||||
pipeToNodeWritable(React.createElement(App), res, moduleMap);
|
||||
const {pipe} = renderToPipeableStream(
|
||||
React.createElement(App),
|
||||
moduleMap
|
||||
);
|
||||
pipe(res);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {pipeToNodeWritable} from 'react-dom/server';
|
||||
import {renderToPipeableStream} from 'react-dom/server';
|
||||
|
||||
import App from '../src/components/App';
|
||||
|
||||
@@ -20,22 +20,18 @@ export default function render(url, res) {
|
||||
console.error('Fatal', error);
|
||||
});
|
||||
let didError = false;
|
||||
const {startWriting, abort} = pipeToNodeWritable(
|
||||
<App assets={assets} />,
|
||||
res,
|
||||
{
|
||||
onCompleteShell() {
|
||||
// If something errored before we started streaming, we set the error code appropriately.
|
||||
res.statusCode = didError ? 500 : 200;
|
||||
res.setHeader('Content-type', 'text/html');
|
||||
startWriting();
|
||||
},
|
||||
onError(x) {
|
||||
didError = true;
|
||||
console.error(x);
|
||||
},
|
||||
}
|
||||
);
|
||||
const {pipe, abort} = renderToPipeableStream(<App assets={assets} />, {
|
||||
onCompleteShell() {
|
||||
// If something errored before we started streaming, we set the error code appropriately.
|
||||
res.statusCode = didError ? 500 : 200;
|
||||
res.setHeader('Content-type', 'text/html');
|
||||
pipe(res);
|
||||
},
|
||||
onError(x) {
|
||||
didError = true;
|
||||
console.error(x);
|
||||
},
|
||||
});
|
||||
// Abandon and switch to client rendering after 5 seconds.
|
||||
// Try lowering this to see the client recover.
|
||||
setTimeout(abort, 5000);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
// import {renderToString} from 'react-dom/server';
|
||||
import {pipeToNodeWritable} from 'react-dom/server';
|
||||
import {renderToPipeableStream} from 'react-dom/server';
|
||||
import App from '../src/App';
|
||||
import {DataProvider} from '../src/data';
|
||||
import {API_DELAY, ABORT_DELAY} from './delays';
|
||||
@@ -37,17 +37,16 @@ module.exports = function render(url, res) {
|
||||
});
|
||||
let didError = false;
|
||||
const data = createServerData();
|
||||
const {startWriting, abort} = pipeToNodeWritable(
|
||||
const {pipe, abort} = renderToPipeableStream(
|
||||
<DataProvider data={data}>
|
||||
<App assets={assets} />
|
||||
</DataProvider>,
|
||||
res,
|
||||
{
|
||||
onCompleteShell() {
|
||||
// If something errored before we started streaming, we set the error code appropriately.
|
||||
res.statusCode = didError ? 500 : 200;
|
||||
res.setHeader('Content-type', 'text/html');
|
||||
startWriting();
|
||||
pipe(res);
|
||||
},
|
||||
onError(x) {
|
||||
didError = true;
|
||||
|
||||
@@ -14,4 +14,4 @@ exports.renderToString = l.renderToString;
|
||||
exports.renderToStaticMarkup = l.renderToStaticMarkup;
|
||||
exports.renderToNodeStream = l.renderToNodeStream;
|
||||
exports.renderToStaticNodeStream = l.renderToStaticNodeStream;
|
||||
exports.pipeToNodeWritable = s.pipeToNodeWritable;
|
||||
exports.renderToPipeableStream = s.renderToPipeableStream;
|
||||
|
||||
@@ -36,8 +36,8 @@ export function renderToStaticNodeStream() {
|
||||
);
|
||||
}
|
||||
|
||||
export function pipeToNodeWritable() {
|
||||
return require('./src/server/ReactDOMFizzServerNode').pipeToNodeWritable.apply(
|
||||
export function renderToPipeableStream() {
|
||||
return require('./src/server/ReactDOMFizzServerNode').renderToPipeableStream.apply(
|
||||
this,
|
||||
arguments,
|
||||
);
|
||||
|
||||
+60
-91
@@ -239,7 +239,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
};
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<div>
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
@@ -252,9 +252,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual(
|
||||
<div>
|
||||
@@ -304,16 +303,16 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App isClient={false} />,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(loggedErrors).toEqual([]);
|
||||
|
||||
@@ -356,15 +355,14 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
{lazyElement}
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
|
||||
await act(async () => {
|
||||
@@ -396,16 +394,16 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App isClient={false} />,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(loggedErrors).toEqual([]);
|
||||
|
||||
@@ -441,15 +439,14 @@ describe('ReactDOMFizzServer', () => {
|
||||
// @gate experimental
|
||||
it('should asynchronously load the suspense boundary', async () => {
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<AsyncText text="Hello World" />
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
|
||||
await act(async () => {
|
||||
@@ -475,11 +472,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<App />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
// We're still showing a fallback.
|
||||
@@ -550,16 +544,16 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
// We originally suspend the boundary and start streaming the loading state.
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App />,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
// We're still showing a fallback.
|
||||
@@ -633,11 +627,10 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
// We originally suspend the boundary and start streaming the loading state.
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App showMore={false} />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(container, {hydrate: true});
|
||||
@@ -701,8 +694,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
let controls;
|
||||
await act(async () => {
|
||||
controls = ReactDOMFizzServer.pipeToNodeWritable(<App />, writable);
|
||||
controls.startWriting();
|
||||
controls = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
controls.pipe(writable);
|
||||
});
|
||||
|
||||
// We're still showing a fallback.
|
||||
@@ -753,7 +746,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
};
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
// We use two nested boundaries to flush out coverage of an old reentrancy bug.
|
||||
<Suspense fallback="Loading...">
|
||||
<Suspense fallback={<Text text="Loading A..." />}>
|
||||
@@ -765,12 +758,11 @@ describe('ReactDOMFizzServer', () => {
|
||||
</>
|
||||
</Suspense>
|
||||
</Suspense>,
|
||||
writableA,
|
||||
{
|
||||
identifierPrefix: 'A_',
|
||||
onCompleteShell() {
|
||||
writableA.write('<div id="container-A">');
|
||||
startWriting();
|
||||
pipe(writableA);
|
||||
writableA.write('</div>');
|
||||
},
|
||||
},
|
||||
@@ -778,19 +770,18 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<Suspense fallback={<Text text="Loading B..." />}>
|
||||
<Text text="This will show B: " />
|
||||
<div>
|
||||
<AsyncText text="B" />
|
||||
</div>
|
||||
</Suspense>,
|
||||
writableB,
|
||||
{
|
||||
identifierPrefix: 'B_',
|
||||
onCompleteShell() {
|
||||
writableB.write('<div id="container-B">');
|
||||
startWriting();
|
||||
pipe(writableB);
|
||||
writableB.write('</div>');
|
||||
},
|
||||
},
|
||||
@@ -876,11 +867,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<App />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
expect(getVisibleChildren(container)).toEqual(
|
||||
@@ -967,11 +955,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<App />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
expect(getVisibleChildren(container)).toEqual(
|
||||
@@ -1024,14 +1009,14 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App />,
|
||||
writable,
|
||||
|
||||
{
|
||||
namespaceURI: 'http://www.w3.org/2000/svg',
|
||||
onCompleteShell() {
|
||||
writable.write('<svg>');
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
writable.write('</svg>');
|
||||
},
|
||||
},
|
||||
@@ -1111,11 +1096,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
try {
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<A />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<A />);
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
expect(getVisibleChildren(container)).toEqual(
|
||||
@@ -1213,7 +1195,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<TestProvider ctx="A">
|
||||
<div>
|
||||
<Suspense fallback={[<Text text="Loading: " />, <TestConsumer />]}>
|
||||
@@ -1224,9 +1206,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
</Suspense>
|
||||
</div>
|
||||
</TestProvider>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual(
|
||||
<div>
|
||||
@@ -1272,7 +1253,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<PrintA />
|
||||
<div>
|
||||
@@ -1287,9 +1268,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
</div>
|
||||
<PrintA />
|
||||
</div>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual(
|
||||
<div>
|
||||
@@ -1335,7 +1315,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
const loggedErrors = [];
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<PrintA />
|
||||
<div>
|
||||
@@ -1355,14 +1335,14 @@ describe('ReactDOMFizzServer', () => {
|
||||
</div>
|
||||
<PrintA />
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(loggedErrors.length).toBe(1);
|
||||
expect(loggedErrors[0].message).toEqual('A0.1.1');
|
||||
@@ -1396,16 +1376,16 @@ describe('ReactDOMFizzServer', () => {
|
||||
const loggedErrors = [];
|
||||
let controls;
|
||||
await act(async () => {
|
||||
controls = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
controls = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App isClient={false} />,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
controls.startWriting();
|
||||
controls.pipe(writable);
|
||||
});
|
||||
|
||||
// We're still showing a fallback.
|
||||
@@ -1456,7 +1436,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
// @gate experimental
|
||||
it('should be able to abort the fallback if the main content finishes first', async () => {
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<Suspense fallback={<Text text="Loading Outer" />}>
|
||||
<div>
|
||||
<Suspense
|
||||
@@ -1470,9 +1450,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
</Suspense>
|
||||
</div>
|
||||
</Suspense>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(getVisibleChildren(container)).toEqual('Loading Outer');
|
||||
// We should have received a partial segment containing the a partial of the fallback.
|
||||
@@ -1554,11 +1533,10 @@ describe('ReactDOMFizzServer', () => {
|
||||
await jest.runAllTimers();
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<App isClient={false} />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
// Nothing is output since root has a suspense with avoidedThisFallback that hasn't resolved
|
||||
@@ -1669,18 +1647,18 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
const loggedErrors = [];
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<Suspense fallback="Loading...">
|
||||
<App />
|
||||
</Suspense>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['server']);
|
||||
|
||||
@@ -1752,18 +1730,18 @@ describe('ReactDOMFizzServer', () => {
|
||||
|
||||
const loggedErrors = [];
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<Suspense fallback="Loading...">
|
||||
<App />
|
||||
</Suspense>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
loggedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['server']);
|
||||
|
||||
@@ -1838,11 +1816,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<App />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Yay!']);
|
||||
|
||||
@@ -1922,11 +1897,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<App />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Yay!']);
|
||||
|
||||
@@ -1997,11 +1969,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
<App />,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
|
||||
pipe(writable);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Yay!']);
|
||||
|
||||
|
||||
+28
-32
@@ -57,13 +57,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
// @gate experimental
|
||||
it('should call pipeToNodeWritable', () => {
|
||||
it('should call renderToPipeableStream', () => {
|
||||
const {writable, output} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>hello world</div>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
jest.runAllTimers();
|
||||
expect(output.result).toMatchInlineSnapshot(`"<div>hello world</div>"`);
|
||||
});
|
||||
@@ -71,13 +70,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
// @gate experimental
|
||||
it('should emit DOCTYPE at the root of the document', () => {
|
||||
const {writable, output} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<html>
|
||||
<body>hello world</body>
|
||||
</html>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
jest.runAllTimers();
|
||||
expect(output.result).toMatchInlineSnapshot(
|
||||
`"<!DOCTYPE html><html><body>hello world</body></html>"`,
|
||||
@@ -85,18 +83,17 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
// @gate experimental
|
||||
it('should start writing after startWriting', () => {
|
||||
it('should start writing after pipe', () => {
|
||||
const {writable, output} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>hello world</div>,
|
||||
writable,
|
||||
);
|
||||
jest.runAllTimers();
|
||||
// First we write our header.
|
||||
output.result +=
|
||||
'<!doctype html><html><head><title>test</title><head><body>';
|
||||
// Then React starts writing.
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
expect(output.result).toMatchInlineSnapshot(
|
||||
`"<!doctype html><html><head><title>test</title><head><body><div>hello world</div>"`,
|
||||
);
|
||||
@@ -115,13 +112,13 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
let isCompleteCalls = 0;
|
||||
const {writable, output} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback="Loading">
|
||||
<Wait />
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onCompleteAll() {
|
||||
isCompleteCalls++;
|
||||
@@ -144,7 +141,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
output.result +=
|
||||
'<!doctype html><html><head><title>test</title><head><body>';
|
||||
// Then React starts writing.
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
expect(output.result).toMatchInlineSnapshot(
|
||||
`"<!doctype html><html><head><title>test</title><head><body><div><!--$-->Done<!-- --><!--/$--></div>"`,
|
||||
);
|
||||
@@ -154,11 +151,11 @@ describe('ReactDOMFizzServer', () => {
|
||||
it('should error the stream when an error is thrown at the root', async () => {
|
||||
const reportedErrors = [];
|
||||
const {writable, output, completed} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Throw />
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
reportedErrors.push(x);
|
||||
@@ -167,7 +164,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
);
|
||||
|
||||
// The stream is errored once we start writing.
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
|
||||
await completed;
|
||||
|
||||
@@ -181,20 +178,20 @@ describe('ReactDOMFizzServer', () => {
|
||||
it('should error the stream when an error is thrown inside a fallback', async () => {
|
||||
const reportedErrors = [];
|
||||
const {writable, output, completed} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback={<Throw />}>
|
||||
<InfiniteSuspend />
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
reportedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
|
||||
await completed;
|
||||
|
||||
@@ -207,20 +204,20 @@ describe('ReactDOMFizzServer', () => {
|
||||
it('should not error the stream when an error is thrown inside suspense boundary', async () => {
|
||||
const reportedErrors = [];
|
||||
const {writable, output, completed} = getTestWritable();
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback={<div>Loading</div>}>
|
||||
<Throw />
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onError(x) {
|
||||
reportedErrors.push(x);
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
|
||||
await completed;
|
||||
|
||||
@@ -242,13 +239,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
function Content() {
|
||||
return 'Hi';
|
||||
}
|
||||
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<Content />
|
||||
</Suspense>,
|
||||
writable,
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
|
||||
await completed;
|
||||
|
||||
@@ -261,20 +257,20 @@ describe('ReactDOMFizzServer', () => {
|
||||
it('should be able to complete by aborting even if the promise never resolves', async () => {
|
||||
let isCompleteCalls = 0;
|
||||
const {writable, output, completed} = getTestWritable();
|
||||
const {startWriting, abort} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe, abort} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback={<div>Loading</div>}>
|
||||
<InfiniteSuspend />
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onCompleteAll() {
|
||||
isCompleteCalls++;
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
@@ -294,7 +290,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
it('should be able to complete by abort when the fallback is also suspended', async () => {
|
||||
let isCompleteCalls = 0;
|
||||
const {writable, output, completed} = getTestWritable();
|
||||
const {startWriting, abort} = ReactDOMFizzServer.pipeToNodeWritable(
|
||||
const {pipe, abort} = ReactDOMFizzServer.renderToPipeableStream(
|
||||
<div>
|
||||
<Suspense fallback="Loading">
|
||||
<Suspense fallback={<InfiniteSuspend />}>
|
||||
@@ -302,14 +298,14 @@ describe('ReactDOMFizzServer', () => {
|
||||
</Suspense>
|
||||
</Suspense>
|
||||
</div>,
|
||||
writable,
|
||||
|
||||
{
|
||||
onCompleteAll() {
|
||||
isCompleteCalls++;
|
||||
},
|
||||
},
|
||||
);
|
||||
startWriting();
|
||||
pipe(writable);
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
|
||||
+8
-6
@@ -41,7 +41,7 @@ type Controls = {|
|
||||
// Cancel any pending I/O and put anything remaining into
|
||||
// client rendered mode.
|
||||
abort(): void,
|
||||
startWriting(): void,
|
||||
pipe<T: Writable>(destination: T): T,
|
||||
|};
|
||||
|
||||
function createRequestImpl(children: ReactNodeList, options: void | Options) {
|
||||
@@ -56,22 +56,24 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) {
|
||||
);
|
||||
}
|
||||
|
||||
function pipeToNodeWritable(
|
||||
function renderToPipeableStream(
|
||||
children: ReactNodeList,
|
||||
destination: Writable,
|
||||
options?: Options,
|
||||
): Controls {
|
||||
const request = createRequestImpl(children, options);
|
||||
let hasStartedFlowing = false;
|
||||
startWork(request);
|
||||
return {
|
||||
startWriting() {
|
||||
pipe<T: Writable>(destination: T): T {
|
||||
if (hasStartedFlowing) {
|
||||
return;
|
||||
throw new Error(
|
||||
'React currently only supports piping to one writable stream.',
|
||||
);
|
||||
}
|
||||
hasStartedFlowing = true;
|
||||
startFlowing(request, destination);
|
||||
destination.on('drain', createDrainHandler(destination, request));
|
||||
return destination;
|
||||
},
|
||||
abort() {
|
||||
abort(request);
|
||||
@@ -79,4 +81,4 @@ function pipeToNodeWritable(
|
||||
};
|
||||
}
|
||||
|
||||
export {pipeToNodeWritable, ReactVersion as version};
|
||||
export {renderToPipeableStream, ReactVersion as version};
|
||||
|
||||
@@ -25,20 +25,35 @@ type Options = {
|
||||
onError?: (error: mixed) => void,
|
||||
};
|
||||
|
||||
function pipeToNodeWritable(
|
||||
type Controls = {|
|
||||
pipe<T: Writable>(destination: T): T,
|
||||
|};
|
||||
|
||||
function renderToPipeableStream(
|
||||
model: ReactModel,
|
||||
destination: Writable,
|
||||
webpackMap: BundlerConfig,
|
||||
options?: Options,
|
||||
): void {
|
||||
): Controls {
|
||||
const request = createRequest(
|
||||
model,
|
||||
webpackMap,
|
||||
options ? options.onError : undefined,
|
||||
);
|
||||
let hasStartedFlowing = false;
|
||||
startWork(request);
|
||||
startFlowing(request, destination);
|
||||
destination.on('drain', createDrainHandler(destination, request));
|
||||
return {
|
||||
pipe<T: Writable>(destination: T): T {
|
||||
if (hasStartedFlowing) {
|
||||
throw new Error(
|
||||
'React currently only supports piping to one writable stream.',
|
||||
);
|
||||
}
|
||||
hasStartedFlowing = true;
|
||||
startFlowing(request, destination);
|
||||
destination.on('drain', createDrainHandler(destination, request));
|
||||
return destination;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export {pipeToNodeWritable};
|
||||
export {renderToPipeableStream};
|
||||
|
||||
@@ -57,8 +57,8 @@ describe('ReactFlightDOM', () => {
|
||||
},
|
||||
});
|
||||
return {
|
||||
writable,
|
||||
readable,
|
||||
writable,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,7 +113,11 @@ describe('ReactFlightDOM', () => {
|
||||
}
|
||||
|
||||
const {writable, readable} = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(<App />, writable, webpackMap);
|
||||
const {pipe} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
<App />,
|
||||
webpackMap,
|
||||
);
|
||||
pipe(writable);
|
||||
const response = ReactServerDOMReader.createFromReadableStream(readable);
|
||||
await waitForSuspense(() => {
|
||||
const model = response.readRoot();
|
||||
@@ -162,11 +166,11 @@ describe('ReactFlightDOM', () => {
|
||||
}
|
||||
|
||||
const {writable, readable} = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(
|
||||
const {pipe} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
<RootModel />,
|
||||
writable,
|
||||
webpackMap,
|
||||
);
|
||||
pipe(writable);
|
||||
const response = ReactServerDOMReader.createFromReadableStream(readable);
|
||||
|
||||
const container = document.createElement('div');
|
||||
@@ -200,11 +204,11 @@ describe('ReactFlightDOM', () => {
|
||||
}
|
||||
|
||||
const {writable, readable} = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(
|
||||
const {pipe} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
<RootModel />,
|
||||
writable,
|
||||
webpackMap,
|
||||
);
|
||||
pipe(writable);
|
||||
const response = ReactServerDOMReader.createFromReadableStream(readable);
|
||||
|
||||
const container = document.createElement('div');
|
||||
@@ -236,11 +240,11 @@ describe('ReactFlightDOM', () => {
|
||||
}
|
||||
|
||||
const {writable, readable} = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(
|
||||
const {pipe} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
<RootModel />,
|
||||
writable,
|
||||
webpackMap,
|
||||
);
|
||||
pipe(writable);
|
||||
const response = ReactServerDOMReader.createFromReadableStream(readable);
|
||||
|
||||
const container = document.createElement('div');
|
||||
@@ -371,11 +375,16 @@ describe('ReactFlightDOM', () => {
|
||||
}
|
||||
|
||||
const {writable, readable} = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(model, writable, webpackMap, {
|
||||
onError(x) {
|
||||
reportedErrors.push(x);
|
||||
const {pipe} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
model,
|
||||
webpackMap,
|
||||
{
|
||||
onError(x) {
|
||||
reportedErrors.push(x);
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
pipe(writable);
|
||||
const response = ReactServerDOMReader.createFromReadableStream(readable);
|
||||
|
||||
const container = document.createElement('div');
|
||||
@@ -481,11 +490,11 @@ describe('ReactFlightDOM', () => {
|
||||
const root = ReactDOM.createRoot(container);
|
||||
|
||||
const stream1 = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(
|
||||
const {pipe} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
<App color="red" />,
|
||||
stream1.writable,
|
||||
webpackMap,
|
||||
);
|
||||
pipe(stream1.writable);
|
||||
const response1 = ReactServerDOMReader.createFromReadableStream(
|
||||
stream1.readable,
|
||||
);
|
||||
@@ -509,11 +518,11 @@ describe('ReactFlightDOM', () => {
|
||||
inputB.value = 'goodbye';
|
||||
|
||||
const stream2 = getTestStream();
|
||||
ReactServerDOMWriter.pipeToNodeWritable(
|
||||
const {pipe: pipe2} = ReactServerDOMWriter.renderToPipeableStream(
|
||||
<App color="blue" />,
|
||||
stream2.writable,
|
||||
webpackMap,
|
||||
);
|
||||
pipe2(stream2.writable);
|
||||
const response2 = ReactServerDOMReader.createFromReadableStream(
|
||||
stream2.readable,
|
||||
);
|
||||
|
||||
@@ -401,5 +401,6 @@
|
||||
"413": "Expected finished root and lanes to be set. This is a bug in React.",
|
||||
"414": "Did not expect this call in production. This is a bug in React. Please file an issue.",
|
||||
"415": "Error parsing the data. It's probably an error code or network corruption.",
|
||||
"416": "This environment don't support binary chunks."
|
||||
"416": "This environment don't support binary chunks.",
|
||||
"417": "React currently only supports piping to one writable stream."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user